From 1ec521b9d214b286e747b3ccb3113730ac3a2509 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Mon, 19 Feb 2024 12:49:17 +0100 Subject: wallet-core: simplify/unify DB access --- packages/taler-wallet-core/src/wallet.ts | 303 +++++++++++++++---------------- 1 file changed, 150 insertions(+), 153 deletions(-) (limited to 'packages/taler-wallet-core/src/wallet.ts') diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index cfe171bd0..45970e770 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -141,6 +141,8 @@ import { CoinSourceType, ConfigRecordKey, DenominationRecord, + WalletDbReadOnlyTransaction, + WalletDbReadWriteTransaction, WalletStoresV1, clearDatabase, exportDb, @@ -268,11 +270,7 @@ import { getMaxPeerPushAmount, } from "./util/instructedAmountConversion.js"; import { checkDbInvariant } from "./util/invariants.js"; -import { - DbAccess, - GetReadOnlyAccess, - GetReadWriteAccess, -} from "./util/query.js"; +import { DbAccess } from "./util/query.js"; import { TimerAPI, TimerGroup } from "./util/timer.js"; import { WALLET_BANK_CONVERSION_API_PROTOCOL_VERSION, @@ -306,30 +304,28 @@ async function runTaskLoop( */ async function fillDefaults(ws: InternalWalletState): Promise { const notifications: WalletNotification[] = []; - await ws.db - .mktx((x) => [x.config, x.exchanges, x.exchangeDetails]) - .runReadWrite(async (tx) => { - const appliedRec = await tx.config.get("currencyDefaultsApplied"); - let alreadyApplied = appliedRec ? !!appliedRec.value : false; - if (alreadyApplied) { - logger.trace("defaults already applied"); - return; - } - for (const exch of ws.config.builtin.exchanges) { - const resp = await addPresetExchangeEntry( - tx, - exch.exchangeBaseUrl, - exch.currencyHint, - ); - if (resp.notification) { - notifications.push(resp.notification); - } + await ws.db.runReadWriteTx(["config", "exchanges"], async (tx) => { + const appliedRec = await tx.config.get("currencyDefaultsApplied"); + let alreadyApplied = appliedRec ? !!appliedRec.value : false; + if (alreadyApplied) { + logger.trace("defaults already applied"); + return; + } + for (const exch of ws.config.builtin.exchanges) { + const resp = await addPresetExchangeEntry( + tx, + exch.exchangeBaseUrl, + exch.currencyHint, + ); + if (resp.notification) { + notifications.push(resp.notification); } - await tx.config.put({ - key: ConfigRecordKey.CurrencyDefaultsApplied, - value: true, - }); + } + await tx.config.put({ + key: ConfigRecordKey.CurrencyDefaultsApplied, + value: true, }); + }); for (const notif of notifications) { ws.notify(notif); } @@ -344,25 +340,23 @@ async function listKnownBankAccounts( currency?: string, ): Promise { const accounts: KnownBankAccountsInfo[] = []; - await ws.db - .mktx((x) => [x.bankAccounts]) - .runReadOnly(async (tx) => { - const knownAccounts = await tx.bankAccounts.iter().toArray(); - for (const r of knownAccounts) { - if (currency && currency !== r.currency) { - continue; - } - const payto = parsePaytoUri(r.uri); - if (payto) { - accounts.push({ - uri: payto, - alias: r.alias, - kyc_completed: r.kycCompleted, - currency: r.currency, - }); - } + await ws.db.runReadOnlyTx(["bankAccounts"], async (tx) => { + const knownAccounts = await tx.bankAccounts.iter().toArray(); + for (const r of knownAccounts) { + if (currency && currency !== r.currency) { + continue; } - }); + const payto = parsePaytoUri(r.uri); + if (payto) { + accounts.push({ + uri: payto, + alias: r.alias, + kyc_completed: r.kycCompleted, + currency: r.currency, + }); + } + } + }); return { accounts }; } @@ -374,16 +368,14 @@ async function addKnownBankAccounts( alias: string, currency: string, ): Promise { - await ws.db - .mktx((x) => [x.bankAccounts]) - .runReadWrite(async (tx) => { - tx.bankAccounts.put({ - uri: payto, - alias: alias, - currency: currency, - kycCompleted: false, - }); + await ws.db.runReadWriteTx(["bankAccounts"], async (tx) => { + tx.bankAccounts.put({ + uri: payto, + alias: alias, + currency: currency, + kycCompleted: false, }); + }); return; } @@ -393,15 +385,13 @@ async function forgetKnownBankAccounts( ws: InternalWalletState, payto: string, ): Promise { - await ws.db - .mktx((x) => [x.bankAccounts]) - .runReadWrite(async (tx) => { - const account = await tx.bankAccounts.get(payto); - if (!account) { - throw Error(`account not found: ${payto}`); - } - tx.bankAccounts.delete(account.uri); - }); + await ws.db.runReadWriteTx(["bankAccounts"], async (tx) => { + const account = await tx.bankAccounts.get(payto); + if (!account) { + throw Error(`account not found: ${payto}`); + } + tx.bankAccounts.delete(account.uri); + }); return; } @@ -410,41 +400,39 @@ async function setCoinSuspended( coinPub: string, suspended: boolean, ): Promise { - await ws.db - .mktx((x) => [x.coins, x.coinAvailability]) - .runReadWrite(async (tx) => { - const c = await tx.coins.get(coinPub); - if (!c) { - logger.warn(`coin ${coinPub} not found, won't suspend`); + await ws.db.runReadWriteTx(["coins", "coinAvailability"], async (tx) => { + const c = await tx.coins.get(coinPub); + if (!c) { + logger.warn(`coin ${coinPub} not found, won't suspend`); + return; + } + const coinAvailability = await tx.coinAvailability.get([ + c.exchangeBaseUrl, + c.denomPubHash, + c.maxAge, + ]); + checkDbInvariant(!!coinAvailability); + if (suspended) { + if (c.status !== CoinStatus.Fresh) { return; } - const coinAvailability = await tx.coinAvailability.get([ - c.exchangeBaseUrl, - c.denomPubHash, - c.maxAge, - ]); - checkDbInvariant(!!coinAvailability); - if (suspended) { - if (c.status !== CoinStatus.Fresh) { - return; - } - if (coinAvailability.freshCoinCount === 0) { - throw Error( - `invalid coin count ${coinAvailability.freshCoinCount} in DB`, - ); - } - coinAvailability.freshCoinCount--; - c.status = CoinStatus.FreshSuspended; - } else { - if (c.status == CoinStatus.Dormant) { - return; - } - coinAvailability.freshCoinCount++; - c.status = CoinStatus.Fresh; + if (coinAvailability.freshCoinCount === 0) { + throw Error( + `invalid coin count ${coinAvailability.freshCoinCount} in DB`, + ); } - await tx.coins.put(c); - await tx.coinAvailability.put(coinAvailability); - }); + coinAvailability.freshCoinCount--; + c.status = CoinStatus.FreshSuspended; + } else { + if (c.status == CoinStatus.Dormant) { + return; + } + coinAvailability.freshCoinCount++; + c.status = CoinStatus.Fresh; + } + await tx.coins.put(c); + await tx.coinAvailability.put(coinAvailability); + }); } /** @@ -453,57 +441,55 @@ async function setCoinSuspended( async function dumpCoins(ws: InternalWalletState): Promise { const coinsJson: CoinDumpJson = { coins: [] }; logger.info("dumping coins"); - await ws.db - .mktx((x) => [x.coins, x.denominations, x.withdrawalGroups]) - .runReadOnly(async (tx) => { - const coins = await tx.coins.iter().toArray(); - for (const c of coins) { - const denom = await tx.denominations.get([ - c.exchangeBaseUrl, - c.denomPubHash, - ]); - if (!denom) { - logger.warn("no denom found for coin"); - continue; - } - const cs = c.coinSource; - let refreshParentCoinPub: string | undefined; - if (cs.type == CoinSourceType.Refresh) { - refreshParentCoinPub = cs.oldCoinPub; - } - let withdrawalReservePub: string | undefined; - if (cs.type == CoinSourceType.Withdraw) { - withdrawalReservePub = cs.reservePub; - } - const denomInfo = await ws.getDenomInfo( - ws, - tx, - c.exchangeBaseUrl, - c.denomPubHash, - ); - if (!denomInfo) { - logger.warn("no denomination found for coin"); - continue; - } - coinsJson.coins.push({ - coin_pub: c.coinPub, - denom_pub: denomInfo.denomPub, - denom_pub_hash: c.denomPubHash, - denom_value: denom.value, - exchange_base_url: c.exchangeBaseUrl, - refresh_parent_coin_pub: refreshParentCoinPub, - withdrawal_reserve_pub: withdrawalReservePub, - coin_status: c.status, - ageCommitmentProof: c.ageCommitmentProof, - spend_allocation: c.spendAllocation - ? { - amount: c.spendAllocation.amount, - id: c.spendAllocation.id, - } - : undefined, - }); + await ws.db.runReadOnlyTx(["coins", "denominations"], async (tx) => { + const coins = await tx.coins.iter().toArray(); + for (const c of coins) { + const denom = await tx.denominations.get([ + c.exchangeBaseUrl, + c.denomPubHash, + ]); + if (!denom) { + logger.warn("no denom found for coin"); + continue; } - }); + const cs = c.coinSource; + let refreshParentCoinPub: string | undefined; + if (cs.type == CoinSourceType.Refresh) { + refreshParentCoinPub = cs.oldCoinPub; + } + let withdrawalReservePub: string | undefined; + if (cs.type == CoinSourceType.Withdraw) { + withdrawalReservePub = cs.reservePub; + } + const denomInfo = await ws.getDenomInfo( + ws, + tx, + c.exchangeBaseUrl, + c.denomPubHash, + ); + if (!denomInfo) { + logger.warn("no denomination found for coin"); + continue; + } + coinsJson.coins.push({ + coin_pub: c.coinPub, + denom_pub: denomInfo.denomPub, + denom_pub_hash: c.denomPubHash, + denom_value: denom.value, + exchange_base_url: c.exchangeBaseUrl, + refresh_parent_coin_pub: refreshParentCoinPub, + withdrawal_reserve_pub: withdrawalReservePub, + coin_status: c.status, + ageCommitmentProof: c.ageCommitmentProof, + spend_allocation: c.spendAllocation + ? { + amount: c.spendAllocation.amount, + id: c.spendAllocation.id, + } + : undefined, + }); + } + }); return coinsJson; } @@ -534,7 +520,7 @@ async function createStoredBackup( const backup = await exportDb(ws.idb); const backupsDb = await openStoredBackupsDatabase(ws.idb); const name = `backup-${new Date().getTime()}`; - await backupsDb.mktxAll().runReadWrite(async (tx) => { + await backupsDb.runAllStoresReadWriteTx(async (tx) => { await tx.backupMeta.add({ name, }); @@ -552,7 +538,7 @@ async function listStoredBackups( storedBackups: [], }; const backupsDb = await openStoredBackupsDatabase(ws.idb); - await backupsDb.mktxAll().runReadWrite(async (tx) => { + await backupsDb.runAllStoresReadWriteTx(async (tx) => { await tx.backupMeta.iter().forEach((x) => { storedBackups.storedBackups.push({ name: x.name, @@ -567,7 +553,7 @@ async function deleteStoredBackup( req: DeleteStoredBackupRequest, ): Promise { const backupsDb = await openStoredBackupsDatabase(ws.idb); - await backupsDb.mktxAll().runReadWrite(async (tx) => { + await backupsDb.runAllStoresReadWriteTx(async (tx) => { await tx.backupData.delete(req.name); await tx.backupMeta.delete(req.name); }); @@ -580,7 +566,7 @@ async function recoverStoredBackup( logger.info(`Recovering stored backup ${req.name}`); const { name } = req; const backupsDb = await openStoredBackupsDatabase(ws.idb); - const bd = await backupsDb.mktxAll().runReadWrite(async (tx) => { + const bd = await backupsDb.runAllStoresReadWriteTx(async (tx) => { const backupMeta = tx.backupMeta.get(name); if (!backupMeta) { throw Error("backup not found"); @@ -1526,7 +1512,20 @@ class InternalWalletStateImpl implements InternalWalletState { async getTransactionState( ws: InternalWalletState, - tx: GetReadOnlyAccess, + tx: WalletDbReadOnlyTransaction< + [ + "depositGroups", + "withdrawalGroups", + "purchases", + "refundGroups", + "peerPullCredit", + "peerPullDebit", + "peerPushDebit", + "peerPushCredit", + "rewards", + "refreshGroups", + ] + >, transactionId: string, ): Promise { const parsedTxId = parseTransactionIdentifier(transactionId); @@ -1613,9 +1612,7 @@ class InternalWalletStateImpl implements InternalWalletState { async getDenomInfo( ws: InternalWalletState, - tx: GetReadWriteAccess<{ - denominations: typeof WalletStoresV1.denominations; - }>, + tx: WalletDbReadWriteTransaction<["denominations"]>, exchangeBaseUrl: string, denomPubHash: string, ): Promise { -- cgit v1.2.3