diff options
Diffstat (limited to 'packages/taler-wallet-core')
22 files changed, 428 insertions, 667 deletions
diff --git a/packages/taler-wallet-core/src/db-utils.ts b/packages/taler-wallet-core/src/db-utils.ts index 9f18ed9be..de54719c9 100644 --- a/packages/taler-wallet-core/src/db-utils.ts +++ b/packages/taler-wallet-core/src/db-utils.ts @@ -46,9 +46,13 @@ function upgradeFromStoreMap( ): void { if (oldVersion === 0) { for (const n in storeMap) { - const swi: StoreWithIndexes<StoreDescriptor<unknown>, any> = storeMap[n]; + const swi: StoreWithIndexes< + any, + StoreDescriptor<unknown>, + any + > = storeMap[n]; const storeDesc: StoreDescriptor<unknown> = swi.store; - const s = db.createObjectStore(storeDesc.name, { + const s = db.createObjectStore(swi.storeName, { autoIncrement: storeDesc.autoIncrement, keyPath: storeDesc.keyPath, }); @@ -117,9 +121,7 @@ export async function openTalerDatabase( const metaDb = new DbAccess(metaDbHandle, walletMetadataStore); let currentMainVersion: string | undefined; await metaDb - .mktx((x) => ({ - metaConfig: x.metaConfig, - })) + .mktx((stores) => [stores.metaConfig]) .runReadWrite(async (tx) => { const dbVersionRecord = await tx.metaConfig.get(CURRENT_DB_CONFIG_KEY); if (!dbVersionRecord) { @@ -141,9 +143,7 @@ export async function openTalerDatabase( // We consider this a pre-release // development version, no migration is done. await metaDb - .mktx((x) => ({ - metaConfig: x.metaConfig, - })) + .mktx((stores) => [stores.metaConfig]) .runReadWrite(async (tx) => { await tx.metaConfig.put({ key: CURRENT_DB_CONFIG_KEY, diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 1052e302d..832bbb9ac 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -1079,7 +1079,7 @@ export interface PurchaseRecord { /** * Pending refunds for the purchase. A refund is pending * when the merchant reports a transient error from the exchange. - * + * * FIXME: Put this into a separate object store? */ refunds: { [refundKey: string]: WalletRefundItem }; @@ -1733,7 +1733,8 @@ export interface OperationAttemptLongpollResult { export const WalletStoresV1 = { coins: describeStore( - describeContents<CoinRecord>("coins", { + "coins", + describeContents<CoinRecord>({ keyPath: "coinPub", }), { @@ -1743,17 +1744,20 @@ export const WalletStoresV1 = { }, ), reserves: describeStore( - describeContents<ReserveRecord>("reserves", { + "reserves", + describeContents<ReserveRecord>({ keyPath: "reservePub", }), {}, ), config: describeStore( - describeContents<ConfigRecord>("config", { keyPath: "key" }), + "config", + describeContents<ConfigRecord>({ keyPath: "key" }), {}, ), auditorTrust: describeStore( - describeContents<AuditorTrustRecord>("auditorTrust", { + "auditorTrust", + describeContents<AuditorTrustRecord>({ keyPath: ["currency", "auditorBaseUrl"], }), { @@ -1764,7 +1768,8 @@ export const WalletStoresV1 = { }, ), exchangeTrust: describeStore( - describeContents<ExchangeTrustRecord>("exchangeTrust", { + "exchangeTrust", + describeContents<ExchangeTrustRecord>({ keyPath: ["currency", "exchangeBaseUrl"], }), { @@ -1775,7 +1780,8 @@ export const WalletStoresV1 = { }, ), denominations: describeStore( - describeContents<DenominationRecord>("denominations", { + "denominations", + describeContents<DenominationRecord>({ keyPath: ["exchangeBaseUrl", "denomPubHash"], }), { @@ -1783,19 +1789,22 @@ export const WalletStoresV1 = { }, ), exchanges: describeStore( - describeContents<ExchangeRecord>("exchanges", { + "exchanges", + describeContents<ExchangeRecord>({ keyPath: "baseUrl", }), {}, ), exchangeDetails: describeStore( - describeContents<ExchangeDetailsRecord>("exchangeDetails", { + "exchangeDetails", + describeContents<ExchangeDetailsRecord>({ keyPath: ["exchangeBaseUrl", "currency", "masterPublicKey"], }), {}, ), proposals: describeStore( - describeContents<ProposalRecord>("proposals", { keyPath: "proposalId" }), + "proposals", + describeContents<ProposalRecord>({ keyPath: "proposalId" }), { byUrlAndOrderId: describeIndex("byUrlAndOrderId", [ "merchantBaseUrl", @@ -1804,7 +1813,8 @@ export const WalletStoresV1 = { }, ), refreshGroups: describeStore( - describeContents<RefreshGroupRecord>("refreshGroups", { + "refreshGroups", + describeContents<RefreshGroupRecord>({ keyPath: "refreshGroupId", }), { @@ -1812,13 +1822,15 @@ export const WalletStoresV1 = { }, ), recoupGroups: describeStore( - describeContents<RecoupGroupRecord>("recoupGroups", { + "recoupGroups", + describeContents<RecoupGroupRecord>({ keyPath: "recoupGroupId", }), {}, ), purchases: describeStore( - describeContents<PurchaseRecord>("purchases", { keyPath: "proposalId" }), + "purchases", + describeContents<PurchaseRecord>({ keyPath: "proposalId" }), { byFulfillmentUrl: describeIndex( "byFulfillmentUrl", @@ -1831,7 +1843,8 @@ export const WalletStoresV1 = { }, ), tips: describeStore( - describeContents<TipRecord>("tips", { keyPath: "walletTipId" }), + "tips", + describeContents<TipRecord>({ keyPath: "walletTipId" }), { byMerchantTipIdAndBaseUrl: describeIndex("byMerchantTipIdAndBaseUrl", [ "merchantTipId", @@ -1840,7 +1853,8 @@ export const WalletStoresV1 = { }, ), withdrawalGroups: describeStore( - describeContents<WithdrawalGroupRecord>("withdrawalGroups", { + "withdrawalGroups", + describeContents<WithdrawalGroupRecord>({ keyPath: "withdrawalGroupId", }), { @@ -1853,7 +1867,8 @@ export const WalletStoresV1 = { }, ), planchets: describeStore( - describeContents<PlanchetRecord>("planchets", { keyPath: "coinPub" }), + "planchets", + describeContents<PlanchetRecord>({ keyPath: "coinPub" }), { byGroupAndIndex: describeIndex("byGroupAndIndex", [ "withdrawalGroupId", @@ -1864,13 +1879,15 @@ export const WalletStoresV1 = { }, ), bankWithdrawUris: describeStore( - describeContents<BankWithdrawUriRecord>("bankWithdrawUris", { + "bankWithdrawUris", + describeContents<BankWithdrawUriRecord>({ keyPath: "talerWithdrawUri", }), {}, ), backupProviders: describeStore( - describeContents<BackupProviderRecord>("backupProviders", { + "backupProviders", + describeContents<BackupProviderRecord>({ keyPath: "baseUrl", }), { @@ -1884,7 +1901,8 @@ export const WalletStoresV1 = { }, ), depositGroups: describeStore( - describeContents<DepositGroupRecord>("depositGroups", { + "depositGroups", + describeContents<DepositGroupRecord>({ keyPath: "depositGroupId", }), { @@ -1892,29 +1910,34 @@ export const WalletStoresV1 = { }, ), tombstones: describeStore( - describeContents<TombstoneRecord>("tombstones", { keyPath: "id" }), + "tombstones", + describeContents<TombstoneRecord>({ keyPath: "id" }), {}, ), operationRetries: describeStore( - describeContents<OperationRetryRecord>("operationRetries", { + "operationRetries", + describeContents<OperationRetryRecord>({ keyPath: "id", }), {}, ), ghostDepositGroups: describeStore( - describeContents<GhostDepositGroupRecord>("ghostDepositGroups", { + "ghostDepositGroups", + describeContents<GhostDepositGroupRecord>({ keyPath: "contractTermsHash", }), {}, ), balancesPerCurrency: describeStore( - describeContents<BalancePerCurrencyRecord>("balancesPerCurrency", { + "balancesPerCurrency", + describeContents<BalancePerCurrencyRecord>({ keyPath: "currency", }), {}, ), peerPushPaymentIncoming: describeStore( - describeContents<PeerPushPaymentIncomingRecord>("peerPushPaymentIncoming", { + "peerPushPaymentIncoming", + describeContents<PeerPushPaymentIncomingRecord>({ keyPath: "peerPushPaymentIncomingId", }), { @@ -1925,7 +1948,8 @@ export const WalletStoresV1 = { }, ), peerPullPaymentIncoming: describeStore( - describeContents<PeerPullPaymentIncomingRecord>("peerPullPaymentIncoming", { + "peerPullPaymentIncoming", + describeContents<PeerPullPaymentIncomingRecord>({ keyPath: "peerPullPaymentIncomingId", }), { @@ -1936,21 +1960,17 @@ export const WalletStoresV1 = { }, ), peerPullPaymentInitiations: describeStore( - describeContents<PeerPullPaymentInitiationRecord>( - "peerPullPaymentInitiations", - { - keyPath: "pursePub", - }, - ), + "peerPullPaymentInitiations", + describeContents<PeerPullPaymentInitiationRecord>({ + keyPath: "pursePub", + }), {}, ), peerPushPaymentInitiations: describeStore( - describeContents<PeerPushPaymentInitiationRecord>( - "peerPushPaymentInitiations", - { - keyPath: "pursePub", - }, - ), + "peerPushPaymentInitiations", + describeContents<PeerPushPaymentInitiationRecord>({ + keyPath: "pursePub", + }), {}, ), }; @@ -1962,7 +1982,8 @@ export interface MetaConfigRecord { export const walletMetadataStore = { metaConfig: describeStore( - describeContents<MetaConfigRecord>("metaConfig", { keyPath: "key" }), + "metaConfig", + describeContents<MetaConfigRecord>({ keyPath: "key" }), {}, ), }; diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts b/packages/taler-wallet-core/src/operations/backup/export.ts index c77ce1a85..fb1fbf90b 100644 --- a/packages/taler-wallet-core/src/operations/backup/export.ts +++ b/packages/taler-wallet-core/src/operations/backup/export.ts @@ -76,20 +76,20 @@ export async function exportBackup( ): Promise<WalletBackupContentV1> { await provideBackupState(ws); return ws.db - .mktx((x) => ({ - config: x.config, - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - coins: x.coins, - denominations: x.denominations, - purchases: x.purchases, - proposals: x.proposals, - refreshGroups: x.refreshGroups, - backupProviders: x.backupProviders, - tips: x.tips, - recoupGroups: x.recoupGroups, - withdrawalGroups: x.withdrawalGroups, - })) + .mktx((x) => [ + x.config, + x.exchanges, + x.exchangeDetails, + x.coins, + x.denominations, + x.purchases, + x.proposals, + x.refreshGroups, + x.backupProviders, + x.tips, + x.recoupGroups, + x.withdrawalGroups, + ]) .runReadWrite(async (tx) => { const bs = await getWalletBackupState(ws, tx); diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index e8683265b..8f5d019d4 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -224,22 +224,22 @@ export async function importBackup( logger.info(`importing backup ${j2s(backupBlobArg)}`); return ws.db - .mktx((x) => ({ - config: x.config, - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - coins: x.coins, - denominations: x.denominations, - purchases: x.purchases, - proposals: x.proposals, - refreshGroups: x.refreshGroups, - backupProviders: x.backupProviders, - tips: x.tips, - recoupGroups: x.recoupGroups, - withdrawalGroups: x.withdrawalGroups, - tombstones: x.tombstones, - depositGroups: x.depositGroups, - })) + .mktx((x) => [ + x.config, + x.exchangeDetails, + x.exchanges, + x.coins, + x.denominations, + x.purchases, + x.proposals, + x.refreshGroups, + x.backupProviders, + x.tips, + x.recoupGroups, + x.withdrawalGroups, + x.tombstones, + x.depositGroups, + ]) .runReadWrite(async (tx) => { // FIXME: validate schema! const backupBlob = backupBlobArg as WalletBackupContentV1; diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts b/packages/taler-wallet-core/src/operations/backup/index.ts index 56871104c..db003af81 100644 --- a/packages/taler-wallet-core/src/operations/backup/index.ts +++ b/packages/taler-wallet-core/src/operations/backup/index.ts @@ -264,7 +264,7 @@ async function runBackupCycleForProvider( args: BackupForProviderArgs, ): Promise<OperationAttemptResult> { const provider = await ws.db - .mktx((x) => ({ backupProviders: x.backupProviders })) + .mktx((x) => [x.backupProviders]) .runReadOnly(async (tx) => { return tx.backupProviders.get(args.backupProviderBaseUrl); }); @@ -322,9 +322,9 @@ async function runBackupCycleForProvider( if (resp.status === HttpStatusCode.NotModified) { await ws.db - .mktx((x) => ({ backupProvider: x.backupProviders })) + .mktx((x) => [x.backupProviders]) .runReadWrite(async (tx) => { - const prov = await tx.backupProvider.get(provider.baseUrl); + const prov = await tx.backupProviders.get(provider.baseUrl); if (!prov) { return; } @@ -333,7 +333,7 @@ async function runBackupCycleForProvider( tag: BackupProviderStateTag.Ready, nextBackupTimestamp: getNextBackupTimestamp(), }; - await tx.backupProvider.put(prov); + await tx.backupProviders.put(prov); }); return { type: OperationAttemptResultType.Finished, @@ -367,10 +367,7 @@ async function runBackupCycleForProvider( // FIXME: check if the provider is overcharging us! await ws.db - .mktx((x) => ({ - backupProviders: x.backupProviders, - operationRetries: x.operationRetries, - })) + .mktx((x) => [x.backupProviders, x.operationRetries]) .runReadWrite(async (tx) => { const provRec = await tx.backupProviders.get(provider.baseUrl); checkDbInvariant(!!provRec); @@ -407,7 +404,7 @@ async function runBackupCycleForProvider( if (resp.status === HttpStatusCode.NoContent) { await ws.db - .mktx((x) => ({ backupProviders: x.backupProviders })) + .mktx((x) => [x.backupProviders]) .runReadWrite(async (tx) => { const prov = await tx.backupProviders.get(provider.baseUrl); if (!prov) { @@ -435,12 +432,9 @@ async function runBackupCycleForProvider( const cryptoData = await computeBackupCryptoData(ws.cryptoApi, blob); await importBackup(ws, blob, cryptoData); await ws.db - .mktx((x) => ({ - backupProvider: x.backupProviders, - operationRetries: x.operationRetries, - })) + .mktx((x) => [x.backupProviders, x.operationRetries]) .runReadWrite(async (tx) => { - const prov = await tx.backupProvider.get(provider.baseUrl); + const prov = await tx.backupProviders.get(provider.baseUrl); if (!prov) { logger.warn("backup provider not found anymore"); return; @@ -453,7 +447,7 @@ async function runBackupCycleForProvider( prov.state = { tag: BackupProviderStateTag.Retrying, }; - await tx.backupProvider.put(prov); + await tx.backupProviders.put(prov); }); logger.info("processed existing backup"); // Now upload our own, merged backup. @@ -480,7 +474,7 @@ export async function processBackupForProvider( backupProviderBaseUrl: string, ): Promise<OperationAttemptResult> { const provider = await ws.db - .mktx((x) => ({ backupProviders: x.backupProviders })) + .mktx((x) => [x.backupProviders]) .runReadOnly(async (tx) => { return await tx.backupProviders.get(backupProviderBaseUrl); }); @@ -509,7 +503,7 @@ export async function removeBackupProvider( req: RemoveBackupProviderRequest, ): Promise<void> { await ws.db - .mktx(({ backupProviders }) => ({ backupProviders })) + .mktx((x) => [x.backupProviders]) .runReadWrite(async (tx) => { await tx.backupProviders.delete(req.provider); }); @@ -539,7 +533,7 @@ export async function runBackupCycle( req: RunBackupCycleRequest, ): Promise<void> { const providers = await ws.db - .mktx((x) => ({ backupProviders: x.backupProviders })) + .mktx((x) => [x.backupProviders]) .runReadOnly(async (tx) => { if (req.providers) { const rs = await Promise.all( @@ -605,7 +599,7 @@ export async function addBackupProvider( await provideBackupState(ws); const canonUrl = canonicalizeBaseUrl(req.backupProviderBaseUrl); await ws.db - .mktx((x) => ({ backupProviders: x.backupProviders })) + .mktx((x) => [x.backupProviders]) .runReadWrite(async (tx) => { const oldProv = await tx.backupProviders.get(canonUrl); if (oldProv) { @@ -628,7 +622,7 @@ export async function addBackupProvider( codecForSyncTermsOfServiceResponse(), ); await ws.db - .mktx((x) => ({ backupProviders: x.backupProviders })) + .mktx((x) => [x.backupProviders]) .runReadWrite(async (tx) => { let state: BackupProviderState; if (req.activate) { @@ -807,10 +801,7 @@ export async function getBackupInfo( ): Promise<BackupInfo> { const backupConfig = await provideBackupState(ws); const providerRecords = await ws.db - .mktx((x) => ({ - backupProviders: x.backupProviders, - operationRetries: x.operationRetries, - })) + .mktx((x) => [x.backupProviders, x.operationRetries]) .runReadOnly(async (tx) => { return await tx.backupProviders.iter().mapAsync(async (bp) => { const opId = RetryTags.forBackup(bp); @@ -853,7 +844,7 @@ export async function getBackupRecovery( ): Promise<BackupRecovery> { const bs = await provideBackupState(ws); const providers = await ws.db - .mktx((x) => ({ backupProviders: x.backupProviders })) + .mktx((x) => [x.backupProviders]) .runReadOnly(async (tx) => { return await tx.backupProviders.iter().toArray(); }); @@ -874,7 +865,7 @@ async function backupRecoveryTheirs( br: BackupRecovery, ) { await ws.db - .mktx((x) => ({ config: x.config, backupProviders: x.backupProviders })) + .mktx((x) => [x.config, x.backupProviders]) .runReadWrite(async (tx) => { let backupStateEntry: ConfigRecord | undefined = await tx.config.get( WALLET_BACKUP_STATE_KEY, @@ -924,7 +915,7 @@ export async function loadBackupRecovery( ): Promise<void> { const bs = await provideBackupState(ws); const providers = await ws.db - .mktx((x) => ({ backupProviders: x.backupProviders })) + .mktx((x) => [x.backupProviders]) .runReadOnly(async (tx) => { return await tx.backupProviders.iter().toArray(); }); @@ -954,7 +945,7 @@ export async function exportBackupEncrypted( await provideBackupState(ws); const blob = await exportBackup(ws); const bs = await ws.db - .mktx((x) => ({ config: x.config })) + .mktx((x) => [x.config]) .runReadOnly(async (tx) => { return await getWalletBackupState(ws, tx); }); diff --git a/packages/taler-wallet-core/src/operations/backup/state.ts b/packages/taler-wallet-core/src/operations/backup/state.ts index 293f56137..2efd9be8e 100644 --- a/packages/taler-wallet-core/src/operations/backup/state.ts +++ b/packages/taler-wallet-core/src/operations/backup/state.ts @@ -29,9 +29,7 @@ export async function provideBackupState( ws: InternalWalletState, ): Promise<WalletBackupConfState> { const bs: ConfigRecord | undefined = await ws.db - .mktx((x) => ({ - config: x.config, - })) + .mktx((stores) => [stores.config]) .runReadOnly(async (tx) => { return await tx.config.get(WALLET_BACKUP_STATE_KEY); }); @@ -47,9 +45,7 @@ export async function provideBackupState( // and be based on hostname const deviceId = `wallet-core-${encodeCrock(d)}`; return await ws.db - .mktx((x) => ({ - config: x.config, - })) + .mktx((x) => [x.config]) .runReadWrite(async (tx) => { let backupStateEntry: ConfigRecord | undefined = await tx.config.get( WALLET_BACKUP_STATE_KEY, @@ -87,9 +83,7 @@ export async function setWalletDeviceId( ): Promise<void> { await provideBackupState(ws); await ws.db - .mktx((x) => ({ - config: x.config, - })) + .mktx((x) => [x.config]) .runReadWrite(async (tx) => { let backupStateEntry: ConfigRecord | undefined = await tx.config.get( WALLET_BACKUP_STATE_KEY, diff --git a/packages/taler-wallet-core/src/operations/balance.ts b/packages/taler-wallet-core/src/operations/balance.ts index 4590f5051..44357fdf4 100644 --- a/packages/taler-wallet-core/src/operations/balance.ts +++ b/packages/taler-wallet-core/src/operations/balance.ts @@ -139,12 +139,7 @@ export async function getBalances( logger.trace("starting to compute balance"); const wbal = await ws.db - .mktx((x) => ({ - coins: x.coins, - refreshGroups: x.refreshGroups, - purchases: x.purchases, - withdrawalGroups: x.withdrawalGroups, - })) + .mktx((x) => [x.coins, x.refreshGroups, x.purchases, x.withdrawalGroups]) .runReadOnly(async (tx) => { return getBalancesInsideTransaction(ws, tx); }); diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts index 6eed12a38..5838be765 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -83,9 +83,7 @@ export async function processDepositGroup( } = {}, ): Promise<OperationAttemptResult> { const depositGroup = await ws.db - .mktx((x) => ({ - depositGroups: x.depositGroups, - })) + .mktx((x) => [x.depositGroups]) .runReadOnly(async (tx) => { return tx.depositGroups.get(depositGroupId); }); @@ -141,7 +139,7 @@ export async function processDepositGroup( }); await readSuccessResponseJsonOrThrow(httpResp, codecForDepositSuccess()); await ws.db - .mktx((x) => ({ depositGroups: x.depositGroups })) + .mktx((x) => [x.depositGroups]) .runReadWrite(async (tx) => { const dg = await tx.depositGroups.get(depositGroupId); if (!dg) { @@ -153,9 +151,7 @@ export async function processDepositGroup( } await ws.db - .mktx((x) => ({ - depositGroups: x.depositGroups, - })) + .mktx((x) => [x.depositGroups]) .runReadWrite(async (tx) => { const dg = await tx.depositGroups.get(depositGroupId); if (!dg) { @@ -185,9 +181,7 @@ export async function trackDepositGroup( body: any; }[] = []; const depositGroup = await ws.db - .mktx((x) => ({ - depositGroups: x.depositGroups, - })) + .mktx((x) => [x.depositGroups]) .runReadOnly(async (tx) => { return tx.depositGroups.get(req.depositGroupId); }); @@ -247,10 +241,7 @@ export async function getFeeForDeposit( const exchangeInfos: { url: string; master_pub: string }[] = []; await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - })) + .mktx((x) => [x.exchanges, x.exchangeDetails]) .runReadOnly(async (tx) => { const allExchanges = await tx.exchanges.iter().toArray(); for (const e of allExchanges) { @@ -315,10 +306,7 @@ export async function prepareDepositGroup( const exchangeInfos: { url: string; master_pub: string }[] = []; await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - })) + .mktx((x) => [x.exchanges, x.exchangeDetails]) .runReadOnly(async (tx) => { const allExchanges = await tx.exchanges.iter().toArray(); for (const e of allExchanges) { @@ -417,10 +405,7 @@ export async function createDepositGroup( const exchangeInfos: { url: string; master_pub: string }[] = []; await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - })) + .mktx((x) => [x.exchanges, x.exchangeDetails]) .runReadOnly(async (tx) => { const allExchanges = await tx.exchanges.iter().toArray(); for (const e of allExchanges) { @@ -532,12 +517,13 @@ export async function createDepositGroup( }; await ws.db - .mktx((x) => ({ - depositGroups: x.depositGroups, - coins: x.coins, - refreshGroups: x.refreshGroups, - denominations: x.denominations, - })) + .mktx((x) => [ + x.depositGroups, + x.coins, + x.recoupGroups, + x.denominations, + x.refreshGroups, + ]) .runReadWrite(async (tx) => { await applyCoinSpend( ws, @@ -565,12 +551,7 @@ export async function getEffectiveDepositAmount( const exchangeSet: Set<string> = new Set(); await ws.db - .mktx((x) => ({ - coins: x.coins, - denominations: x.denominations, - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - })) + .mktx((x) => [x.coins, x.denominations, x.exchanges, x.exchangeDetails]) .runReadOnly(async (tx) => { for (let i = 0; i < pcs.coinPubs.length; i++) { const coin = await tx.coins.get(pcs.coinPubs[i]); @@ -637,12 +618,7 @@ export async function getTotalFeesForDepositAmount( const exchangeSet: Set<string> = new Set(); await ws.db - .mktx((x) => ({ - coins: x.coins, - denominations: x.denominations, - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - })) + .mktx((x) => [x.coins, x.denominations, x.exchanges, x.exchangeDetails]) .runReadOnly(async (tx) => { for (let i = 0; i < pcs.coinPubs.length; i++) { const coin = await tx.coins.get(pcs.coinPubs[i]); diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index 1021da6b6..504978441 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -161,10 +161,7 @@ export async function getExchangeDetails( } getExchangeDetails.makeContext = (db: DbAccess<typeof WalletStoresV1>) => - db.mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - })); + db.mktx((x) => [x.exchanges, x.exchangeDetails]); export async function updateExchangeTermsOfService( ws: InternalWalletState, @@ -172,10 +169,7 @@ export async function updateExchangeTermsOfService( tos: ExchangeTosDownloadResult, ): Promise<void> { await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - })) + .mktx((x) => [x.exchanges, x.exchangeDetails]) .runReadWrite(async (tx) => { const d = await getExchangeDetails(tx, exchangeBaseUrl); if (d) { @@ -193,10 +187,7 @@ export async function acceptExchangeTermsOfService( etag: string | undefined, ): Promise<void> { await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - })) + .mktx((x) => [x.exchanges, x.exchangeDetails]) .runReadWrite(async (tx) => { const d = await getExchangeDetails(tx, exchangeBaseUrl); if (d) { @@ -326,10 +317,7 @@ async function provideExchangeRecord( exchangeDetails: ExchangeDetailsRecord | undefined; }> { return await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - })) + .mktx((x) => [x.exchanges, x.exchangeDetails]) .runReadWrite(async (tx) => { let exchange = await tx.exchanges.get(baseUrl); if (!exchange) { @@ -569,14 +557,14 @@ export async function updateExchangeFromUrlHandler( logger.trace("updating exchange info in database"); const updated = await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - denominations: x.denominations, - coins: x.coins, - refreshGroups: x.refreshGroups, - recoupGroups: x.recoupGroups, - })) + .mktx((x) => [ + x.exchanges, + x.exchangeDetails, + x.denominations, + x.coins, + x.refreshGroups, + x.recoupGroups, + ]) .runReadWrite(async (tx) => { const r = await tx.exchanges.get(baseUrl); if (!r) { @@ -770,12 +758,12 @@ export async function getExchangeTrust( let isAudited = false; return await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - exchangesTrustStore: x.exchangeTrust, - auditorTrust: x.auditorTrust, - })) + .mktx((x) => [ + x.exchanges, + x.exchangeDetails, + x.exchangeTrust, + x.auditorTrust, + ]) .runReadOnly(async (tx) => { const exchangeDetails = await getExchangeDetails( tx, @@ -786,7 +774,7 @@ export async function getExchangeTrust( throw Error(`exchange ${exchangeInfo.baseUrl} details not available`); } const exchangeTrustRecord = - await tx.exchangesTrustStore.indexes.byExchangeMasterPub.get( + await tx.exchangeTrust.indexes.byExchangeMasterPub.get( exchangeDetails.masterPublicKey, ); if ( diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts index 9e63cd516..322e90487 100644 --- a/packages/taler-wallet-core/src/operations/pay.ts +++ b/packages/taler-wallet-core/src/operations/pay.ts @@ -120,7 +120,7 @@ export async function getTotalPaymentCost( pcs: PayCoinSelection, ): Promise<AmountJson> { return ws.db - .mktx((x) => ({ coins: x.coins, denominations: x.denominations })) + .mktx((x) => [x.coins, x.denominations]) .runReadOnly(async (tx) => { const costs: AmountJson[] = []; for (let i = 0; i < pcs.coinPubs.length; i++) { @@ -222,12 +222,7 @@ export async function getCandidatePayCoins( const wireFeesPerExchange: Record<string, AmountJson> = {}; await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - denominations: x.denominations, - coins: x.coins, - })) + .mktx((x) => [x.exchanges, x.exchangeDetails, x.denominations, x.coins]) .runReadOnly(async (tx) => { const exchanges = await tx.exchanges.iter().toArray(); for (const exchange of exchanges) { @@ -459,13 +454,13 @@ async function recordConfirmPay( }; await ws.db - .mktx((x) => ({ - proposals: x.proposals, - purchases: x.purchases, - coins: x.coins, - refreshGroups: x.refreshGroups, - denominations: x.denominations, - })) + .mktx((x) => [ + x.proposals, + x.purchases, + x.coins, + x.refreshGroups, + x.denominations, + ]) .runReadWrite(async (tx) => { const p = await tx.proposals.get(proposal.proposalId); if (p) { @@ -489,7 +484,7 @@ async function failProposalPermanently( err: TalerErrorDetail, ): Promise<void> { await ws.db - .mktx((x) => ({ proposals: x.proposals })) + .mktx((x) => [x.proposals]) .runReadWrite(async (tx) => { const p = await tx.proposals.get(proposalId); if (!p) { @@ -567,13 +562,10 @@ export async function processDownloadProposal( proposalId: string, options: {} = {}, ): Promise<OperationAttemptResult> { - - const res = ws.db.mktx2((x) => [x.auditorTrust, x.coins]) - const proposal = await ws.db - .mktx((x) => ({ proposals: x.proposals })) + .mktx((x) => [x.proposals]) .runReadOnly(async (tx) => { - return tx.proposals.get(proposalId); + return await tx.proposals.get(proposalId); }); if (!proposal) { @@ -608,7 +600,7 @@ export async function processDownloadProposal( const opId = RetryTags.forProposalClaim(proposal); const retryRecord = await ws.db - .mktx((x) => ({ operationRetries: x.operationRetries })) + .mktx((x) => [x.operationRetries]) .runReadOnly(async (tx) => { return tx.operationRetries.get(opId); }); @@ -748,7 +740,7 @@ export async function processDownloadProposal( logger.trace(`extracted contract data: ${j2s(contractData)}`); await ws.db - .mktx((x) => ({ proposals: x.proposals, purchases: x.purchases })) + .mktx((x) => [x.purchases, x.proposals]) .runReadWrite(async (tx) => { const p = await tx.proposals.get(proposalId); if (!p) { @@ -807,7 +799,7 @@ async function startDownloadProposal( noncePriv: string | undefined, ): Promise<string> { const oldProposal = await ws.db - .mktx((x) => ({ proposals: x.proposals })) + .mktx((x) => [x.proposals]) .runReadOnly(async (tx) => { return tx.proposals.indexes.byUrlAndOrderId.get([ merchantBaseUrl, @@ -855,7 +847,7 @@ async function startDownloadProposal( }; await ws.db - .mktx((x) => ({ proposals: x.proposals })) + .mktx((x) => [x.proposals]) .runReadWrite(async (tx) => { const existingRecord = await tx.proposals.indexes.byUrlAndOrderId.get([ merchantBaseUrl, @@ -880,7 +872,7 @@ async function storeFirstPaySuccess( ): Promise<void> { const now = AbsoluteTime.toTimestamp(AbsoluteTime.now()); await ws.db - .mktx((x) => ({ purchases: x.purchases })) + .mktx((x) => [x.purchases]) .runReadWrite(async (tx) => { const purchase = await tx.purchases.get(proposalId); @@ -916,7 +908,7 @@ async function storePayReplaySuccess( sessionId: string | undefined, ): Promise<void> { await ws.db - .mktx((x) => ({ purchases: x.purchases })) + .mktx((x) => [x.purchases]) .runReadWrite(async (tx) => { const purchase = await tx.purchases.get(proposalId); @@ -950,9 +942,9 @@ async function handleInsufficientFunds( logger.trace("handling insufficient funds, trying to re-select coins"); const proposal = await ws.db - .mktx((x) => ({ purchaes: x.purchases })) + .mktx((x) => [x.purchases]) .runReadOnly(async (tx) => { - return tx.purchaes.get(proposalId); + return tx.purchases.get(proposalId); }); if (!proposal) { return; @@ -990,7 +982,7 @@ async function handleInsufficientFunds( const prevPayCoins: PreviousPayCoins = []; await ws.db - .mktx((x) => ({ coins: x.coins, denominations: x.denominations })) + .mktx((x) => [x.coins, x.denominations]) .runReadOnly(async (tx) => { for (let i = 0; i < proposal.payCoinSelection.coinPubs.length; i++) { const coinPub = proposal.payCoinSelection.coinPubs[i]; @@ -1036,12 +1028,7 @@ async function handleInsufficientFunds( logger.trace("re-selected coins"); await ws.db - .mktx((x) => ({ - purchases: x.purchases, - coins: x.coins, - denominations: x.denominations, - refreshGroups: x.refreshGroups, - })) + .mktx((x) => [x.purchases, x.coins, x.denominations, x.refreshGroups]) .runReadWrite(async (tx) => { const p = await tx.purchases.get(proposalId); if (!p) { @@ -1060,7 +1047,7 @@ async function unblockBackup( proposalId: string, ): Promise<void> { await ws.db - .mktx((x) => ({ backupProviders: x.backupProviders })) + .mktx((x) => [x.backupProviders]) .runReadWrite(async (tx) => { await tx.backupProviders.indexes.byPaymentProposalId .iter(proposalId) @@ -1081,7 +1068,7 @@ export async function checkPaymentByProposalId( sessionId?: string, ): Promise<PreparePayResult> { let proposal = await ws.db - .mktx((x) => ({ proposals: x.proposals })) + .mktx((x) => [x.proposals]) .runReadOnly(async (tx) => { return tx.proposals.get(proposalId); }); @@ -1095,7 +1082,7 @@ export async function checkPaymentByProposalId( } logger.trace("using existing purchase for same product"); proposal = await ws.db - .mktx((x) => ({ proposals: x.proposals })) + .mktx((x) => [x.proposals]) .runReadOnly(async (tx) => { return tx.proposals.get(existingProposalId); }); @@ -1118,7 +1105,7 @@ export async function checkPaymentByProposalId( // First check if we already paid for it. const purchase = await ws.db - .mktx((x) => ({ purchases: x.purchases })) + .mktx((x) => [x.purchases]) .runReadOnly(async (tx) => { return tx.purchases.get(proposalId); }); @@ -1176,7 +1163,7 @@ export async function checkPaymentByProposalId( "automatically re-submitting payment with different session ID", ); await ws.db - .mktx((x) => ({ purchases: x.purchases })) + .mktx((x) => [x.purchases]) .runReadWrite(async (tx) => { const p = await tx.purchases.get(proposalId); if (!p) { @@ -1230,7 +1217,7 @@ export async function getContractTermsDetails( proposalId: string, ): Promise<WalletContractData> { const proposal = await ws.db - .mktx((x) => ({ proposals: x.proposals })) + .mktx((x) => [x.proposals]) .runReadOnly(async (tx) => { return tx.proposals.get(proposalId); }); @@ -1296,7 +1283,7 @@ export async function generateDepositPermissions( denom: DenominationRecord; }> = []; await ws.db - .mktx((x) => ({ coins: x.coins, denominations: x.denominations })) + .mktx((x) => [x.coins, x.denominations]) .runReadOnly(async (tx) => { for (let i = 0; i < payCoinSel.coinPubs.length; i++) { const coin = await tx.coins.get(payCoinSel.coinPubs[i]); @@ -1359,7 +1346,7 @@ export async function runPayForConfirmPay( switch (res.type) { case OperationAttemptResultType.Finished: { const purchase = await ws.db - .mktx((x) => ({ purchases: x.purchases })) + .mktx((x) => [x.purchases]) .runReadOnly(async (tx) => { return tx.purchases.get(proposalId); }); @@ -1399,7 +1386,7 @@ export async function confirmPay( `executing confirmPay with proposalId ${proposalId} and sessionIdOverride ${sessionIdOverride}`, ); const proposal = await ws.db - .mktx((x) => ({ proposals: x.proposals })) + .mktx((x) => [x.proposals]) .runReadOnly(async (tx) => { return tx.proposals.get(proposalId); }); @@ -1414,7 +1401,7 @@ export async function confirmPay( } const existingPurchase = await ws.db - .mktx((x) => ({ purchases: x.purchases })) + .mktx((x) => [x.purchases]) .runReadWrite(async (tx) => { const purchase = await tx.purchases.get(proposalId); if ( @@ -1508,7 +1495,7 @@ export async function processPurchasePay( } = {}, ): Promise<OperationAttemptResult> { const purchase = await ws.db - .mktx((x) => ({ purchases: x.purchases })) + .mktx((x) => [x.purchases]) .runReadOnly(async (tx) => { return tx.purchases.get(proposalId); }); @@ -1568,20 +1555,12 @@ export async function processPurchasePay( ); logger.trace(`got resp ${JSON.stringify(resp)}`); - - const payOpId = RetryTags.forPay(purchase); - const payRetryRecord = await ws.db - .mktx((x) => ({ operationRetries: x.operationRetries })) - .runReadOnly(async (tx) => { - return await tx.operationRetries.get(payOpId); - }); - if (resp.status === HttpStatusCode.BadRequest) { const errDetails = await readUnexpectedResponseDetails(resp); logger.warn("unexpected 400 response for /pay"); logger.warn(j2s(errDetails)); await ws.db - .mktx((x) => ({ purchases: x.purchases })) + .mktx((x) => [x.purchases]) .runReadWrite(async (tx) => { const purch = await tx.purchases.get(proposalId); if (!purch) { @@ -1683,7 +1662,7 @@ export async function refuseProposal( proposalId: string, ): Promise<void> { const success = await ws.db - .mktx((x) => ({ proposals: x.proposals })) + .mktx((x) => [x.proposals]) .runReadWrite(async (tx) => { const proposal = await tx.proposals.get(proposalId); if (!proposal) { diff --git a/packages/taler-wallet-core/src/operations/peer-to-peer.ts b/packages/taler-wallet-core/src/operations/peer-to-peer.ts index 9ce460ad6..59dad3d55 100644 --- a/packages/taler-wallet-core/src/operations/peer-to-peer.ts +++ b/packages/taler-wallet-core/src/operations/peer-to-peer.ts @@ -242,13 +242,14 @@ export async function initiatePeerToPeerPush( }); const coinSelRes: PeerCoinSelection | undefined = await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - coins: x.coins, - denominations: x.denominations, - refreshGroups: x.refreshGroups, - peerPushPaymentInitiations: x.peerPushPaymentInitiations, - })) + .mktx((x) => [ + x.exchanges, + x.coins, + x.denominations, + x.refreshGroups, + x.peerPullPaymentInitiations, + x.peerPushPaymentInitiations, + ]) .runReadWrite(async (tx) => { const sel = await selectPeerCoins(ws, tx, instructedAmount); if (!sel) { @@ -401,9 +402,7 @@ export async function checkPeerPushPayment( const peerPushPaymentIncomingId = encodeCrock(getRandomBytes(32)); await ws.db - .mktx((x) => ({ - peerPushPaymentIncoming: x.peerPushPaymentIncoming, - })) + .mktx((x) => [x.peerPushPaymentIncoming]) .runReadWrite(async (tx) => { await tx.peerPushPaymentIncoming.add({ peerPushPaymentIncomingId, @@ -456,10 +455,7 @@ async function getMergeReserveInfo( const newReservePair = await ws.cryptoApi.createEddsaKeypair({}); const mergeReserveInfo: MergeReserveInfo = await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - withdrawalGroups: x.withdrawalGroups, - })) + .mktx((x) => [x.exchanges, x.withdrawalGroups]) .runReadWrite(async (tx) => { const ex = await tx.exchanges.get(req.exchangeBaseUrl); checkDbInvariant(!!ex); @@ -482,7 +478,7 @@ export async function acceptPeerPushPayment( req: AcceptPeerPushPaymentRequest, ): Promise<void> { const peerInc = await ws.db - .mktx((x) => ({ peerPushPaymentIncoming: x.peerPushPaymentIncoming })) + .mktx((x) => [x.peerPushPaymentIncoming]) .runReadOnly(async (tx) => { return tx.peerPushPaymentIncoming.get(req.peerPushPaymentIncomingId); }); @@ -564,7 +560,7 @@ export async function acceptPeerPullPayment( req: AcceptPeerPullPaymentRequest, ): Promise<void> { const peerPullInc = await ws.db - .mktx((x) => ({ peerPullPaymentIncoming: x.peerPullPaymentIncoming })) + .mktx((x) => [x.peerPullPaymentIncoming]) .runReadOnly(async (tx) => { return tx.peerPullPaymentIncoming.get(req.peerPullPaymentIncomingId); }); @@ -579,13 +575,13 @@ export async function acceptPeerPullPayment( peerPullInc.contractTerms.amount, ); const coinSelRes: PeerCoinSelection | undefined = await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - coins: x.coins, - denominations: x.denominations, - refreshGroups: x.refreshGroups, - peerPullPaymentIncoming: x.peerPullPaymentIncoming, - })) + .mktx((x) => [ + x.exchanges, + x.coins, + x.denominations, + x.refreshGroups, + x.peerPullPaymentIncoming, + ]) .runReadWrite(async (tx) => { const sel = await selectPeerCoins(ws, tx, instructedAmount); if (!sel) { @@ -689,9 +685,7 @@ export async function checkPeerPullPayment( const peerPullPaymentIncomingId = encodeCrock(getRandomBytes(32)); await ws.db - .mktx((x) => ({ - peerPullPaymentIncoming: x.peerPullPaymentIncoming, - })) + .mktx((x) => [x.peerPullPaymentIncoming]) .runReadWrite(async (tx) => { await tx.peerPullPaymentIncoming.add({ peerPullPaymentIncomingId, @@ -775,11 +769,9 @@ export async function initiatePeerRequestForPay( }); await ws.db - .mktx((x) => ({ - peerPullPaymentInitiation: x.peerPullPaymentInitiations, - })) + .mktx((x) => [x.peerPullPaymentInitiations]) .runReadWrite(async (tx) => { - await tx.peerPullPaymentInitiation.put({ + await tx.peerPullPaymentInitiations.put({ amount: req.amount, contractTerms, exchangeBaseUrl: req.exchangeBaseUrl, diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts index 7d5a5bfd9..9ba532ab7 100644 --- a/packages/taler-wallet-core/src/operations/pending.ts +++ b/packages/taler-wallet-core/src/operations/pending.ts @@ -357,21 +357,21 @@ export async function getPendingOperations( ): Promise<PendingOperationsResponse> { const now = AbsoluteTime.now(); return await ws.db - .mktx((x) => ({ - backupProviders: x.backupProviders, - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - refreshGroups: x.refreshGroups, - coins: x.coins, - withdrawalGroups: x.withdrawalGroups, - proposals: x.proposals, - tips: x.tips, - purchases: x.purchases, - planchets: x.planchets, - depositGroups: x.depositGroups, - recoupGroups: x.recoupGroups, - operationRetries: x.operationRetries, - })) + .mktx((x) => [ + x.backupProviders, + x.exchanges, + x.exchangeDetails, + x.refreshGroups, + x.coins, + x.withdrawalGroups, + x.proposals, + x.tips, + x.purchases, + x.planchets, + x.depositGroups, + x.recoupGroups, + x.operationRetries, + ]) .runReadWrite(async (tx) => { const resp: PendingOperationsResponse = { pendingOperations: [], diff --git a/packages/taler-wallet-core/src/operations/recoup.ts b/packages/taler-wallet-core/src/operations/recoup.ts index 387c23f41..100bbc074 100644 --- a/packages/taler-wallet-core/src/operations/recoup.ts +++ b/packages/taler-wallet-core/src/operations/recoup.ts @@ -96,12 +96,12 @@ async function recoupTipCoin( // Thus we just put the coin to sleep. // FIXME: somehow report this to the user await ws.db - .mktx((x) => ({ - recoupGroups: x.recoupGroups, - denominations: WalletStoresV1.denominations, - refreshGroups: WalletStoresV1.refreshGroups, - coins: WalletStoresV1.coins, - })) + .mktx((stores) => [ + stores.recoupGroups, + stores.denominations, + stores.refreshGroups, + stores.coins, + ]) .runReadWrite(async (tx) => { const recoupGroup = await tx.recoupGroups.get(recoupGroupId); if (!recoupGroup) { @@ -123,9 +123,7 @@ async function recoupWithdrawCoin( ): Promise<void> { const reservePub = cs.reservePub; const denomInfo = await ws.db - .mktx((x) => ({ - denominations: x.denominations, - })) + .mktx((x) => [x.denominations]) .runReadOnly(async (tx) => { const denomInfo = await ws.getDenomInfo( ws, @@ -169,12 +167,7 @@ async function recoupWithdrawCoin( // FIXME: verify that our expectations about the amount match await ws.db - .mktx((x) => ({ - coins: x.coins, - denominations: x.denominations, - recoupGroups: x.recoupGroups, - refreshGroups: x.refreshGroups, - })) + .mktx((x) => [x.coins, x.denominations, x.recoupGroups, x.refreshGroups]) .runReadWrite(async (tx) => { const recoupGroup = await tx.recoupGroups.get(recoupGroupId); if (!recoupGroup) { @@ -207,10 +200,7 @@ async function recoupRefreshCoin( cs: RefreshCoinSource, ): Promise<void> { const d = await ws.db - .mktx((x) => ({ - coins: x.coins, - denominations: x.denominations, - })) + .mktx((x) => [x.coins, x.denominations]) .runReadOnly(async (tx) => { const denomInfo = await ws.getDenomInfo( ws, @@ -257,12 +247,7 @@ async function recoupRefreshCoin( } await ws.db - .mktx((x) => ({ - coins: x.coins, - denominations: x.denominations, - recoupGroups: x.recoupGroups, - refreshGroups: x.refreshGroups, - })) + .mktx((x) => [x.coins, x.denominations, x.recoupGroups, x.refreshGroups]) .runReadWrite(async (tx) => { const recoupGroup = await tx.recoupGroups.get(recoupGroupId); if (!recoupGroup) { @@ -319,9 +304,7 @@ export async function processRecoupGroupHandler( ): Promise<OperationAttemptResult> { const forceNow = options.forceNow ?? false; let recoupGroup = await ws.db - .mktx((x) => ({ - recoupGroups: x.recoupGroups, - })) + .mktx((x) => [x.recoupGroups]) .runReadOnly(async (tx) => { return tx.recoupGroups.get(recoupGroupId); }); @@ -343,9 +326,7 @@ export async function processRecoupGroupHandler( await Promise.all(ps); recoupGroup = await ws.db - .mktx((x) => ({ - recoupGroups: x.recoupGroups, - })) + .mktx((x) => [x.recoupGroups]) .runReadOnly(async (tx) => { return tx.recoupGroups.get(recoupGroupId); }); @@ -366,10 +347,7 @@ export async function processRecoupGroupHandler( for (let i = 0; i < recoupGroup.coinPubs.length; i++) { const coinPub = recoupGroup.coinPubs[i]; await ws.db - .mktx((x) => ({ - coins: x.coins, - reserves: x.reserves, - })) + .mktx((x) => [x.coins, x.reserves]) .runReadOnly(async (tx) => { const coin = await tx.coins.get(coinPub); if (!coin) { @@ -414,12 +392,7 @@ export async function processRecoupGroupHandler( } await ws.db - .mktx((x) => ({ - recoupGroups: x.recoupGroups, - denominations: WalletStoresV1.denominations, - refreshGroups: WalletStoresV1.refreshGroups, - coins: WalletStoresV1.coins, - })) + .mktx((x) => [x.recoupGroups, x.denominations, x.refreshGroups, x.coins]) .runReadWrite(async (tx) => { const rg2 = await tx.recoupGroups.get(recoupGroupId); if (!rg2) { @@ -497,10 +470,7 @@ async function processRecoup( coinIdx: number, ): Promise<void> { const coin = await ws.db - .mktx((x) => ({ - recoupGroups: x.recoupGroups, - coins: x.coins, - })) + .mktx((x) => [x.recoupGroups, x.coins]) .runReadOnly(async (tx) => { const recoupGroup = await tx.recoupGroups.get(recoupGroupId); if (!recoupGroup) { diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index 64b85a040..719093bd8 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -155,10 +155,7 @@ async function refreshCreateSession( ); const d = await ws.db - .mktx((x) => ({ - refreshGroups: x.refreshGroups, - coins: x.coins, - })) + .mktx((x) => [x.refreshGroups, x.coins]) .runReadWrite(async (tx) => { const refreshGroup = await tx.refreshGroups.get(refreshGroupId); if (!refreshGroup) { @@ -197,9 +194,7 @@ async function refreshCreateSession( // to update and filter withdrawable denoms. const { availableAmount, availableDenoms } = await ws.db - .mktx((x) => ({ - denominations: x.denominations, - })) + .mktx((x) => [x.denominations]) .runReadOnly(async (tx) => { const oldDenom = await ws.getDenomInfo( ws, @@ -237,10 +232,7 @@ async function refreshCreateSession( )} too small`, ); await ws.db - .mktx((x) => ({ - coins: x.coins, - refreshGroups: x.refreshGroups, - })) + .mktx((x) => [x.coins, x.refreshGroups]) .runReadWrite(async (tx) => { const rg = await tx.refreshGroups.get(refreshGroupId); if (!rg) { @@ -259,10 +251,7 @@ async function refreshCreateSession( // Store refresh session for this coin in the database. await ws.db - .mktx((x) => ({ - refreshGroups: x.refreshGroups, - coins: x.coins, - })) + .mktx((x) => [x.refreshGroups, x.coins]) .runReadWrite(async (tx) => { const rg = await tx.refreshGroups.get(refreshGroupId); if (!rg) { @@ -300,11 +289,7 @@ async function refreshMelt( coinIndex: number, ): Promise<void> { const d = await ws.db - .mktx((x) => ({ - refreshGroups: x.refreshGroups, - coins: x.coins, - denominations: x.denominations, - })) + .mktx((x) => [x.refreshGroups, x.coins, x.denominations]) .runReadWrite(async (tx) => { const refreshGroup = await tx.refreshGroups.get(refreshGroupId); if (!refreshGroup) { @@ -414,9 +399,7 @@ async function refreshMelt( if (resp.status === HttpStatusCode.NotFound) { const errDetails = await readUnexpectedResponseDetails(resp); await ws.db - .mktx((x) => ({ - refreshGroups: x.refreshGroups, - })) + .mktx((x) => [x.refreshGroups]) .runReadWrite(async (tx) => { const rg = await tx.refreshGroups.get(refreshGroupId); if (!rg) { @@ -446,9 +429,7 @@ async function refreshMelt( refreshSession.norevealIndex = norevealIndex; await ws.db - .mktx((x) => ({ - refreshGroups: x.refreshGroups, - })) + .mktx((x) => [x.refreshGroups]) .runReadWrite(async (tx) => { const rg = await tx.refreshGroups.get(refreshGroupId); if (!rg) { @@ -538,11 +519,7 @@ async function refreshReveal( ): Promise<void> { logger.info("doing refresh reveal"); const d = await ws.db - .mktx((x) => ({ - refreshGroups: x.refreshGroups, - coins: x.coins, - denominations: x.denominations, - })) + .mktx((x) => [x.refreshGroups, x.coins, x.denominations]) .runReadOnly(async (tx) => { const refreshGroup = await tx.refreshGroups.get(refreshGroupId); if (!refreshGroup) { @@ -703,10 +680,7 @@ async function refreshReveal( } await ws.db - .mktx((x) => ({ - coins: x.coins, - refreshGroups: x.refreshGroups, - })) + .mktx((x) => [x.coins, x.refreshGroups]) .runReadWrite(async (tx) => { const rg = await tx.refreshGroups.get(refreshGroupId); if (!rg) { @@ -740,12 +714,8 @@ export async function processRefreshGroup( logger.info(`processing refresh group ${refreshGroupId}`); const refreshGroup = await ws.db - .mktx((x) => ({ - refreshGroups: x.refreshGroups, - })) - .runReadOnly(async (tx) => { - return tx.refreshGroups.get(refreshGroupId); - }); + .mktx((x) => [x.refreshGroups]) + .runReadOnly(async (tx) => tx.refreshGroups.get(refreshGroupId)); if (!refreshGroup) { return { type: OperationAttemptResultType.Finished, @@ -801,7 +771,7 @@ async function processRefreshSession( `processing refresh session for coin ${coinIndex} of group ${refreshGroupId}`, ); let refreshGroup = await ws.db - .mktx((x) => ({ refreshGroups: x.refreshGroups })) + .mktx((x) => [x.refreshGroups]) .runReadOnly(async (tx) => { return tx.refreshGroups.get(refreshGroupId); }); @@ -814,7 +784,7 @@ async function processRefreshSession( if (!refreshGroup.refreshSessionPerCoin[coinIndex]) { await refreshCreateSession(ws, refreshGroupId, coinIndex); refreshGroup = await ws.db - .mktx((x) => ({ refreshGroups: x.refreshGroups })) + .mktx((x) => [x.refreshGroups]) .runReadOnly(async (tx) => { return tx.refreshGroups.get(refreshGroupId); }); @@ -981,12 +951,7 @@ export async function autoRefresh( durationFromSpec({ days: 1 }), ); await ws.db - .mktx((x) => ({ - coins: x.coins, - denominations: x.denominations, - refreshGroups: x.refreshGroups, - exchanges: x.exchanges, - })) + .mktx((x) => [x.coins, x.denominations, x.refreshGroups, x.exchanges]) .runReadWrite(async (tx) => { const exchange = await tx.exchanges.get(exchangeBaseUrl); if (!exchange) { diff --git a/packages/taler-wallet-core/src/operations/refund.ts b/packages/taler-wallet-core/src/operations/refund.ts index bc8c185db..5ee0680d7 100644 --- a/packages/taler-wallet-core/src/operations/refund.ts +++ b/packages/taler-wallet-core/src/operations/refund.ts @@ -78,9 +78,7 @@ export async function prepareRefund( } const purchase = await ws.db - .mktx((x) => ({ - purchases: x.purchases, - })) + .mktx((x) => [x.purchases]) .runReadOnly(async (tx) => { return tx.purchases.indexes.byMerchantUrlAndOrderId.get([ parseResult.merchantBaseUrl, @@ -335,12 +333,7 @@ async function acceptRefunds( const now = TalerProtocolTimestamp.now(); await ws.db - .mktx((x) => ({ - purchases: x.purchases, - coins: x.coins, - denominations: x.denominations, - refreshGroups: x.refreshGroups, - })) + .mktx((x) => [x.purchases, x.coins, x.denominations, x.refreshGroups]) .runReadWrite(async (tx) => { const p = await tx.purchases.get(proposalId); if (!p) { @@ -517,9 +510,7 @@ export async function applyRefund( } const purchase = await ws.db - .mktx((x) => ({ - purchases: x.purchases, - })) + .mktx((x) => [x.purchases]) .runReadOnly(async (tx) => { return tx.purchases.indexes.byMerchantUrlAndOrderId.get([ parseResult.merchantBaseUrl, @@ -544,9 +535,7 @@ export async function applyRefundFromPurchaseId( logger.info("processing purchase for refund"); const success = await ws.db - .mktx((x) => ({ - purchases: x.purchases, - })) + .mktx((x) => [x.purchases]) .runReadWrite(async (tx) => { const p = await tx.purchases.get(proposalId); if (!p) { @@ -569,9 +558,7 @@ export async function applyRefundFromPurchaseId( } const purchase = await ws.db - .mktx((x) => ({ - purchases: x.purchases, - })) + .mktx((x) => [x.purchases]) .runReadOnly(async (tx) => { return tx.purchases.get(proposalId); }); @@ -642,7 +629,7 @@ async function queryAndSaveAwaitingRefund( Amounts.cmp(refundAwaiting, purchase.refundAwaiting) !== 0 ) { await ws.db - .mktx((x) => ({ purchases: x.purchases })) + .mktx((x) => [x.purchases]) .runReadWrite(async (tx) => { const p = await tx.purchases.get(purchase.proposalId); if (!p) { @@ -667,9 +654,7 @@ export async function processPurchaseQueryRefund( ): Promise<OperationAttemptResult> { const waitForAutoRefund = options.waitForAutoRefund ?? false; const purchase = await ws.db - .mktx((x) => ({ - purchases: x.purchases, - })) + .mktx((x) => [x.purchases]) .runReadOnly(async (tx) => { return tx.purchases.get(proposalId); }); @@ -729,9 +714,7 @@ export async function processPurchaseQueryRefund( const abortingCoins: AbortingCoin[] = []; await ws.db - .mktx((x) => ({ - coins: x.coins, - })) + .mktx((x) => [x.coins]) .runReadOnly(async (tx) => { for (let i = 0; i < purchase.payCoinSelection.coinPubs.length; i++) { const coinPub = purchase.payCoinSelection.coinPubs[i]; @@ -796,9 +779,7 @@ export async function abortFailedPayWithRefund( proposalId: string, ): Promise<void> { await ws.db - .mktx((x) => ({ - purchases: x.purchases, - })) + .mktx((x) => [x.purchases]) .runReadWrite(async (tx) => { const purchase = await tx.purchases.get(proposalId); if (!purchase) { diff --git a/packages/taler-wallet-core/src/operations/testing.ts b/packages/taler-wallet-core/src/operations/testing.ts index 5c54d22cf..e2a0c7dbd 100644 --- a/packages/taler-wallet-core/src/operations/testing.ts +++ b/packages/taler-wallet-core/src/operations/testing.ts @@ -467,7 +467,7 @@ export async function testPay( throw Error("payment not done"); } const purchase = await ws.db - .mktx((x) => ({ purchases: x.purchases })) + .mktx((x) => [x.purchases]) .runReadOnly(async (tx) => { return tx.purchases.get(result.proposalId); }); diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts index f19be91b2..04da2b988 100644 --- a/packages/taler-wallet-core/src/operations/tip.ts +++ b/packages/taler-wallet-core/src/operations/tip.ts @@ -71,9 +71,7 @@ export async function prepareTip( } let tipRecord = await ws.db - .mktx((x) => ({ - tips: x.tips, - })) + .mktx((x) => [x.tips]) .runReadOnly(async (tx) => { return tx.tips.indexes.byMerchantTipIdAndBaseUrl.get([ res.merchantTipId, @@ -100,13 +98,13 @@ export async function prepareTip( await updateExchangeFromUrl(ws, tipPickupStatus.exchange_url); //FIXME: is this needed? withdrawDetails is not used - // * if the intention is to update the exchange information in the database + // * if the intention is to update the exchange information in the database // maybe we can use another name. `get` seems like a pure-function const withdrawDetails = await getExchangeWithdrawalInfo( ws, tipPickupStatus.exchange_url, amount, - undefined + undefined, ); const walletTipId = encodeCrock(getRandomBytes(32)); @@ -136,9 +134,7 @@ export async function prepareTip( denomSelUid, }; await ws.db - .mktx((x) => ({ - tips: x.tips, - })) + .mktx((x) => [x.tips]) .runReadWrite(async (tx) => { await tx.tips.put(newTipRecord); }); @@ -166,9 +162,7 @@ export async function processTip( } = {}, ): Promise<OperationAttemptResult> { const tipRecord = await ws.db - .mktx((x) => ({ - tips: x.tips, - })) + .mktx((x) => [x.tips]) .runReadOnly(async (tx) => { return tx.tips.get(walletTipId); }); @@ -196,9 +190,7 @@ export async function processTip( for (const dh of denomsForWithdraw.selectedDenoms) { const denom = await ws.db - .mktx((x) => ({ - denominations: x.denominations, - })) + .mktx((x) => [x.denominations]) .runReadOnly(async (tx) => { return tx.denominations.get([ tipRecord.exchangeBaseUrl, @@ -324,11 +316,7 @@ export async function processTip( } await ws.db - .mktx((x) => ({ - coins: x.coins, - tips: x.tips, - withdrawalGroups: x.withdrawalGroups, - })) + .mktx((x) => [x.coins, x.tips, x.withdrawalGroups]) .runReadWrite(async (tx) => { const tr = await tx.tips.get(walletTipId); if (!tr) { @@ -355,9 +343,7 @@ export async function acceptTip( tipId: string, ): Promise<void> { const found = await ws.db - .mktx((x) => ({ - tips: x.tips, - })) + .mktx((x) => [x.tips]) .runReadWrite(async (tx) => { const tipRecord = await tx.tips.get(tipId); if (!tipRecord) { diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 5a96fc6ff..5be24c57c 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -126,24 +126,24 @@ export async function getTransactions( const transactions: Transaction[] = []; await ws.db - .mktx((x) => ({ - coins: x.coins, - denominations: x.denominations, - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - proposals: x.proposals, - purchases: x.purchases, - refreshGroups: x.refreshGroups, - tips: x.tips, - withdrawalGroups: x.withdrawalGroups, - planchets: x.planchets, - recoupGroups: x.recoupGroups, - depositGroups: x.depositGroups, - tombstones: x.tombstones, - peerPushPaymentInitiations: x.peerPushPaymentInitiations, - peerPullPaymentIncoming: x.peerPullPaymentIncoming, - operationRetries: x.operationRetries, - })) + .mktx((x) => [ + x.coins, + x.denominations, + x.depositGroups, + x.exchangeDetails, + x.exchanges, + x.operationRetries, + x.peerPullPaymentIncoming, + x.peerPushPaymentInitiations, + x.planchets, + x.proposals, + x.purchases, + x.recoupGroups, + x.recoupGroups, + x.tips, + x.tombstones, + x.withdrawalGroups, + ]) .runReadOnly(async (tx) => { tx.peerPushPaymentInitiations.iter().forEachAsync(async (pi) => { const amount = Amounts.parseOrThrow(pi.amount); @@ -609,10 +609,7 @@ export async function deleteTransaction( ) { const withdrawalGroupId = rest[0]; await ws.db - .mktx((x) => ({ - withdrawalGroups: x.withdrawalGroups, - tombstones: x.tombstones, - })) + .mktx((x) => [x.withdrawalGroups, x.tombstones]) .runReadWrite(async (tx) => { const withdrawalGroupRecord = await tx.withdrawalGroups.get( withdrawalGroupId, @@ -628,11 +625,7 @@ export async function deleteTransaction( } else if (type === TransactionType.Payment) { const proposalId = rest[0]; await ws.db - .mktx((x) => ({ - proposals: x.proposals, - purchases: x.purchases, - tombstones: x.tombstones, - })) + .mktx((x) => [x.proposals, x.purchases, x.tombstones]) .runReadWrite(async (tx) => { let found = false; const proposal = await tx.proposals.get(proposalId); @@ -654,10 +647,7 @@ export async function deleteTransaction( } else if (type === TransactionType.Refresh) { const refreshGroupId = rest[0]; await ws.db - .mktx((x) => ({ - refreshGroups: x.refreshGroups, - tombstones: x.tombstones, - })) + .mktx((x) => [x.refreshGroups, x.tombstones]) .runReadWrite(async (tx) => { const rg = await tx.refreshGroups.get(refreshGroupId); if (rg) { @@ -670,10 +660,7 @@ export async function deleteTransaction( } else if (type === TransactionType.Tip) { const tipId = rest[0]; await ws.db - .mktx((x) => ({ - tips: x.tips, - tombstones: x.tombstones, - })) + .mktx((x) => [x.tips, x.tombstones]) .runReadWrite(async (tx) => { const tipRecord = await tx.tips.get(tipId); if (tipRecord) { @@ -686,10 +673,7 @@ export async function deleteTransaction( } else if (type === TransactionType.Deposit) { const depositGroupId = rest[0]; await ws.db - .mktx((x) => ({ - depositGroups: x.depositGroups, - tombstones: x.tombstones, - })) + .mktx((x) => [x.depositGroups, x.tombstones]) .runReadWrite(async (tx) => { const tipRecord = await tx.depositGroups.get(depositGroupId); if (tipRecord) { @@ -704,11 +688,7 @@ export async function deleteTransaction( const executionTimeStr = rest[1]; await ws.db - .mktx((x) => ({ - proposals: x.proposals, - purchases: x.purchases, - tombstones: x.tombstones, - })) + .mktx((x) => [x.proposals, x.purchases, x.tombstones]) .runReadWrite(async (tx) => { const purchase = await tx.purchases.get(proposalId); if (purchase) { @@ -726,10 +706,7 @@ export async function deleteTransaction( } else if (type === TransactionType.PeerPullDebit) { const peerPullPaymentIncomingId = rest[0]; await ws.db - .mktx((x) => ({ - peerPullPaymentIncoming: x.peerPullPaymentIncoming, - tombstones: x.tombstones, - })) + .mktx((x) => [x.peerPullPaymentIncoming, x.tombstones]) .runReadWrite(async (tx) => { const debit = await tx.peerPullPaymentIncoming.get( peerPullPaymentIncomingId, @@ -747,10 +724,7 @@ export async function deleteTransaction( } else if (type === TransactionType.PeerPushDebit) { const pursePub = rest[0]; await ws.db - .mktx((x) => ({ - peerPushPaymentInitiations: x.peerPushPaymentInitiations, - tombstones: x.tombstones, - })) + .mktx((x) => [x.peerPushPaymentInitiations, x.tombstones]) .runReadWrite(async (tx) => { const debit = await tx.peerPushPaymentInitiations.get(pursePub); if (debit) { diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index ce5863b31..1b8383776 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -195,9 +195,9 @@ export interface ExchangeWithdrawDetails { /** * If the exchange supports age-restricted coins it will return * the array of ages. - * + * */ - ageRestrictionOptions?: number[], + ageRestrictionOptions?: number[]; } /** @@ -248,7 +248,7 @@ export function selectWithdrawalDenominations( for (const d of denoms) { let count = 0; const cost = Amounts.add(d.value, d.feeWithdraw).amount; - for (; ;) { + for (;;) { if (Amounts.cmp(remaining, cost) < 0) { break; } @@ -412,7 +412,7 @@ export async function getCandidateWithdrawalDenoms( exchangeBaseUrl: string, ): Promise<DenominationRecord[]> { return await ws.db - .mktx((x) => ({ denominations: x.denominations })) + .mktx((x) => [x.denominations]) .runReadOnly(async (tx) => { const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl.getAll( exchangeBaseUrl, @@ -434,9 +434,7 @@ async function processPlanchetGenerate( coinIdx: number, ): Promise<void> { let planchet = await ws.db - .mktx((x) => ({ - planchets: x.planchets, - })) + .mktx((x) => [x.planchets]) .runReadOnly(async (tx) => { return tx.planchets.indexes.byGroupAndIndex.get([ withdrawalGroup.withdrawalGroupId, @@ -462,9 +460,7 @@ async function processPlanchetGenerate( const denomPubHash = maybeDenomPubHash; const denom = await ws.db - .mktx((x) => ({ - denominations: x.denominations, - })) + .mktx((x) => [x.denominations]) .runReadOnly(async (tx) => { return ws.getDenomInfo( ws, @@ -500,7 +496,7 @@ async function processPlanchetGenerate( lastError: undefined, }; await ws.db - .mktx((x) => ({ planchets: x.planchets })) + .mktx((x) => [x.planchets]) .runReadWrite(async (tx) => { const p = await tx.planchets.indexes.byGroupAndIndex.get([ withdrawalGroup.withdrawalGroupId, @@ -529,12 +525,12 @@ async function processPlanchetExchangeRequest( `processing planchet exchange request ${withdrawalGroup.withdrawalGroupId}/${coinIdx}`, ); const d = await ws.db - .mktx((x) => ({ - withdrawalGroups: x.withdrawalGroups, - planchets: x.planchets, - exchanges: x.exchanges, - denominations: x.denominations, - })) + .mktx((x) => [ + x.withdrawalGroups, + x.planchets, + x.exchanges, + x.denominations, + ]) .runReadOnly(async (tx) => { let planchet = await tx.planchets.indexes.byGroupAndIndex.get([ withdrawalGroup.withdrawalGroupId, @@ -599,7 +595,7 @@ async function processPlanchetExchangeRequest( logger.trace("withdrawal request failed", e); logger.trace(e); await ws.db - .mktx((x) => ({ planchets: x.planchets })) + .mktx((x) => [x.planchets]) .runReadWrite(async (tx) => { let planchet = await tx.planchets.indexes.byGroupAndIndex.get([ withdrawalGroup.withdrawalGroupId, @@ -631,12 +627,12 @@ async function processPlanchetExchangeBatchRequest( .map((x) => x.count) .reduce((a, b) => a + b); const d = await ws.db - .mktx((x) => ({ - withdrawalGroups: x.withdrawalGroups, - planchets: x.planchets, - exchanges: x.exchanges, - denominations: x.denominations, - })) + .mktx((x) => [ + x.withdrawalGroups, + x.planchets, + x.exchanges, + x.denominations, + ]) .runReadOnly(async (tx) => { const reqBody: { planchets: ExchangeWithdrawRequest[] } = { planchets: [], @@ -705,11 +701,7 @@ async function processPlanchetVerifyAndStoreCoin( resp: WithdrawResponse, ): Promise<void> { const d = await ws.db - .mktx((x) => ({ - withdrawalGroups: x.withdrawalGroups, - planchets: x.planchets, - denominations: x.denominations, - })) + .mktx((x) => [x.withdrawalGroups, x.planchets, x.denominations]) .runReadOnly(async (tx) => { let planchet = await tx.planchets.indexes.byGroupAndIndex.get([ withdrawalGroup.withdrawalGroupId, @@ -768,7 +760,7 @@ async function processPlanchetVerifyAndStoreCoin( if (!isValid) { await ws.db - .mktx((x) => ({ planchets: x.planchets })) + .mktx((x) => [x.planchets]) .runReadWrite(async (tx) => { let planchet = await tx.planchets.indexes.byGroupAndIndex.get([ withdrawalGroup.withdrawalGroupId, @@ -823,11 +815,7 @@ async function processPlanchetVerifyAndStoreCoin( // withdrawal succeeded. If so, mark the withdrawal // group as finished. const firstSuccess = await ws.db - .mktx((x) => ({ - coins: x.coins, - withdrawalGroups: x.withdrawalGroups, - planchets: x.planchets, - })) + .mktx((x) => [x.coins, x.withdrawalGroups, x.planchets]) .runReadWrite(async (tx) => { const p = await tx.planchets.get(planchetCoinPub); if (!p || p.withdrawalDone) { @@ -858,10 +846,7 @@ export async function updateWithdrawalDenoms( `updating denominations used for withdrawal for ${exchangeBaseUrl}`, ); const exchangeDetails = await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - })) + .mktx((x) => [x.exchanges, x.exchangeDetails]) .runReadOnly(async (tx) => { return ws.exchangeOps.getExchangeDetails(tx, exchangeBaseUrl); }); @@ -890,7 +875,8 @@ export async function updateWithdrawalDenoms( denom.verificationStatus === DenominationVerificationStatus.Unverified ) { logger.trace( - `Validating denomination (${current + 1}/${denominations.length + `Validating denomination (${current + 1}/${ + denominations.length }) signature of ${denom.denomPubHash}`, ); let valid = false; @@ -919,7 +905,7 @@ export async function updateWithdrawalDenoms( if (updatedDenominations.length > 0) { logger.trace("writing denomination batch to db"); await ws.db - .mktx((x) => ({ denominations: x.denominations })) + .mktx((x) => [x.denominations]) .runReadWrite(async (tx) => { for (let i = 0; i < updatedDenominations.length; i++) { const denom = updatedDenominations[i]; @@ -973,7 +959,7 @@ async function queryReserve( if ( resp.status === 404 && result.talerErrorResponse.code === - TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN + TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN ) { ws.notify({ type: NotificationType.ReserveNotYetFound, @@ -988,9 +974,7 @@ async function queryReserve( logger.trace(`got reserve status ${j2s(result.response)}`); await ws.db - .mktx((x) => ({ - withdrawalGroups: x.withdrawalGroups, - })) + .mktx((x) => [x.withdrawalGroups]) .runReadWrite(async (tx) => { const wg = await tx.withdrawalGroups.get(withdrawalGroupId); if (!wg) { @@ -1011,7 +995,7 @@ export async function processWithdrawalGroup( ): Promise<OperationAttemptResult> { logger.trace("processing withdrawal group", withdrawalGroupId); const withdrawalGroup = await ws.db - .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups })) + .mktx((x) => [x.withdrawalGroups]) .runReadOnly(async (tx) => { return tx.withdrawalGroups.get(withdrawalGroupId); }); @@ -1080,7 +1064,7 @@ export async function processWithdrawalGroup( if (withdrawalGroup.denomsSel.selectedDenoms.length === 0) { await ws.db - .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups })) + .mktx((x) => [x.withdrawalGroups]) .runReadWrite(async (tx) => { const wg = await tx.withdrawalGroups.get(withdrawalGroupId); if (!wg) { @@ -1148,11 +1132,7 @@ export async function processWithdrawalGroup( let errorsPerCoin: Record<number, TalerErrorDetail> = {}; await ws.db - .mktx((x) => ({ - coins: x.coins, - withdrawalGroups: x.withdrawalGroups, - planchets: x.planchets, - })) + .mktx((x) => [x.coins, x.withdrawalGroups, x.planchets]) .runReadWrite(async (tx) => { const wg = await tx.withdrawalGroups.get(withdrawalGroupId); if (!wg) { @@ -1202,7 +1182,9 @@ export async function processWithdrawalGroup( }; } -const AGE_MASK_GROUPS = "8:10:12:14:16:18".split(":").map(n => parseInt(n, 10)) +const AGE_MASK_GROUPS = "8:10:12:14:16:18" + .split(":") + .map((n) => parseInt(n, 10)); export async function getExchangeWithdrawalInfo( ws: InternalWalletState, @@ -1237,14 +1219,14 @@ export async function getExchangeWithdrawalInfo( exchange, ); - let hasDenomWithAgeRestriction = false + let hasDenomWithAgeRestriction = false; let earliestDepositExpiration: TalerProtocolTimestamp | undefined; for (let i = 0; i < selectedDenoms.selectedDenoms.length; i++) { const ds = selectedDenoms.selectedDenoms[i]; // FIXME: Do in one transaction! const denom = await ws.db - .mktx((x) => ({ denominations: x.denominations })) + .mktx((x) => [x.denominations]) .runReadOnly(async (tx) => { return ws.getDenomInfo(ws, tx, exchangeBaseUrl, ds.denomPubHash); }); @@ -1262,13 +1244,14 @@ export async function getExchangeWithdrawalInfo( ) { earliestDepositExpiration = expireDeposit; } - hasDenomWithAgeRestriction = hasDenomWithAgeRestriction || denom.denomPub.age_mask > 0 + hasDenomWithAgeRestriction = + hasDenomWithAgeRestriction || denom.denomPub.age_mask > 0; } checkLogicInvariant(!!earliestDepositExpiration); const possibleDenoms = await ws.db - .mktx((x) => ({ denominations: x.denominations })) + .mktx((x) => [x.denominations]) .runReadOnly(async (tx) => { const ds = await tx.denominations.indexes.byExchangeBaseUrl.getAll( exchangeBaseUrl, @@ -1290,7 +1273,7 @@ export async function getExchangeWithdrawalInfo( ) { logger.warn( `wallet's support for exchange protocol version ${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` + - `(exchange has ${exchangeDetails.protocolVersion}), checking for updates`, + `(exchange has ${exchangeDetails.protocolVersion}), checking for updates`, ); } } @@ -1325,7 +1308,9 @@ export async function getExchangeWithdrawalInfo( withdrawalAmountRaw: Amounts.stringify(instructedAmount), // TODO: remove hardcoding, this should be calculated from the denominations info // force enabled for testing - ageRestrictionOptions: hasDenomWithAgeRestriction ? AGE_MASK_GROUPS : undefined + ageRestrictionOptions: hasDenomWithAgeRestriction + ? AGE_MASK_GROUPS + : undefined, }; return ret; } @@ -1369,11 +1354,7 @@ export async function getWithdrawalDetailsForUri( const exchanges: ExchangeListItem[] = []; await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - denominations: x.denominations, - })) + .mktx((x) => [x.exchanges, x.exchangeDetails, x.denominations]) .runReadOnly(async (tx) => { const exchangeRecords = await tx.exchanges.iter().toArray(); for (const r of exchangeRecords) { @@ -1409,11 +1390,7 @@ export async function getFundingPaytoUrisTx( withdrawalGroupId: string, ): Promise<string[]> { return await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - withdrawalGroups: x.withdrawalGroups, - })) + .mktx((x) => [x.exchanges, x.exchangeDetails, x.withdrawalGroups]) .runReadWrite((tx) => getFundingPaytoUris(tx, withdrawalGroupId)); } @@ -1461,9 +1438,7 @@ async function getWithdrawalGroupRecordTx( }, ): Promise<WithdrawalGroupRecord | undefined> { return await db - .mktx((x) => ({ - withdrawalGroups: x.withdrawalGroups, - })) + .mktx((x) => [x.withdrawalGroups]) .runReadOnly(async (tx) => { return tx.withdrawalGroups.get(req.withdrawalGroupId); }); @@ -1490,9 +1465,7 @@ async function registerReserveWithBank( withdrawalGroupId: string, ): Promise<void> { const withdrawalGroup = await ws.db - .mktx((x) => ({ - withdrawalGroups: x.withdrawalGroups, - })) + .mktx((x) => [x.withdrawalGroups]) .runReadOnly(async (tx) => { return await tx.withdrawalGroups.get(withdrawalGroupId); }); @@ -1526,9 +1499,7 @@ async function registerReserveWithBank( codecForBankWithdrawalOperationPostResponse(), ); await ws.db - .mktx((x) => ({ - withdrawalGroups: x.withdrawalGroups, - })) + .mktx((x) => [x.withdrawalGroups]) .runReadWrite(async (tx) => { const r = await tx.withdrawalGroups.get(withdrawalGroupId); if (!r) { @@ -1606,9 +1577,7 @@ async function processReserveBankStatus( if (status.aborted) { logger.info("bank aborted the withdrawal"); await ws.db - .mktx((x) => ({ - withdrawalGroups: x.withdrawalGroups, - })) + .mktx((x) => [x.withdrawalGroups]) .runReadWrite(async (tx) => { const r = await tx.withdrawalGroups.get(withdrawalGroupId); if (!r) { @@ -1648,9 +1617,7 @@ async function processReserveBankStatus( } await ws.db - .mktx((x) => ({ - withdrawalGroups: x.withdrawalGroups, - })) + .mktx((x) => [x.withdrawalGroups]) .runReadWrite(async (tx) => { const r = await tx.withdrawalGroups.get(withdrawalGroupId); if (!r) { @@ -1753,13 +1720,13 @@ export async function internalCreateWithdrawalGroup( ); await ws.db - .mktx((x) => ({ - withdrawalGroups: x.withdrawalGroups, - reserves: x.reserves, - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - exchangeTrust: x.exchangeTrust, - })) + .mktx((x) => [ + x.withdrawalGroups, + x.reserves, + x.exchanges, + x.exchangeDetails, + x.exchangeTrust, + ]) .runReadWrite(async (tx) => { await tx.withdrawalGroups.add(withdrawalGroup); await tx.reserves.put({ @@ -1790,7 +1757,7 @@ export async function acceptWithdrawalFromUri( }, ): Promise<AcceptWithdrawalResponse> { const existingWithdrawalGroup = await ws.db - .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups })) + .mktx((x) => [x.withdrawalGroups]) .runReadOnly(async (tx) => { return await tx.withdrawalGroups.indexes.byTalerWithdrawUri.get( req.talerWithdrawUri, @@ -1899,12 +1866,12 @@ export async function createManualWithdrawal( const withdrawalGroupId = withdrawalGroup.withdrawalGroupId; const exchangePaytoUris = await ws.db - .mktx((x) => ({ - withdrawalGroups: x.withdrawalGroups, - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - exchangeTrust: x.exchangeTrust, - })) + .mktx((x) => [ + x.withdrawalGroups, + x.exchanges, + x.exchangeDetails, + x.exchangeTrust, + ]) .runReadWrite(async (tx) => { return await getFundingPaytoUris(tx, withdrawalGroup.withdrawalGroupId); }); diff --git a/packages/taler-wallet-core/src/util/query.ts b/packages/taler-wallet-core/src/util/query.ts index 65b67eff2..025959253 100644 --- a/packages/taler-wallet-core/src/util/query.ts +++ b/packages/taler-wallet-core/src/util/query.ts @@ -36,6 +36,7 @@ import { } from "@gnu-taler/idb-bridge"; import { Logger } from "@gnu-taler/taler-util"; import { performanceNow } from "./timer.js"; +import { access } from "fs"; const logger = new Logger("query.ts"); @@ -280,7 +281,6 @@ export interface IndexDescriptor { export interface StoreDescriptor<RecordType> { _dummy: undefined & RecordType; - name: string; keyPath?: IDBKeyPath | IDBKeyPath[]; autoIncrement?: boolean; } @@ -291,10 +291,9 @@ export interface StoreOptions { } export function describeContents<RecordType = never>( - name: string, options: StoreOptions, ): StoreDescriptor<RecordType> { - return { name, keyPath: options.keyPath, _dummy: undefined as any }; + return { keyPath: options.keyPath, _dummy: undefined as any }; } export function describeIndex( @@ -345,9 +344,11 @@ export interface StoreReadWriteAccessor<RecordType, IndexMap> { } export interface StoreWithIndexes< + StoreName extends string, SD extends StoreDescriptor<unknown>, IndexMap, > { + storeName: StoreName; store: SD; indexMap: IndexMap; @@ -362,11 +363,17 @@ export type GetRecordType<T> = T extends StoreDescriptor<infer X> ? X : unknown; const storeWithIndexesSymbol = Symbol("StoreWithIndexesMark"); -export function describeStore<SD extends StoreDescriptor<unknown>, IndexMap>( +export function describeStore< + StoreName extends string, + SD extends StoreDescriptor<unknown>, + IndexMap, +>( + name: StoreName, s: SD, m: IndexMap, -): StoreWithIndexes<SD, IndexMap> { +): StoreWithIndexes<StoreName, SD, IndexMap> { return { + storeName: name, store: s, indexMap: m, mark: storeWithIndexesSymbol, @@ -375,6 +382,7 @@ export function describeStore<SD extends StoreDescriptor<unknown>, IndexMap>( export type GetReadOnlyAccess<BoundStores> = { [P in keyof BoundStores]: BoundStores[P] extends StoreWithIndexes< + infer SN, infer SD, infer IM > @@ -384,6 +392,7 @@ export type GetReadOnlyAccess<BoundStores> = { export type GetReadWriteAccess<BoundStores> = { [P in keyof BoundStores]: BoundStores[P] extends StoreWithIndexes< + infer SN, infer SD, infer IM > @@ -404,8 +413,12 @@ export interface TransactionContext<BoundStores> { runReadOnly<T>(f: ReadOnlyTransactionFunction<BoundStores, T>): Promise<T>; } -type CheckDescriptor<T> = T extends StoreWithIndexes<infer SD, infer IM> - ? StoreWithIndexes<SD, IM> +type CheckDescriptor<T> = T extends StoreWithIndexes< + infer SN, + infer SD, + infer IM +> + ? StoreWithIndexes<SN, SD, IM> : unknown; type GetPickerType<F, SM> = F extends (x: SM) => infer Out @@ -477,13 +490,13 @@ function runTx<Arg, Res>( function makeReadContext( tx: IDBTransaction, - storePick: { [n: string]: StoreWithIndexes<any, any> }, + storePick: { [n: string]: StoreWithIndexes<any, any, any> }, ): any { const ctx: { [s: string]: StoreReadOnlyAccessor<any, any> } = {}; for (const storeAlias in storePick) { const indexes: { [s: string]: IndexReadOnlyAccessor<any> } = {}; const swi = storePick[storeAlias]; - const storeName = swi.store.name; + const storeName = swi.storeName; for (const indexAlias in storePick[storeAlias].indexMap) { const indexDescriptor: IndexDescriptor = storePick[storeAlias].indexMap[indexAlias]; @@ -526,13 +539,13 @@ function makeReadContext( function makeWriteContext( tx: IDBTransaction, - storePick: { [n: string]: StoreWithIndexes<any, any> }, + storePick: { [n: string]: StoreWithIndexes<any, any, any> }, ): any { const ctx: { [s: string]: StoreReadWriteAccessor<any, any> } = {}; for (const storeAlias in storePick) { const indexes: { [s: string]: IndexReadWriteAccessor<any> } = {}; const swi = storePick[storeAlias]; - const storeName = swi.store.name; + const storeName = swi.storeName; for (const indexAlias in storePick[storeAlias].indexMap) { const indexDescriptor: IndexDescriptor = storePick[storeAlias].indexMap[indexAlias]; @@ -585,25 +598,11 @@ function makeWriteContext( return ctx; } -const storeList = [ - { name: "foo" as const, value: 1 as const }, - { name: "bar" as const, value: 2 as const }, -]; -// => { foo: { value: 1}, bar: {value: 2} } - -type StoreList = typeof storeList; - -type StoreNames = StoreList[number] extends { name: infer I } ? I : never; - -type H = StoreList[number] & { name: "foo"}; - -type Cleanup<V> = V extends { name: infer N, value: infer X} ? {name: N, value: X} : never; - -type G = { - [X in StoreNames]: { - X: StoreList[number] & { name: X }; - }; -}; +type StoreNamesOf<X> = X extends { [x: number]: infer F } + ? F extends { storeName: infer I } + ? I + : never + : never; /** * Type-safe access to a database with a particular store map. @@ -617,36 +616,41 @@ export class DbAccess<StoreMap> { return this.db; } - mktx2< + /** + * Run a transaction with selected object stores. + * + * The {@link namePicker} must be a function that selects a list of object + * stores from all available object stores. + */ + mktx< StoreNames extends keyof StoreMap, Stores extends StoreMap[StoreNames], StoreList extends Stores[], - >(namePicker: (x: StoreMap) => StoreList): StoreList { - return namePicker(this.stores); - } - - mktx< - PickerType extends (x: StoreMap) => unknown, - BoundStores extends GetPickerType<PickerType, StoreMap>, - >(f: PickerType): TransactionContext<BoundStores> { - const storePick = f(this.stores) as any; + BoundStores extends { + [X in StoreNamesOf<StoreList>]: StoreList[number] & { storeName: X }; + }, + >(namePicker: (x: StoreMap) => StoreList): TransactionContext<BoundStores> { + const storePick = namePicker(this.stores) as any; if (typeof storePick !== "object" || storePick === null) { throw Error(); } const storeNames: string[] = []; - for (const storeAlias of Object.keys(storePick)) { - const swi = (storePick as any)[storeAlias] as StoreWithIndexes<any, any>; + const accessibleStores: { [x: string]: StoreWithIndexes<any, any, any> } = + {}; + for (const swiPicked of storePick) { + const swi = swiPicked as StoreWithIndexes<any, any, any>; if (swi.mark !== storeWithIndexesSymbol) { throw Error("invalid store descriptor returned from selector function"); } - storeNames.push(swi.store.name); + storeNames.push(swi.storeName); + accessibleStores[swi.storeName] = swi; } const runReadOnly = <T>( txf: ReadOnlyTransactionFunction<BoundStores, T>, ): Promise<T> => { const tx = this.db.transaction(storeNames, "readonly"); - const readContext = makeReadContext(tx, storePick); + const readContext = makeReadContext(tx, accessibleStores); return runTx(tx, readContext, txf); }; @@ -654,7 +658,7 @@ export class DbAccess<StoreMap> { txf: ReadWriteTransactionFunction<BoundStores, T>, ): Promise<T> => { const tx = this.db.transaction(storeNames, "readwrite"); - const writeContext = makeWriteContext(tx, storePick); + const writeContext = makeWriteContext(tx, accessibleStores); return runTx(tx, writeContext, txf); }; diff --git a/packages/taler-wallet-core/src/util/retries.ts b/packages/taler-wallet-core/src/util/retries.ts index 3a41e8348..4763bbc42 100644 --- a/packages/taler-wallet-core/src/util/retries.ts +++ b/packages/taler-wallet-core/src/util/retries.ts @@ -201,8 +201,9 @@ export async function scheduleRetry( errorDetail?: TalerErrorDetail, ): Promise<void> { return await ws.db - .mktx((x) => ({ operationRetries: x.operationRetries })) + .mktx((x) => [x.operationRetries]) .runReadWrite(async (tx) => { + tx.operationRetries scheduleRetryInTx(ws, tx, opId, errorDetail); }); } diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index f041d9aa9..58e11e90f 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -328,7 +328,7 @@ export async function storeOperationError( e: TalerErrorDetail, ): Promise<void> { await ws.db - .mktx((x) => ({ operationRetries: x.operationRetries })) + .mktx((x) => [x.operationRetries]) .runReadWrite(async (tx) => { const retryRecord = await tx.operationRetries.get(pendingTaskId); if (!retryRecord) { @@ -345,7 +345,7 @@ export async function storeOperationFinished( pendingTaskId: string, ): Promise<void> { await ws.db - .mktx((x) => ({ operationRetries: x.operationRetries })) + .mktx((x) => [x.operationRetries]) .runReadWrite(async (tx) => { await tx.operationRetries.delete(pendingTaskId); }); @@ -356,7 +356,7 @@ export async function storeOperationPending( pendingTaskId: string, ): Promise<void> { await ws.db - .mktx((x) => ({ operationRetries: x.operationRetries })) + .mktx((x) => [x.operationRetries]) .runReadWrite(async (tx) => { const retryRecord = await tx.operationRetries.get(pendingTaskId); if (!retryRecord) { @@ -542,7 +542,7 @@ async function runTaskLoop( */ async function fillDefaults(ws: InternalWalletState): Promise<void> { await ws.db - .mktx((x) => ({ config: x.config, auditorTrustStore: x.auditorTrust })) + .mktx((x) => [x.config, x.auditorTrust]) .runReadWrite(async (tx) => { let applied = false; await tx.config.iter().forEach((x) => { @@ -552,7 +552,7 @@ async function fillDefaults(ws: InternalWalletState): Promise<void> { }); if (!applied) { for (const c of builtinAuditors) { - await tx.auditorTrustStore.put(c); + await tx.auditorTrust.put(c); } } // FIXME: make sure exchanges are added transactionally to @@ -634,9 +634,7 @@ async function listKnownBankAccounts( ): Promise<KnownBankAccounts> { const accounts: { [account: string]: PaytoUri } = {}; await ws.db - .mktx((x) => ({ - withdrawalGroups: x.withdrawalGroups, - })) + .mktx((x) => [x.withdrawalGroups]) .runReadOnly(async (tx) => { const withdrawalGroups = await tx.withdrawalGroups.iter().toArray(); for (const r of withdrawalGroups) { @@ -660,11 +658,7 @@ async function getExchanges( ): Promise<ExchangesListResponse> { const exchanges: ExchangeListItem[] = []; await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - denominations: x.denominations, - })) + .mktx((x) => [x.exchanges, x.exchangeDetails, x.denominations]) .runReadOnly(async (tx) => { const exchangeRecords = await tx.exchanges.iter().toArray(); for (const r of exchangeRecords) { @@ -708,11 +702,7 @@ async function getExchangeDetailedInfo( ): Promise<ExchangeFullDetails> { //TODO: should we use the forceUpdate parameter? const exchange = await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - denominations: x.denominations, - })) + .mktx((x) => [x.exchanges, x.exchangeDetails, x.denominations]) .runReadOnly(async (tx) => { const ex = await tx.exchanges.get(exchangeBaseurl); const dp = ex?.detailsPointer; @@ -790,9 +780,7 @@ async function setCoinSuspended( suspended: boolean, ): Promise<void> { await ws.db - .mktx((x) => ({ - coins: x.coins, - })) + .mktx((x) => [x.coins]) .runReadWrite(async (tx) => { const c = await tx.coins.get(coinPub); if (!c) { @@ -811,11 +799,7 @@ async function dumpCoins(ws: InternalWalletState): Promise<CoinDumpJson> { const coinsJson: CoinDumpJson = { coins: [] }; logger.info("dumping coins"); await ws.db - .mktx((x) => ({ - coins: x.coins, - denominations: x.denominations, - withdrawalGroups: x.withdrawalGroups, - })) + .mktx((x) => [x.coins, x.denominations, x.withdrawalGroups]) .runReadOnly(async (tx) => { const coins = await tx.coins.iter().toArray(); for (const c of coins) { @@ -1065,11 +1049,7 @@ async function dispatchRequestInternal( const req = codecForForceRefreshRequest().decode(payload); const coinPubs = req.coinPubList.map((x) => ({ coinPub: x })); const refreshGroupId = await ws.db - .mktx((x) => ({ - refreshGroups: x.refreshGroups, - denominations: x.denominations, - coins: x.coins, - })) + .mktx((x) => [x.refreshGroups, x.denominations, x.coins]) .runReadWrite(async (tx) => { return await createRefreshGroup( ws, @@ -1164,10 +1144,7 @@ async function dispatchRequestInternal( } case "listCurrencies": { return await ws.db - .mktx((x) => ({ - auditorTrust: x.auditorTrust, - exchangeTrust: x.exchangeTrust, - })) + .mktx((x) => [x.auditorTrust, x.exchangeTrust]) .runReadOnly(async (tx) => { const trustedAuditors = await tx.auditorTrust.iter().toArray(); const trustedExchanges = await tx.exchangeTrust.iter().toArray(); |