taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 416c6e7d2a4760297c8de4a8afc6d06f569bc463
parent f022e722d4d155a2ec21b2eb96a9ee8b253e45df
Author: Florian Dold <florian@dold.me>
Date:   Wed, 18 Mar 2026 21:19:39 +0100

wallet-core: further simplify DB access

Diffstat:
Mpackages/taler-wallet-core/src/balance.ts | 43++++---------------------------------------
Mpackages/taler-wallet-core/src/coinSelection.ts | 97++++++++++++-------------------------------------------------------------------
Mpackages/taler-wallet-core/src/common.ts | 28+++++++---------------------
Mpackages/taler-wallet-core/src/contacts.ts | 4++--
Mpackages/taler-wallet-core/src/db.ts | 56+++++++++++++++++++-------------------------------------
Mpackages/taler-wallet-core/src/dbtx.ts | 6+++---
Mpackages/taler-wallet-core/src/deposits.ts | 24++++++++----------------
Mpackages/taler-wallet-core/src/donau.ts | 6+++---
Mpackages/taler-wallet-core/src/exchanges.ts | 107+++++++++++++++++--------------------------------------------------------------
Mpackages/taler-wallet-core/src/index.ts | 2+-
Mpackages/taler-wallet-core/src/observable-wrappers.ts | 113-------------------------------------------------------------------------------
Mpackages/taler-wallet-core/src/pay-merchant.ts | 64++++++++++++++++++----------------------------------------------
Mpackages/taler-wallet-core/src/pay-peer-common.ts | 6++----
Mpackages/taler-wallet-core/src/pay-peer-pull-credit.ts | 19+++++--------------
Mpackages/taler-wallet-core/src/pay-peer-pull-debit.ts | 17++++++-----------
Mpackages/taler-wallet-core/src/pay-peer-push-credit.ts | 19+++++--------------
Mpackages/taler-wallet-core/src/pay-peer-push-debit.ts | 11+++++------
Mpackages/taler-wallet-core/src/query.ts | 174-------------------------------------------------------------------------------
Mpackages/taler-wallet-core/src/recoup.ts | 35++++++-----------------------------
Mpackages/taler-wallet-core/src/refresh.ts | 67++++++++++++-------------------------------------------------------
Mpackages/taler-wallet-core/src/shepherd.ts | 24+++++-------------------
Mpackages/taler-wallet-core/src/tokenSelection.ts | 4++--
Mpackages/taler-wallet-core/src/transactions.ts | 9++++-----
Mpackages/taler-wallet-core/src/wallet.ts | 53++++++++++++++++++++++++-----------------------------
Mpackages/taler-wallet-core/src/withdraw.ts | 27++++++++++-----------------
25 files changed, 187 insertions(+), 828 deletions(-)

diff --git a/packages/taler-wallet-core/src/balance.ts b/packages/taler-wallet-core/src/balance.ts @@ -92,7 +92,7 @@ import { PurchaseStatus, RefreshGroupRecord, RefreshOperationStatus, - WalletDbReadOnlyTransaction, + WalletIndexedDbTransaction, WithdrawalGroupStatus, WithdrawalRecordType, } from "./db.js"; @@ -177,14 +177,7 @@ class BalancesStore { constructor( private wex: WalletExecutionContext, - private tx: WalletDbReadOnlyTransaction< - [ - "globalCurrencyAuditors", - "globalCurrencyExchanges", - "exchanges", - "exchangeDetails", - ] - >, + private tx: WalletIndexedDbTransaction, ) {} /** @@ -391,25 +384,7 @@ class BalancesStore { */ export async function getBalancesInsideTransaction( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction< - [ - "exchanges", - "exchangeDetails", - "coinAvailability", - "refreshGroups", - "depositGroups", - "withdrawalGroups", - "globalCurrencyAuditors", - "globalCurrencyExchanges", - "peerPushDebit", - "peerPushCredit", - "peerPullCredit", - "peerPullDebit", - "purchases", - "coins", - "donationSummaries", - ] - >, + tx: WalletIndexedDbTransaction, ): Promise<BalancesResponse> { const balanceStore: BalancesStore = new BalancesStore(wex, tx); @@ -897,17 +872,7 @@ export async function getPaymentBalanceDetails( export async function getPaymentBalanceDetailsInTx( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction< - [ - "coinAvailability", - "refreshGroups", - "exchanges", - "exchangeDetails", - "globalCurrencyAuditors", - "globalCurrencyExchanges", - "denominations", - ] - >, + tx: WalletIndexedDbTransaction, req: PaymentRestrictionsForBalance, ): Promise<PaymentBalanceDetails> { const d: PaymentBalanceDetails = { diff --git a/packages/taler-wallet-core/src/coinSelection.ts b/packages/taler-wallet-core/src/coinSelection.ts @@ -63,7 +63,10 @@ import { PaymentBalanceDetails, } from "./balance.js"; import { getAutoRefreshExecuteThreshold } from "./common.js"; -import { DenominationRecord, WalletDbReadOnlyTransaction } from "./db.js"; +import { + DenominationRecord, + WalletIndexedDbTransaction, +} from "./db.js"; import { checkExchangeInScopeTx, ExchangeDetails, @@ -190,18 +193,7 @@ export type SelectPayCoinsResult = async function internalSelectPayCoins( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction< - [ - "coinAvailability", - "denominations", - "refreshGroups", - "exchanges", - "exchangeDetails", - "coins", - "globalCurrencyAuditors", - "globalCurrencyExchanges", - ] - >, + tx: WalletIndexedDbTransaction, req: SelectPayCoinRequestNg, includePendingCoins: boolean, ): Promise< @@ -296,18 +288,7 @@ async function internalSelectPayCoins( export async function selectPayCoinsInTx( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction< - [ - "coinAvailability", - "denominations", - "refreshGroups", - "exchanges", - "exchangeDetails", - "coins", - "globalCurrencyAuditors", - "globalCurrencyExchanges", - ] - >, + tx: WalletIndexedDbTransaction, req: SelectPayCoinRequestNg, ): Promise<SelectPayCoinsResult> { if (logger.shouldLogTrace()) { @@ -413,7 +394,7 @@ export async function selectPayCoins( async function maybeRepairCoinSelection( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction<["coins", "denominations"]>, + tx: WalletIndexedDbTransaction, prevPayCoins: PreviousPayCoins, coinRes: SelectedCoin[], tally: CoinSelectionTally, @@ -461,7 +442,7 @@ async function maybeRepairCoinSelection( * as not enough coins are actually available. */ async function assembleSelectPayCoinsSuccessResult( - tx: WalletDbReadOnlyTransaction<["coins"]>, + tx: WalletIndexedDbTransaction, finalSel: SelResult, coinRes: SelectedCoin[], tally: CoinSelectionTally, @@ -557,17 +538,7 @@ function getHint( export async function reportInsufficientBalanceDetails( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction< - [ - "coinAvailability", - "exchanges", - "exchangeDetails", - "refreshGroups", - "denominations", - "globalCurrencyAuditors", - "globalCurrencyExchanges", - ] - >, + tx: WalletIndexedDbTransaction, req: ReportInsufficientBalanceRequest, ): Promise<PaymentInsufficientBalanceDetails> { const currency = Amounts.currencyOf(req.instructedAmount); @@ -1017,16 +988,7 @@ export interface PayCoinCandidates { async function selectPayCandidates( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction< - [ - "exchanges", - "coinAvailability", - "exchangeDetails", - "denominations", - "globalCurrencyAuditors", - "globalCurrencyExchanges", - ] - >, + tx: WalletIndexedDbTransaction, req: SelectPayCandidatesRequest, ): Promise<PayCoinCandidates> { // FIXME: Use the existing helper (from balance.ts) to @@ -1236,7 +1198,7 @@ export interface PeerCoinSelectionRequest { export async function computeCoinSelMaxExpirationDate( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction<["coins", "denominations"]>, + tx: WalletIndexedDbTransaction, selectedDenom: SelResult, ): Promise<TalerProtocolTimestamp> { let minAutorefreshExecuteThreshold = TalerProtocolTimestamp.never(); @@ -1304,19 +1266,7 @@ function getGlobalFees( async function internalSelectPeerCoins( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction< - [ - "exchanges", - "contractTerms", - "coins", - "coinAvailability", - "denominations", - "refreshGroups", - "exchangeDetails", - "globalCurrencyAuditors", - "globalCurrencyExchanges", - ] - >, + tx: WalletIndexedDbTransaction, req: PeerCoinSelectionRequest, exch: ExchangeDetails, includePendingCoins: boolean, @@ -1375,19 +1325,7 @@ async function internalSelectPeerCoins( export async function selectPeerCoinsInTx( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction< - [ - "exchanges", - "contractTerms", - "coins", - "coinAvailability", - "denominations", - "refreshGroups", - "exchangeDetails", - "globalCurrencyExchanges", - "globalCurrencyAuditors", - ] - >, + tx: WalletIndexedDbTransaction, req: PeerCoinSelectionRequest, ): Promise<SelectPeerCoinsResult> { const instructedAmount = req.instructedAmount; @@ -1560,14 +1498,7 @@ function getMaxDepositAmountForAvailableCoins( export async function getExchangesForDepositInTx( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction< - [ - "exchanges", - "exchangeDetails", - "globalCurrencyExchanges", - "globalCurrencyAuditors", - ] - >, + tx: WalletIndexedDbTransaction, req: { restrictScope?: ScopeInfo; currency: string }, ): Promise<Exchange[]> { logger.trace(`getting exchanges for deposit ${j2s(req)}`); diff --git a/packages/taler-wallet-core/src/common.ts b/packages/taler-wallet-core/src/common.ts @@ -63,8 +63,7 @@ import { PurchaseRecord, RecoupGroupRecord, RefreshGroupRecord, - WalletDbAllStoresReadOnlyTransaction, - WalletDbReadWriteTransaction, + WalletIndexedDbTransaction, WithdrawalGroupRecord, timestampPreciseToDb, } from "./db.js"; @@ -92,7 +91,7 @@ export interface TokensSpendInfo { export async function makeCoinsVisible( wex: WalletExecutionContext, - tx: WalletDbReadWriteTransaction<["coins", "coinAvailability"]>, + tx: WalletIndexedDbTransaction, transactionId: string, ): Promise<void> { const coins = @@ -120,9 +119,7 @@ export async function makeCoinsVisible( export async function makeCoinAvailable( wex: WalletExecutionContext, - tx: WalletDbReadWriteTransaction< - ["coins", "coinAvailability", "denominations"] - >, + tx: WalletIndexedDbTransaction, coinRecord: CoinRecord, ): Promise<void> { checkLogicInvariant(coinRecord.status === CoinStatus.Fresh); @@ -167,18 +164,7 @@ export async function makeCoinAvailable( */ export async function spendCoins( wex: WalletExecutionContext, - tx: WalletDbReadWriteTransaction< - [ - "coins", - "coinHistory", - "coinAvailability", - "refreshGroups", - "refreshSessions", - "denominations", - "denominationFamilies", - "transactionsMeta", - ] - >, + tx: WalletIndexedDbTransaction, csi: CoinsSpendInfo, ): Promise<void> { if (csi.coinPubs.length != csi.contributions.length) { @@ -265,7 +251,7 @@ export async function spendCoins( } export async function spendTokens( - tx: WalletDbReadWriteTransaction<["tokens", "purchases"]>, + tx: WalletIndexedDbTransaction, tsi: TokensSpendInfo, ): Promise<void> { if (tsi.tokenPubs.length === 0) { @@ -836,7 +822,7 @@ export interface TransactionContext { failTransaction(reason?: TalerErrorDetail): Promise<void>; deleteTransaction(): Promise<void>; lookupFullTransaction( - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, args?: LookupFullTransactionOpts, ): Promise<Transaction | undefined>; } @@ -1065,7 +1051,7 @@ export interface RecordHandle<T> { */ export async function getGenericRecordHandle<T>( ctx: TransactionContext, - tx: WalletDbReadWriteTransaction<any>, + tx: WalletIndexedDbTransaction, getRec: () => Promise<T | undefined>, storeRec: (r: T) => Promise<void>, deleteRec: () => Promise<void>, diff --git a/packages/taler-wallet-core/src/contacts.ts b/packages/taler-wallet-core/src/contacts.ts @@ -30,14 +30,14 @@ import { Logger, NotificationType, } from "@gnu-taler/taler-util"; -import { ContactRecord, WalletDbReadOnlyTransaction } from "./db.js"; +import { ContactRecord, WalletIndexedDbTransaction } from "./db.js"; import { WalletExecutionContext } from "./wallet.js"; const logger = new Logger("contacts.ts"); async function makeContactListItem( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction<["contacts"]>, + tx: WalletIndexedDbTransaction, r: ContactRecord, //lastError: TalerErrorDetail | undefined, ): Promise<ContactEntry> { diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts @@ -87,7 +87,6 @@ import { DbRetryInfo, TaskIdentifiers } from "./common.js"; import { DbAccess, DbAccessImpl, - DbReadOnlyTransaction, DbReadWriteTransaction, IndexDescriptor, StoreDescriptor, @@ -2921,7 +2920,7 @@ export interface ContactRecord { * Schema definition for the IndexedDB * wallet database. */ -export const WalletStoresV1 = { +export const WalletIndexedDbStoresV1 = { exchangeBaseUrlMigrationLog: describeStoreV2({ recordCodec: passthroughCodec<ExchangeMigrationLogRecord>(), storeName: "exchangeBaseUrlMigrationLog", @@ -3576,23 +3575,9 @@ export const WalletStoresV1 = { ), }; -export type WalletDbStoresName = StoreNames<typeof WalletStoresV1>; -export type WalletDbStoresArr = Array<WalletDbStoresName>; - -export type WalletDbReadWriteTransaction<StoresArr extends WalletDbStoresArr> = - DbReadWriteTransaction<typeof WalletStoresV1, StoresArr>; - -export type WalletDbReadOnlyTransaction<StoresArr extends WalletDbStoresArr> = - DbReadOnlyTransaction<typeof WalletStoresV1, StoresArr>; - -export type WalletDbAllStoresReadOnlyTransaction = DbReadOnlyTransaction< - typeof WalletStoresV1, - WalletDbStoresArr ->; - -export type WalletDbAllStoresReadWriteTransaction = DbReadWriteTransaction< - typeof WalletStoresV1, - WalletDbStoresArr +export type WalletIndexedDbTransaction = DbReadWriteTransaction< + typeof WalletIndexedDbStoresV1, + Array<StoreNames<typeof WalletIndexedDbStoresV1>> >; /** @@ -3822,7 +3807,7 @@ export async function importDb(db: IDBDatabase, dumpJson: any): Promise<void> { export interface FixupDescription { name: string; - fn(tx: WalletDbReadOnlyTransaction<WalletDbStoresArr>): Promise<void>; + fn(tx: WalletIndexedDbTransaction): Promise<void>; } /** @@ -3866,7 +3851,7 @@ export const walletDbFixups: FixupDescription[] = [ ]; async function fixup20260213RefreshBlunder( - tx: WalletDbAllStoresReadWriteTransaction, + tx: WalletIndexedDbTransaction, ): Promise<void> { const refreshes = await tx.refreshGroups.indexes.byStatus.getAll( RefreshOperationStatus.Failed, @@ -3903,7 +3888,7 @@ async function fixup20260213RefreshBlunder( } async function fixup20260203DenomFamilyMigration( - tx: WalletDbAllStoresReadWriteTransaction, + tx: WalletIndexedDbTransaction, ): Promise<void> { const batchSize = 500; @@ -3978,7 +3963,7 @@ async function fixup20260203DenomFamilyMigration( } async function fixup20260116BadRefreshCoinSelection( - tx: WalletDbAllStoresReadWriteTransaction, + tx: WalletIndexedDbTransaction, ): Promise<void> { await tx.refreshGroups.iter().forEachAsync(async (rec) => { const inputAmount = Amounts.sumOrZero( @@ -4008,7 +3993,7 @@ async function fixup20260116BadRefreshCoinSelection( * based on the coin selection. */ async function fixup20250915TransactionsScope( - tx: WalletDbAllStoresReadWriteTransaction, + tx: WalletIndexedDbTransaction, ): Promise<void> { await tx.purchases.iter().forEachAsync(async (rec) => { if ( @@ -4045,7 +4030,7 @@ async function fixup20250915TransactionsScope( } async function fixupCoinAvailabilityExchangePub( - tx: WalletDbAllStoresReadWriteTransaction, + tx: WalletIndexedDbTransaction, ): Promise<void> { const cars = await tx.coinAvailability.getAll(); const exchanges: Record<string, ExchangeDetailsRecord | undefined> = {}; @@ -4068,7 +4053,7 @@ async function fixupCoinAvailabilityExchangePub( } export async function applyFixups( - db: DbAccess<typeof WalletStoresV1>, + db: DbAccess<typeof WalletIndexedDbStoresV1>, ): Promise<number> { logger.trace("applying fixups"); let count = 0; @@ -4233,7 +4218,7 @@ function onTalerDbUpgradeNeeded( upgradeTransaction: IDBTransaction, ) { upgradeFromStoreMap( - WalletStoresV1, + WalletIndexedDbStoresV1, db, oldVersion, newVersion, @@ -4317,7 +4302,7 @@ export async function openTalerDatabase( CancellationToken.CONTINUE, ); let currentMainVersion: string | undefined; - await metaDb.runReadWriteTx({ storeNames: ["metaConfig"] }, async (tx) => { + await metaDb.runAllStoresReadWriteTx({}, async (tx) => { const dbVersionRecord = await tx.metaConfig.get(CURRENT_DB_CONFIG_KEY); if (!dbVersionRecord) { currentMainVersion = TALER_WALLET_MAIN_DB_NAME; @@ -4342,15 +4327,12 @@ export async function openTalerDatabase( case "taler-wallet-main-v9": // We consider this a pre-release // development version, no migration is done. - await metaDb.runReadWriteTx( - { storeNames: ["metaConfig"] }, - async (tx) => { - await tx.metaConfig.put({ - key: CURRENT_DB_CONFIG_KEY, - value: TALER_WALLET_MAIN_DB_NAME, - }); - }, - ); + await metaDb.runAllStoresReadWriteTx({}, async (tx) => { + await tx.metaConfig.put({ + key: CURRENT_DB_CONFIG_KEY, + value: TALER_WALLET_MAIN_DB_NAME, + }); + }); break; default: throw Error( diff --git a/packages/taler-wallet-core/src/dbtx.ts b/packages/taler-wallet-core/src/dbtx.ts @@ -19,7 +19,7 @@ import { ScopeInfo, stringifyScopeInfo, } from "@gnu-taler/taler-util"; -import { ConfigRecord, WalletDbAllStoresReadWriteTransaction } from "./db.js"; +import { ConfigRecord, WalletIndexedDbTransaction } from "./db.js"; export interface GetCurrencyInfoDbResult { /** @@ -61,8 +61,8 @@ export interface WalletDbTransaction { } export class IdbWalletTransaction implements WalletDbTransaction { - tx: WalletDbAllStoresReadWriteTransaction; - constructor(tx: WalletDbAllStoresReadWriteTransaction) { + tx: WalletIndexedDbTransaction; + constructor(tx: WalletIndexedDbTransaction) { this.tx = tx; } async getCurrencyInfo( diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts @@ -111,9 +111,7 @@ import { DepositOperationStatus, DepositTrackingInfo, RefreshOperationStatus, - WalletDbAllStoresReadOnlyTransaction, - WalletDbAllStoresReadWriteTransaction, - WalletDbReadWriteTransaction, + WalletIndexedDbTransaction, WithdrawalGroupRecord, WithdrawalGroupStatus, WithdrawalRecordType, @@ -187,7 +185,7 @@ export class DepositTransactionContext implements TransactionContext { * transaction item (e.g. if it was deleted). */ async lookupFullTransaction( - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, ): Promise<Transaction | undefined> { const dg = await tx.depositGroups.get(this.depositGroupId); if (!dg) { @@ -319,9 +317,7 @@ export class DepositTransactionContext implements TransactionContext { /** * Update the metadata of the transaction in the database. */ - async updateTransactionMeta( - tx: WalletDbReadWriteTransaction<["depositGroups", "transactionsMeta"]>, - ): Promise<void> { + async updateTransactionMeta(tx: WalletIndexedDbTransaction): Promise<void> { const depositRec = await tx.depositGroups.get(this.depositGroupId); if (!depositRec) { await tx.transactionsMeta.delete(this.transactionId); @@ -337,7 +333,7 @@ export class DepositTransactionContext implements TransactionContext { } async getRecordHandle( - tx: WalletDbReadWriteTransaction<["depositGroups", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise< [DepositGroupRecord | undefined, RecordHandle<DepositGroupRecord>] > { @@ -361,11 +357,7 @@ export class DepositTransactionContext implements TransactionContext { }); } - async deleteTransactionInTx( - tx: WalletDbReadWriteTransaction< - ["depositGroups", "tombstones", "transactionsMeta"] - >, - ): Promise<void> { + async deleteTransactionInTx(tx: WalletIndexedDbTransaction): Promise<void> { const [rec, h] = await this.getRecordHandle(tx); if (!rec) { return; @@ -1157,7 +1149,7 @@ async function processDepositGroupPendingKyc( } async function tryFindAccountKeypair( - tx: WalletDbAllStoresReadWriteTransaction, + tx: WalletIndexedDbTransaction, exchangeBaseUrl: string, ): Promise<EddsaKeyPairStrings | undefined> { let candidateTimestamp: AbsoluteTime | undefined = undefined; @@ -1239,7 +1231,7 @@ async function provideDepositAccountKeypair( wex: WalletExecutionContext, exchangeBaseUrl: string, ): Promise<EddsaKeyPairStrings> { - const existingPair = await wex.runLegacyWalletDbTx(async (tx, wtx) => { + const existingPair = await wex.runLegacyWalletDbTx(async (tx) => { const exchange = await tx.exchanges.get(exchangeBaseUrl); if (!exchange) { throw Error("exchange for deposit not found anymore"); @@ -1262,7 +1254,7 @@ async function provideDepositAccountKeypair( return existingPair; } const newPair = await wex.cryptoApi.createEddsaKeypair({}); - await wex.runLegacyWalletDbTx(async (tx, wtx) => { + await wex.runLegacyWalletDbTx(async (tx) => { const exchange = await tx.exchanges.get(exchangeBaseUrl); if (!exchange) { throw Error("exchange for deposit not found anymore"); diff --git a/packages/taler-wallet-core/src/donau.ts b/packages/taler-wallet-core/src/donau.ts @@ -283,8 +283,8 @@ export async function handleSetDonau( idHasher.update(stringToBytes(req.taxPayerId + "\0")); idHasher.update(stringToBytes(encodeCrock(salt) + "\0")); const saltedId = idHasher.finish(); - await wex.runLegacyWalletDbTx(async (tx, wtx) => { - const oldRec = await wtx.getConfig(ConfigRecordKey.DonauConfig); + await wex.runLegacyWalletDbTx(async (tx) => { + const oldRec = await tx.wtx.getConfig(ConfigRecordKey.DonauConfig); if ( oldRec && oldRec.value.donauBaseUrl === req.donauBaseUrl && @@ -314,7 +314,7 @@ export async function handleGetDonau( wex: WalletExecutionContext, req: EmptyObject, ): Promise<GetDonauResponse> { - const currentDonauInfo = await wex.runLegacyWalletDbTx(async (tx, wtx) => { + const currentDonauInfo = await wex.runWalletDbTx(async (wtx) => { const res = await wtx.getConfig(ConfigRecordKey.DonauConfig); if (!res) { return undefined; diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts @@ -149,10 +149,7 @@ import { ExchangeMigrationReason, ReserveRecord, ReserveRecordStatus, - WalletDbAllStoresReadOnlyTransaction, - WalletDbAllStoresReadWriteTransaction, - WalletDbReadOnlyTransaction, - WalletDbReadWriteTransaction, + WalletIndexedDbTransaction, timestampAbsoluteFromDb, timestampOptionalPreciseFromDb, timestampPreciseFromDb, @@ -160,7 +157,6 @@ import { timestampProtocolFromDb, timestampProtocolToDb, } from "./db.js"; -import { IdbWalletTransaction } from "./dbtx.js"; import { createTimeline, isCandidateWithdrawableDenomRec, @@ -273,7 +269,7 @@ async function downloadExchangeWithTermsOfService( * Get exchange details from the database. */ async function getExchangeRecordsInternal( - tx: WalletDbReadOnlyTransaction<["exchanges", "exchangeDetails"]>, + tx: WalletIndexedDbTransaction, exchangeBaseUrl: string, ): Promise<ExchangeDetailsRecord | undefined> { const r = await tx.exchanges.get(exchangeBaseUrl); @@ -318,15 +314,7 @@ async function getExchangeRecordsInternal( } export async function getScopeForAllCoins( - tx: WalletDbReadOnlyTransaction< - [ - "exchanges", - "coins", - "exchangeDetails", - "globalCurrencyExchanges", - "globalCurrencyAuditors", - ] - >, + tx: WalletIndexedDbTransaction, coinPubs: string[], ): Promise<ScopeInfo[]> { let exchangeSet = new Set<string>(); @@ -345,14 +333,7 @@ export async function getScopeForAllCoins( * Get a list of scope infos applicable to a list of exchanges. */ export async function getScopeForAllExchanges( - tx: WalletDbReadOnlyTransaction< - [ - "exchanges", - "exchangeDetails", - "globalCurrencyExchanges", - "globalCurrencyAuditors", - ] - >, + tx: WalletIndexedDbTransaction, exs: string[], ): Promise<ScopeInfo[]> { const scopes: ScopeInfo[] = []; @@ -372,14 +353,7 @@ export async function getScopeForAllExchanges( } export async function getExchangeScopeInfoOrUndefined( - tx: WalletDbReadOnlyTransaction< - [ - "exchanges", - "exchangeDetails", - "globalCurrencyExchanges", - "globalCurrencyAuditors", - ] - >, + tx: WalletIndexedDbTransaction, exchangeBaseUrl: string, ): Promise<ScopeInfo | undefined> { const det = await getExchangeRecordsInternal(tx, exchangeBaseUrl); @@ -390,14 +364,7 @@ export async function getExchangeScopeInfoOrUndefined( } export async function getExchangeScopeInfo( - tx: WalletDbReadOnlyTransaction< - [ - "exchanges", - "exchangeDetails", - "globalCurrencyExchanges", - "globalCurrencyAuditors", - ] - >, + tx: WalletIndexedDbTransaction, exchangeBaseUrl: string, currency: string, ): Promise<ScopeInfo> { @@ -413,9 +380,7 @@ export async function getExchangeScopeInfo( } async function internalGetExchangeScopeInfo( - tx: WalletDbReadOnlyTransaction< - ["globalCurrencyExchanges", "globalCurrencyAuditors"] - >, + tx: WalletIndexedDbTransaction, exchangeDetails: ExchangeDetailsRecord, ): Promise<ScopeInfo> { const globalExchangeRec = @@ -472,9 +437,7 @@ function getKycStatusFromReserveStatus( async function makeExchangeListItem( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction< - ["globalCurrencyExchanges", "globalCurrencyAuditors"] - >, + tx: WalletIndexedDbTransaction, r: ExchangeEntryRecord, exchangeDetails: ExchangeDetailsRecord | undefined, reserveRec: ReserveRecord | undefined, @@ -556,7 +519,7 @@ export interface ExchangeDetails { } export async function getExchangeDetailsInTx( - tx: WalletDbReadOnlyTransaction<["exchanges", "exchangeDetails"]>, + tx: WalletIndexedDbTransaction, exchangeBaseUrl: string, ): Promise<ExchangeDetails | undefined> { const det = await getExchangeRecordsInternal(tx, exchangeBaseUrl); @@ -795,7 +758,7 @@ async function validateGlobalFees( * if the DB transaction succeeds. */ export async function addPresetExchangeEntry( - tx: WalletDbReadWriteTransaction<["exchanges"]>, + tx: WalletIndexedDbTransaction, exchangeBaseUrl: string, currencyHint?: string, ): Promise<{ notification?: WalletNotification }> { @@ -835,7 +798,7 @@ export async function addPresetExchangeEntry( async function provideExchangeRecordInTx( ws: InternalWalletState, - tx: WalletDbReadWriteTransaction<["exchanges", "exchangeDetails"]>, + tx: WalletIndexedDbTransaction, baseUrl: string, ): Promise<{ exchange: ExchangeEntryRecord; @@ -1062,7 +1025,7 @@ async function downloadTosFromAcceptedFormat( */ async function checkExchangeEntryOutdated( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction<["exchanges", "denominations"]>, + tx: WalletIndexedDbTransaction, exchangeBaseUrl: string, ): Promise<boolean> { // We currently consider the exchange outdated when no @@ -1817,7 +1780,7 @@ export async function updateExchangeFromUrlHandler( } } - const updated = await wex.runLegacyWalletDbTx(async (tx, wtx) => { + const updated = await wex.runLegacyWalletDbTx(async (tx) => { const r = await tx.exchanges.get(exchangeBaseUrl); if (!r) { logger.warn(`exchange ${exchangeBaseUrl} no longer present`); @@ -1930,7 +1893,7 @@ export async function updateExchangeFromUrlHandler( if (keysInfo.currency_specification) { // Since this is the per-exchange currency info, // we update it when the exchange changes it. - await wtx.upsertCurrencyInfo({ + await tx.wtx.upsertCurrencyInfo({ currencySpec: keysInfo.currency_specification, scopeInfo: { type: ScopeType.Exchange, @@ -2272,15 +2235,7 @@ interface DenomLossResult { async function handleDenomLoss( wex: WalletExecutionContext, - tx: WalletDbReadWriteTransaction< - [ - "coinAvailability", - "denominations", - "denomLossEvents", - "coins", - "transactionsMeta", - ] - >, + tx: WalletIndexedDbTransaction, currency: string, exchangeBaseUrl: string, ): Promise<DenomLossResult> { @@ -2491,9 +2446,7 @@ export class DenomLossTransactionContext implements TransactionContext { return undefined; } - async updateTransactionMeta( - tx: WalletDbReadWriteTransaction<["denomLossEvents", "transactionsMeta"]>, - ): Promise<void> { + async updateTransactionMeta(tx: WalletIndexedDbTransaction): Promise<void> { const denomLossRec = await tx.denomLossEvents.get(this.denomLossEventId); if (!denomLossRec) { await tx.transactionsMeta.delete(this.transactionId); @@ -2545,7 +2498,7 @@ export class DenomLossTransactionContext implements TransactionContext { } async lookupFullTransaction( - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, ): Promise<Transaction | undefined> { const rec = await tx.denomLossEvents.get(this.denomLossEventId); if (!rec) { @@ -2573,16 +2526,7 @@ export class DenomLossTransactionContext implements TransactionContext { async function handleRecoup( wex: WalletExecutionContext, - tx: WalletDbReadWriteTransaction< - [ - "denominations", - "coins", - "recoupGroups", - "refreshGroups", - "transactionsMeta", - "exchanges", - ] - >, + tx: WalletIndexedDbTransaction, exchangeBaseUrl: string, recoup: Recoup[], ): Promise<void> { @@ -2828,7 +2772,7 @@ export async function listExchanges( * succeeded. */ export async function markExchangeUsed( - tx: WalletDbReadWriteTransaction<["exchanges"]>, + tx: WalletIndexedDbTransaction, exchangeBaseUrl: string, ): Promise<void> { logger.trace(`marking exchange ${exchangeBaseUrl} as used`); @@ -3013,7 +2957,7 @@ export async function getExchangeDetailedInfo( } async function internalGetExchangeResources( - tx: WalletDbReadOnlyTransaction<["exchanges", "coins", "withdrawalGroups"]>, + tx: WalletIndexedDbTransaction, exchangeBaseUrl: string, ): Promise<GetExchangeResourcesResponse> { let numWithdrawals = 0; @@ -3035,7 +2979,7 @@ async function internalGetExchangeResources( */ async function purgeExchange( wex: WalletExecutionContext, - tx: WalletDbAllStoresReadWriteTransaction, + tx: WalletIndexedDbTransaction, exchangeRec: ExchangeEntryRecord, purgeTransactions?: boolean, ): Promise<void> { @@ -3950,14 +3894,7 @@ export async function processExchangeKyc( } export async function checkExchangeInScopeTx( - tx: WalletDbReadOnlyTransaction< - [ - "globalCurrencyExchanges", - "globalCurrencyAuditors", - "exchanges", - "exchangeDetails", - ] - >, + tx: WalletIndexedDbTransaction, exchangeBaseUrl: string, scope: ScopeInfo, ): Promise<boolean> { diff --git a/packages/taler-wallet-core/src/index.ts b/packages/taler-wallet-core/src/index.ts @@ -42,7 +42,7 @@ export { deleteTalerDatabase, exportDb, importDb, - WalletStoresV1, + WalletIndexedDbStoresV1 as WalletStoresV1, WithdrawalGroupStatus, } from "./db.js"; export { DbAccess } from "./query.js"; diff --git a/packages/taler-wallet-core/src/observable-wrappers.ts b/packages/taler-wallet-core/src/observable-wrappers.ts @@ -33,7 +33,6 @@ import { TaskIdStr } from "./common.js"; import { TalerCryptoInterface } from "./index.js"; import { DbAccess, - DbReadOnlyTransaction, DbReadWriteTransaction, StoreMap, StoreNames, @@ -177,118 +176,6 @@ export class ObservableDbAccess<Stores extends StoreMap> throw e; } } - - async runAllStoresReadOnlyTx<T>( - options: { - label?: string; - }, - txf: ( - tx: DbReadOnlyTransaction<Stores, StoreNames<Stores>[]>, - ) => Promise<T>, - ): Promise<T> { - const location = getCallerInfo(); - this.oc.observe({ - type: ObservabilityEventType.DbQueryStart, - name: options.label ?? "<unknown>", - location, - }); - const start = performanceNow(); - try { - const ret = await this.impl.runAllStoresReadOnlyTx(options, txf); - const end = performanceNow(); - this.oc.observe({ - type: ObservabilityEventType.DbQueryFinishSuccess, - name: options.label ?? "<unknown>", - location, - durationMs: performanceDelta(start, end), - }); - return ret; - } catch (e) { - const end = performanceNow(); - this.oc.observe({ - type: ObservabilityEventType.DbQueryFinishError, - name: options.label ?? "<unknown>", - location, - error: getErrorDetailFromException(e), - durationMs: performanceDelta(start, end), - }); - throw e; - } - } - - async runReadWriteTx<T, StoreNameArray extends StoreNames<Stores>[]>( - opts: { - storeNames: StoreNameArray; - label?: string; - }, - txf: (tx: DbReadWriteTransaction<Stores, StoreNameArray>) => Promise<T>, - ): Promise<T> { - const location = getCallerInfo(); - this.oc.observe({ - type: ObservabilityEventType.DbQueryStart, - name: opts.label ?? "<unknown>", - location, - }); - const start = performanceNow(); - try { - const ret = await this.impl.runReadWriteTx(opts, txf); - const end = performanceNow(); - this.oc.observe({ - type: ObservabilityEventType.DbQueryFinishSuccess, - name: opts.label ?? "<unknown>", - location, - durationMs: performanceDelta(start, end), - }); - return ret; - } catch (e) { - const end = performanceNow(); - this.oc.observe({ - type: ObservabilityEventType.DbQueryFinishError, - name: opts.label ?? "<unknown>", - location, - error: getErrorDetailFromException(e), - durationMs: performanceDelta(start, end), - }); - throw e; - } - } - - async runReadOnlyTx<T, StoreNameArray extends StoreNames<Stores>[]>( - opts: { - storeNames: StoreNameArray; - label?: string; - }, - txf: (tx: DbReadOnlyTransaction<Stores, StoreNameArray>) => Promise<T>, - ): Promise<T> { - const location = getCallerInfo(); - const start = performanceNow(); - try { - this.oc.observe({ - type: ObservabilityEventType.DbQueryStart, - name: opts.label ?? "<unknown>", - location, - }); - const ret = await this.impl.runReadOnlyTx(opts, txf); - const end = performanceNow(); - this.oc.observe({ - type: ObservabilityEventType.DbQueryFinishSuccess, - name: opts.label ?? "<unknown>", - location, - durationMs: performanceDelta(start, end), - }); - return ret; - } catch (e) { - const end = performanceNow(); - this.oc.observe({ - type: ObservabilityEventType.DbQueryFinishError, - name: opts.label ?? "<unknown>", - location, - error: getErrorDetailFromException(e), - durationMs: performanceDelta(start, end), - }); - throw e; - } - } } export function observeTalerCrypto( diff --git a/packages/taler-wallet-core/src/pay-merchant.ts b/packages/taler-wallet-core/src/pay-merchant.ts @@ -168,10 +168,7 @@ import { timestampProtocolFromDb, timestampProtocolToDb, TokenRecord, - WalletDbAllStoresReadOnlyTransaction, - WalletDbAllStoresReadWriteTransaction, - WalletDbReadOnlyTransaction, - WalletDbReadWriteTransaction, + WalletIndexedDbTransaction, } from "./db.js"; import { acceptDonauBlindSigs, generateDonauPlanchets } from "./donau.js"; import { getScopeForAllCoins, getScopeForAllExchanges } from "./exchanges.js"; @@ -231,9 +228,7 @@ export class PayMerchantTransactionContext implements TransactionContext { * * Must be called each time the DB record for the transaction is updated. */ - async updateTransactionMeta( - tx: WalletDbReadWriteTransaction<["purchases", "transactionsMeta"]>, - ): Promise<void> { + async updateTransactionMeta(tx: WalletIndexedDbTransaction): Promise<void> { const purchaseRec = await tx.purchases.get(this.proposalId); if (!purchaseRec) { await tx.transactionsMeta.delete(this.transactionId); @@ -255,7 +250,7 @@ export class PayMerchantTransactionContext implements TransactionContext { } async lookupFullTransaction( - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, req?: LookupFullTransactionOpts, ): Promise<Transaction | undefined> { const proposalId = this.proposalId; @@ -385,9 +380,7 @@ export class PayMerchantTransactionContext implements TransactionContext { } async deleteTransactionInTx( - tx: WalletDbReadWriteTransaction< - ["purchases", "tombstones", "transactionsMeta"] - >, + tx: WalletIndexedDbTransaction, opts: { keepRelated?: boolean } = {}, ): Promise<void> { const [rec, h] = await this.getRecordHandle(tx); @@ -521,7 +514,7 @@ export class PayMerchantTransactionContext implements TransactionContext { } async getRecordHandle( - tx: WalletDbReadWriteTransaction<["purchases", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise<[PurchaseRecord | undefined, RecordHandle<PurchaseRecord>]> { return getGenericRecordHandle<PurchaseRecord>( this, @@ -539,7 +532,7 @@ export class PayMerchantTransactionContext implements TransactionContext { } async function computePayMerchantExchangesInTx( - tx: WalletDbReadOnlyTransaction<["purchases", "contractTerms"]>, + tx: WalletIndexedDbTransaction, purchaseRecord: PurchaseRecord, ): Promise<string[]> { if (purchaseRecord?.exchanges) { @@ -559,7 +552,7 @@ async function computePayMerchantExchangesInTx( } async function computePayMerchantTransactionScopesInTx( - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, purchaseRec: PurchaseRecord, ): Promise<ScopeInfo[]> { if (purchaseRec.exchanges && purchaseRec.exchanges.length > 0) { @@ -610,17 +603,7 @@ export class RefundTransactionContext implements TransactionContext { * * Must be called each time the DB record for the transaction is updated. */ - async updateTransactionMeta( - tx: WalletDbReadWriteTransaction< - [ - "refundGroups", - "purchases", - "transactionsMeta", - "contractTerms", - "contractTerms", - ] - >, - ): Promise<void> { + async updateTransactionMeta(tx: WalletIndexedDbTransaction): Promise<void> { const refundRec = await tx.refundGroups.get(this.refundGroupId); if (!refundRec) { await tx.transactionsMeta.delete(this.transactionId); @@ -641,7 +624,7 @@ export class RefundTransactionContext implements TransactionContext { } async lookupFullTransaction( - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, ): Promise<Transaction | undefined> { const refundRecord = await tx.refundGroups.get(this.refundGroupId); if (!refundRecord) { @@ -714,16 +697,7 @@ export class RefundTransactionContext implements TransactionContext { } async deleteTransactionInTx( - tx: WalletDbReadWriteTransaction< - [ - "purchases", - "refundGroups", - "refundItems", - "tombstones", - "transactionsMeta", - "contractTerms", - ] - >, + tx: WalletIndexedDbTransaction, ): Promise<{ notifs: WalletNotification[] }> { const notifs: WalletNotification[] = []; const refundRecord = await tx.refundGroups.get(this.refundGroupId); @@ -762,7 +736,7 @@ export class RefundTransactionContext implements TransactionContext { } async function lookupMaybeContractData( - tx: WalletDbReadOnlyTransaction<["purchases", "contractTerms"]>, + tx: WalletIndexedDbTransaction, proposalId: string, ): Promise<DownloadedContractData | undefined> { let contractData: DownloadedContractData | undefined = undefined; @@ -806,9 +780,7 @@ export async function getTotalPaymentCost( export async function getTotalPaymentCostInTx( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction< - ["coins", "denominations", "denominationFamilies"] - >, + tx: WalletIndexedDbTransaction, currency: string, pcs: SelectedProspectiveCoin[], ): Promise<AmountJson> { @@ -863,7 +835,7 @@ function getPayRequestTimeout(purchase: PurchaseRecord): Duration { export async function expectProposalDownloadByIdInTx( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction<["contractTerms", "purchases"]>, + tx: WalletIndexedDbTransaction, proposalId: string, ): Promise<DownloadedContractData> { const rec = await tx.purchases.get(proposalId); @@ -875,7 +847,7 @@ export async function expectProposalDownloadByIdInTx( export async function expectProposalDownloadInTx( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction<["contractTerms"]>, + tx: WalletIndexedDbTransaction, p: PurchaseRecord, ): Promise<DownloadedContractData> { if (!p.download) { @@ -1537,7 +1509,7 @@ function setCoinSel(rec: PurchaseRecord, coinSel: PayCoinSelection): void { } async function reselectCoinsTx( - tx: WalletDbAllStoresReadWriteTransaction, + tx: WalletIndexedDbTransaction, ctx: PayMerchantTransactionContext, ): Promise<void> { const p = await tx.purchases.get(ctx.proposalId); @@ -2778,7 +2750,7 @@ export async function confirmPay( `recording payment on ${proposal.orderId} with session ID ${sessionId}`, ); - await wex.runLegacyWalletDbTx(async (tx, wtx) => { + await wex.runLegacyWalletDbTx(async (tx) => { const [p, h] = await ctx.getRecordHandle(tx); if (!p) { return; @@ -2854,7 +2826,7 @@ export async function confirmPay( p.download.currency = Amounts.currencyOf(amount); } - const confRes = await wtx.getConfig(ConfigRecordKey.DonauConfig); + const confRes = await tx.wtx.getConfig(ConfigRecordKey.DonauConfig); logger.info( `donau conf: ${j2s(confRes)}, useDonau: ${ @@ -4501,7 +4473,7 @@ export async function startQueryRefund( async function computeRefreshRequest( wex: WalletExecutionContext, - tx: WalletDbReadWriteTransaction<["coins", "denominations"]>, + tx: WalletIndexedDbTransaction, items: RefundItemRecord[], ): Promise<CoinRefreshRequest[]> { const refreshCoins: CoinRefreshRequest[] = []; diff --git a/packages/taler-wallet-core/src/pay-peer-common.ts b/packages/taler-wallet-core/src/pay-peer-common.ts @@ -27,7 +27,7 @@ import { SpendCoinDetails } from "./crypto/cryptoImplementation.js"; import { DbPeerPushPaymentCoinSelection, ReserveRecord, - WalletDbReadOnlyTransaction, + WalletIndexedDbTransaction, } from "./db.js"; import { getTotalRefreshCost } from "./refresh.js"; import { WalletExecutionContext, getDenomInfo } from "./wallet.js"; @@ -72,9 +72,7 @@ export async function queryCoinInfosForSelection( export async function getTotalPeerPaymentCostInTx( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction< - ["coins", "denominations", "denominationFamilies"] - >, + tx: WalletIndexedDbTransaction, pcs: SelectedProspectiveCoin[], ): Promise<AmountJson> { const costs: AmountJson[] = []; diff --git a/packages/taler-wallet-core/src/pay-peer-pull-credit.ts b/packages/taler-wallet-core/src/pay-peer-pull-credit.ts @@ -69,8 +69,7 @@ import { OperationRetryRecord, PeerPullCreditRecord, PeerPullPaymentCreditStatus, - WalletDbAllStoresReadOnlyTransaction, - WalletDbReadWriteTransaction, + WalletIndexedDbTransaction, WithdrawalGroupRecord, WithdrawalGroupStatus, WithdrawalRecordType, @@ -125,7 +124,7 @@ export class PeerPullCreditTransactionContext implements TransactionContext { } async updateTransactionMeta( - tx: WalletDbReadWriteTransaction<["peerPullCredit", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise<void> { const rec = await tx.peerPullCredit.get(this.pursePub); if (rec == null) { @@ -142,15 +141,7 @@ export class PeerPullCreditTransactionContext implements TransactionContext { } async deleteTransactionInTx( - tx: WalletDbReadWriteTransaction< - [ - "withdrawalGroups", - "peerPullCredit", - "planchets", - "tombstones", - "transactionsMeta", - ] - >, + tx: WalletIndexedDbTransaction, ): Promise<void> { const [rec, h] = await this.getRecordHandle(tx); if (!rec) { @@ -174,7 +165,7 @@ export class PeerPullCreditTransactionContext implements TransactionContext { * transaction item (e.g. if it was deleted). */ async lookupFullTransaction( - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, ): Promise<Transaction | undefined> { const pullCredit = await tx.peerPullCredit.get(this.pursePub); if (!pullCredit) { @@ -298,7 +289,7 @@ export class PeerPullCreditTransactionContext implements TransactionContext { } async getRecordHandle( - tx: WalletDbReadWriteTransaction<["peerPullCredit", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise< [PeerPullCreditRecord | undefined, RecordHandle<PeerPullCreditRecord>] > { diff --git a/packages/taler-wallet-core/src/pay-peer-pull-debit.ts b/packages/taler-wallet-core/src/pay-peer-pull-debit.ts @@ -76,16 +76,11 @@ import { PeerPullDebitRecordStatus, PeerPullPaymentIncomingRecord, RefreshOperationStatus, - WalletDbAllStoresReadOnlyTransaction, - WalletDbReadWriteTransaction, + WalletIndexedDbTransaction, timestampPreciseFromDb, timestampPreciseToDb, } from "./db.js"; -import { - fetchFreshExchange, - getExchangeScopeInfo, - getScopeForAllExchanges, -} from "./exchanges.js"; +import { getExchangeScopeInfo, getScopeForAllExchanges } from "./exchanges.js"; import { getTotalPeerPaymentCost, isPurseDeposited, @@ -124,7 +119,7 @@ export class PeerPullDebitTransactionContext implements TransactionContext { } async updateTransactionMeta( - tx: WalletDbReadWriteTransaction<["peerPullDebit", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise<void> { const rec = await tx.peerPullDebit.get(this.peerPullDebitId); if (rec == null) { @@ -147,7 +142,7 @@ export class PeerPullDebitTransactionContext implements TransactionContext { * transaction item (e.g. if it was deleted). */ async lookupFullTransaction( - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, ): Promise<Transaction | undefined> { const pi = await tx.peerPullDebit.get(this.peerPullDebitId); if (!pi) { @@ -185,7 +180,7 @@ export class PeerPullDebitTransactionContext implements TransactionContext { } async getRecordHandle( - tx: WalletDbReadWriteTransaction<["peerPullDebit", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise< [ PeerPullPaymentIncomingRecord | undefined, @@ -213,7 +208,7 @@ export class PeerPullDebitTransactionContext implements TransactionContext { } async deleteTransactionInTx( - tx: WalletDbReadWriteTransaction<["peerPullDebit", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise<void> { const [rec, h] = await this.getRecordHandle(tx); if (!rec) { diff --git a/packages/taler-wallet-core/src/pay-peer-push-credit.ts b/packages/taler-wallet-core/src/pay-peer-push-credit.ts @@ -68,8 +68,7 @@ import { OperationRetryRecord, PeerPushCreditRecord, PeerPushCreditStatus, - WalletDbAllStoresReadOnlyTransaction, - WalletDbReadWriteTransaction, + WalletIndexedDbTransaction, WithdrawalGroupRecord, WithdrawalGroupStatus, WithdrawalRecordType, @@ -128,7 +127,7 @@ export class PeerPushCreditTransactionContext implements TransactionContext { } async updateTransactionMeta( - tx: WalletDbReadWriteTransaction<["peerPushCredit", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise<void> { const rec = await tx.peerPushCredit.get(this.peerPushCreditId); if (rec == null) { @@ -151,7 +150,7 @@ export class PeerPushCreditTransactionContext implements TransactionContext { * transaction item (e.g. if it was deleted). */ async lookupFullTransaction( - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, ): Promise<Transaction | undefined> { const pushInc = await tx.peerPushCredit.get(this.peerPushCreditId); if (!pushInc) { @@ -260,7 +259,7 @@ export class PeerPushCreditTransactionContext implements TransactionContext { } async getRecordHandle( - tx: WalletDbReadWriteTransaction<["peerPushCredit", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise< [PeerPushCreditRecord | undefined, RecordHandle<PeerPushCreditRecord>] > { @@ -285,15 +284,7 @@ export class PeerPushCreditTransactionContext implements TransactionContext { } async deleteTransactionInTx( - tx: WalletDbReadWriteTransaction< - [ - "withdrawalGroups", - "planchets", - "peerPushCredit", - "tombstones", - "transactionsMeta", - ] - >, + tx: WalletIndexedDbTransaction, ): Promise<void> { const [rec, h] = await this.getRecordHandle(tx); if (!rec) { diff --git a/packages/taler-wallet-core/src/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/pay-peer-push-debit.ts @@ -71,8 +71,7 @@ import { EncryptContractRequest } from "./crypto/cryptoTypes.js"; import { PeerPushDebitRecord, PeerPushDebitStatus, - WalletDbAllStoresReadOnlyTransaction, - WalletDbReadWriteTransaction, + WalletIndexedDbTransaction, timestampPreciseFromDb, timestampPreciseToDb, timestampProtocolFromDb, @@ -118,7 +117,7 @@ export class PeerPushDebitTransactionContext implements TransactionContext { } async updateTransactionMeta( - tx: WalletDbReadWriteTransaction<["peerPushDebit", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise<void> { const rec = await tx.peerPushDebit.get(this.pursePub); if (rec == null) { @@ -141,7 +140,7 @@ export class PeerPushDebitTransactionContext implements TransactionContext { * transaction item (e.g. if it was deleted). */ async lookupFullTransaction( - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, ): Promise<Transaction | undefined> { const pushDebitRec = await tx.peerPushDebit.get(this.pursePub); if (pushDebitRec == null) { @@ -196,7 +195,7 @@ export class PeerPushDebitTransactionContext implements TransactionContext { } async getRecordHandle( - tx: WalletDbReadWriteTransaction<["peerPushDebit", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise< [PeerPushDebitRecord | undefined, RecordHandle<PeerPushDebitRecord>] > { @@ -221,7 +220,7 @@ export class PeerPushDebitTransactionContext implements TransactionContext { } async deleteTransactionInTx( - tx: WalletDbReadWriteTransaction<["peerPushDebit", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise<void> { const [rec, h] = await this.getRecordHandle(tx); if (!rec) { diff --git a/packages/taler-wallet-core/src/query.ts b/packages/taler-wallet-core/src/query.ts @@ -457,17 +457,6 @@ type GetIndexReadWriteAccess<RecordType, IndexMap> = { [P in keyof IndexMap]: IndexReadWriteAccessor<RecordType>; }; -export interface StoreReadOnlyAccessor<RecordType, IndexMap> { - get(key: IDBValidKey): Promise<RecordType | undefined>; - getAll( - query?: IDBKeyRange | IDBValidKey, - count?: number, - ): Promise<RecordType[]>; - iter(query?: IDBValidKey): ResultStream<RecordType>; - count(query?: IDBValidKey): Promise<number>; - indexes: GetIndexReadOnlyAccess<RecordType, IndexMap>; -} - export interface InsertResponse { /** * Key of the newly inserted (via put/add) record. @@ -599,19 +588,6 @@ export type DbReadWriteTransaction< notify: (w: WalletNotification) => void; }; -export type DbReadOnlyTransaction< - Stores extends StoreMap, - StoresArr extends Array<StoreNames<Stores>>, -> = { - [X in StoresArr[number]]: StoreReadOnlyAccessor< - Stores[X]["store"]["_dummy"], - Stores[X]["indexMap"] - >; -} & { - _util: TransactionUtil; - notify: (w: WalletNotification) => void; -}; - /** * Convert the type of an array to a union of the contents. * @@ -926,55 +902,6 @@ export interface DbAccess<Stores extends StoreMap> { tx: DbReadWriteTransaction<Stores, Array<StoreNames<Stores>>>, ) => Promise<T>, ): Promise<T>; - - /** - * Run an async function in a "readonly" transaction on the database, using - * all object store. - * - * The transaction function must run within the microtask queue. - * Waiting for macrotasks results in an autocommit and - * a subsequent exception thrown by this function. - */ - runAllStoresReadOnlyTx<T>( - options: { - label?: string; - }, - txf: ( - tx: DbReadOnlyTransaction<Stores, Array<StoreNames<Stores>>>, - ) => Promise<T>, - ): Promise<T>; - - /** - * Run an async function in a "readwrite" transaction on the database, using - * the selected object store. - * - * The transaction function must run within the microtask queue. - * Waiting for macrotasks results in an autocommit and - * a subsequent exception thrown by this function. - */ - runReadWriteTx<T, StoreNameArray extends Array<StoreNames<Stores>>>( - opts: { - storeNames: StoreNameArray; - label?: string; - }, - txf: (tx: DbReadWriteTransaction<Stores, StoreNameArray>) => Promise<T>, - ): Promise<T>; - - /** - * Run an async function in a "readonly" transaction on the database, using - * the selected object store. - * - * The transaction function must run within the microtask queue. - * Waiting for macrotasks results in an autocommit and - * a subsequent exception thrown by this function. - */ - runReadOnlyTx<T, StoreNameArray extends Array<StoreNames<Stores>>>( - opts: { - storeNames: StoreNameArray; - label?: string; - }, - txf: (tx: DbReadOnlyTransaction<Stores, StoreNameArray>) => Promise<T>, - ): Promise<T>; } export interface AfterCommitInfo { @@ -1101,105 +1028,4 @@ export class DbAccessImpl<Stores extends StoreMap> implements DbAccess<Stores> { ); return await runTx(tx, writeContext, txf, triggerContext); } - - async runAllStoresReadOnlyTx<T>( - options: { - label?: string; - }, - txf: ( - tx: DbReadOnlyTransaction<Stores, Array<StoreNames<Stores>>>, - ) => Promise<T>, - ): Promise<T> { - this.cancellationToken.throwIfCancelled(); - const accessibleStores: { [x: string]: StoreWithIndexes<any, any, any> } = - {}; - const strStoreNames: string[] = []; - for (const sn of Object.keys(this.stores as any)) { - const swi = (this.stores as any)[sn] as StoreWithIndexes<any, any, any>; - strStoreNames.push(swi.storeName); - accessibleStores[swi.storeName] = swi; - } - const mode = "readonly"; - const internalContext = new InternalTransactionContext( - this.triggers, - mode, - strStoreNames, - this.cancellationToken, - this.applyNotifications, - ); - const tx = this.db.transaction(strStoreNames, mode); - const writeContext = makeTxClientContext( - tx, - accessibleStores, - internalContext, - ); - const res = await runTx(tx, writeContext, txf, internalContext); - return res; - } - - async runReadWriteTx<T, StoreNameArray extends Array<StoreNames<Stores>>>( - opts: { - storeNames: StoreNameArray; - }, - txf: (tx: DbReadWriteTransaction<Stores, StoreNameArray>) => Promise<T>, - ): Promise<T> { - this.cancellationToken.throwIfCancelled(); - const accessibleStores: { [x: string]: StoreWithIndexes<any, any, any> } = - {}; - const strStoreNames: string[] = []; - for (const sn of opts.storeNames) { - const swi = (this.stores as any)[sn] as StoreWithIndexes<any, any, any>; - strStoreNames.push(swi.storeName); - accessibleStores[swi.storeName] = swi; - } - const mode = "readwrite"; - const triggerContext = new InternalTransactionContext( - this.triggers, - mode, - strStoreNames, - this.cancellationToken, - this.applyNotifications, - ); - const tx = this.db.transaction(strStoreNames, mode); - const writeContext = makeTxClientContext( - tx, - accessibleStores, - triggerContext, - ); - const res = await runTx(tx, writeContext, txf, triggerContext); - return res; - } - - async runReadOnlyTx<T, StoreNameArray extends Array<StoreNames<Stores>>>( - opts: { - storeNames: StoreNameArray; - }, - txf: (tx: DbReadOnlyTransaction<Stores, StoreNameArray>) => Promise<T>, - ): Promise<T> { - this.cancellationToken.throwIfCancelled(); - const accessibleStores: { [x: string]: StoreWithIndexes<any, any, any> } = - {}; - const strStoreNames: string[] = []; - for (const sn of opts.storeNames) { - const swi = (this.stores as any)[sn] as StoreWithIndexes<any, any, any>; - strStoreNames.push(swi.storeName); - accessibleStores[swi.storeName] = swi; - } - const mode = "readonly"; - const triggerContext = new InternalTransactionContext( - this.triggers, - mode, - strStoreNames, - this.cancellationToken, - this.applyNotifications, - ); - const tx = this.db.transaction(strStoreNames, mode); - const readContext = makeTxClientContext( - tx, - accessibleStores, - triggerContext, - ); - const res = await runTx(tx, readContext, txf, triggerContext); - return res; - } } diff --git a/packages/taler-wallet-core/src/recoup.ts b/packages/taler-wallet-core/src/recoup.ts @@ -57,8 +57,7 @@ import { RecoupGroupRecord, RecoupOperationStatus, RefreshCoinSource, - WalletDbAllStoresReadOnlyTransaction, - WalletDbReadWriteTransaction, + WalletIndexedDbTransaction, WithdrawCoinSource, WithdrawalGroupStatus, WithdrawalRecordType, @@ -77,16 +76,7 @@ const logger = new Logger("operations/recoup.ts"); */ export async function putGroupAsFinished( wex: WalletExecutionContext, - tx: WalletDbReadWriteTransaction< - [ - "recoupGroups", - "denominations", - "refreshGroups", - "coins", - "exchanges", - "transactionsMeta", - ] - >, + tx: WalletIndexedDbTransaction, recoupGroup: RecoupGroupRecord, coinIdx: number, ): Promise<void> { @@ -429,9 +419,7 @@ export class RecoupTransactionContext implements TransactionContext { } async updateTransactionMeta( - tx: WalletDbReadWriteTransaction< - ["recoupGroups", "exchanges", "transactionsMeta"] - >, + tx: WalletIndexedDbTransaction, ): Promise<void> { const recoupRec = await tx.recoupGroups.get(this.recoupGroupId); if (!recoupRec) { @@ -478,9 +466,7 @@ export class RecoupTransactionContext implements TransactionContext { } async deleteTransactionInTx( - tx: WalletDbReadWriteTransaction< - ["recoupGroups", "tombstones", "exchanges", "transactionsMeta"] - >, + tx: WalletIndexedDbTransaction, ): Promise<{ notifs: WalletNotification[] }> { const notifs: WalletNotification[] = []; const rec = await tx.recoupGroups.get(this.recoupGroupId); @@ -493,7 +479,7 @@ export class RecoupTransactionContext implements TransactionContext { } lookupFullTransaction( - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, ): Promise<Transaction | undefined> { throw new Error("Method not implemented."); } @@ -501,16 +487,7 @@ export class RecoupTransactionContext implements TransactionContext { export async function createRecoupGroup( wex: WalletExecutionContext, - tx: WalletDbReadWriteTransaction< - [ - "recoupGroups", - "denominations", - "refreshGroups", - "coins", - "exchanges", - "transactionsMeta", - ] - >, + tx: WalletIndexedDbTransaction, exchangeBaseUrl: string, coinPubs: string[], ): Promise<string> { diff --git a/packages/taler-wallet-core/src/refresh.ts b/packages/taler-wallet-core/src/refresh.ts @@ -103,9 +103,7 @@ import { RefreshSessionRecord, timestampPreciseFromDb, timestampPreciseToDb, - WalletDbAllStoresReadOnlyTransaction, - WalletDbReadOnlyTransaction, - WalletDbReadWriteTransaction, + WalletIndexedDbTransaction, } from "./db.js"; import { selectWithdrawalDenominations } from "./denomSelection.js"; import { fetchFreshExchange, getScopeForAllExchanges } from "./exchanges.js"; @@ -147,7 +145,7 @@ export class RefreshTransactionContext implements TransactionContext { } async updateTransactionMeta( - tx: WalletDbReadWriteTransaction<["refreshGroups", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise<void> { const rgRec = await tx.refreshGroups.get(this.refreshGroupId); if (!rgRec) { @@ -170,7 +168,7 @@ export class RefreshTransactionContext implements TransactionContext { * transaction item (e.g. if it was deleted). */ async lookupFullTransaction( - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, ): Promise<Transaction | undefined> { const refreshGroupRecord = await tx.refreshGroups.get(this.refreshGroupId); if (!refreshGroupRecord) { @@ -218,7 +216,7 @@ export class RefreshTransactionContext implements TransactionContext { } async getRecordHandle( - tx: WalletDbReadWriteTransaction<["refreshGroups", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise< [RefreshGroupRecord | undefined, RecordHandle<RefreshGroupRecord>] > { @@ -243,9 +241,7 @@ export class RefreshTransactionContext implements TransactionContext { } async deleteTransactionInTx( - tx: WalletDbReadWriteTransaction< - ["refreshGroups", "refreshSessions", "tombstones", "transactionsMeta"] - >, + tx: WalletIndexedDbTransaction, ): Promise<void> { const [rg, h] = await this.getRecordHandle(tx); if (!rg) { @@ -353,7 +349,7 @@ export class RefreshTransactionContext implements TransactionContext { export async function getTotalRefreshCost( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction<["denominations", "denominationFamilies"]>, + tx: WalletIndexedDbTransaction, refreshedDenom: DenominationInfo, amountLeft: AmountJson, ): Promise<AmountJson> { @@ -419,9 +415,7 @@ function getTotalRefreshCostInternal( async function getCoinAvailabilityForDenom( wex: WalletExecutionContext, - tx: WalletDbReadWriteTransaction< - ["coins", "coinAvailability", "denominations"] - >, + tx: WalletIndexedDbTransaction, denom: DenominationInfo, ageRestriction: number, ): Promise<CoinAvailabilityRecord> { @@ -450,15 +444,7 @@ async function getCoinAvailabilityForDenom( */ async function initRefreshSession( wex: WalletExecutionContext, - tx: WalletDbReadWriteTransaction< - [ - "refreshSessions", - "coinAvailability", - "coins", - "denominations", - "denominationFamilies", - ] - >, + tx: WalletIndexedDbTransaction, refreshGroup: RefreshGroupRecord, coinIndex: number, ): Promise<void> { @@ -556,9 +542,7 @@ async function initRefreshSession( */ async function destroyRefreshSession( wex: WalletExecutionContext, - tx: WalletDbReadWriteTransaction< - ["denominations", "coinAvailability", "coins"] - >, + tx: WalletIndexedDbTransaction, refreshGroup: RefreshGroupRecord, refreshSession: RefreshSessionRecord, ): Promise<void> { @@ -1478,15 +1462,7 @@ export interface RefreshOutputInfo { export async function calculateRefreshOutput( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction< - [ - "denominations", - "denominationFamilies", - "coins", - "refreshGroups", - "coinAvailability", - ] - >, + tx: WalletIndexedDbTransaction, currency: string, oldCoinPubs: CoinRefreshRequest[], ): Promise<RefreshOutputInfo> { @@ -1535,15 +1511,7 @@ export async function calculateRefreshOutput( async function applyRefreshToOldCoins( wex: WalletExecutionContext, - tx: WalletDbReadWriteTransaction< - [ - "denominations", - "coins", - "coinHistory", - "refreshGroups", - "coinAvailability", - ] - >, + tx: WalletIndexedDbTransaction, oldCoinPubs: CoinRefreshRequest[], refreshGroupId: string, ): Promise<void> { @@ -1637,18 +1605,7 @@ export interface CreateRefreshGroupResult { */ export async function createRefreshGroup( wex: WalletExecutionContext, - tx: WalletDbReadWriteTransaction< - [ - "denominations", - "denominationFamilies", - "coins", - "coinHistory", - "refreshGroups", - "refreshSessions", - "coinAvailability", - "transactionsMeta", - ] - >, + tx: WalletIndexedDbTransaction, currency: string, oldCoinPubs: CoinRefreshRequest[], refreshReason: RefreshReason, diff --git a/packages/taler-wallet-core/src/shepherd.ts b/packages/taler-wallet-core/src/shepherd.ts @@ -54,8 +54,7 @@ import { OPERATION_STATUS_NONFINAL_LAST, OperationRetryRecord, ReserveRecordStatus, - WalletDbAllStoresReadOnlyTransaction, - WalletDbReadOnlyTransaction, + WalletIndexedDbTransaction, timestampAbsoluteFromDb, timestampPreciseToDb, } from "./db.js"; @@ -687,7 +686,7 @@ async function callOperationHandlerForTaskId( */ async function taskToRetryNotification( ws: InternalWalletState, - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, pendingTaskId: string, e: TalerErrorDetail | undefined, ): Promise<WalletNotification | undefined> { @@ -715,20 +714,7 @@ async function taskToRetryNotification( async function getTransactionState( ws: InternalWalletState, - tx: WalletDbReadOnlyTransaction< - [ - "depositGroups", - "withdrawalGroups", - "purchases", - "refundGroups", - "peerPullCredit", - "peerPullDebit", - "peerPushDebit", - "peerPushCredit", - "refreshGroups", - "denomLossEvents", - ] - >, + tx: WalletIndexedDbTransaction, transactionId: string, ): Promise<{ txState: TransactionState; stId: number } | undefined> { const parsedTxId = parseTransactionIdentifier(transactionId); @@ -846,7 +832,7 @@ async function getTransactionState( async function makeTransactionRetryNotification( ws: InternalWalletState, - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, pendingTaskId: string, e: TalerErrorDetail | undefined, ): Promise<WalletNotification | undefined> { @@ -876,7 +862,7 @@ async function makeTransactionRetryNotification( async function makeExchangeRetryNotification( ws: InternalWalletState, - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, pendingTaskId: string, e: TalerErrorDetail | undefined, ): Promise<WalletNotification | undefined> { diff --git a/packages/taler-wallet-core/src/tokenSelection.ts b/packages/taler-wallet-core/src/tokenSelection.ts @@ -31,7 +31,7 @@ import { import { timestampProtocolFromDb, TokenRecord, - WalletDbReadOnlyTransaction, + WalletIndexedDbTransaction, } from "./db.js"; import { WalletExecutionContext } from "./index.js"; @@ -159,7 +159,7 @@ export function verifyTokenMerchant( } export async function selectPayTokensInTx( - tx: WalletDbReadOnlyTransaction<["tokens", "purchases"]>, + tx: WalletIndexedDbTransaction, req: SelectPayTokensRequest, ): Promise<SelectPayTokensResult> { if (logger.shouldLogTrace()) { diff --git a/packages/taler-wallet-core/src/transactions.ts b/packages/taler-wallet-core/src/transactions.ts @@ -60,8 +60,7 @@ import { PurchaseStatus, timestampPreciseToDb, TransactionMetaRecord, - WalletDbAllStoresReadOnlyTransaction, - WalletDbAllStoresReadWriteTransaction, + WalletIndexedDbTransaction, } from "./db.js"; import { DepositTransactionContext } from "./deposits.js"; import { DenomLossTransactionContext } from "./exchanges.js"; @@ -255,7 +254,7 @@ function checkFilterIncludes( async function addFiltered( wex: WalletExecutionContext, - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, req: GetTransactionsV2Request | undefined, target: Transaction[], source: TransactionMetaRecord[], @@ -312,7 +311,7 @@ function sortTransactions( } async function findOffsetTransaction( - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, req?: GetTransactionsV2Request, ): Promise<TransactionMetaRecord | undefined> { let forwards = req?.limit == null || req.limit >= 0; @@ -567,7 +566,7 @@ export async function getTransactions( */ export async function rematerializeTransactions( wex: WalletExecutionContext, - tx: WalletDbAllStoresReadWriteTransaction, + tx: WalletIndexedDbTransaction, ): Promise<void> { logger.trace("re-materializing transactions"); diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts @@ -305,9 +305,8 @@ import { CoinSourceType, ConfigRecordKey, DenominationRecord, - WalletDbAllStoresReadWriteTransaction, - WalletDbReadOnlyTransaction, - WalletStoresV1, + WalletIndexedDbStoresV1, + WalletIndexedDbTransaction, applyFixups, clearDatabase, exportDb, @@ -479,10 +478,7 @@ export interface WalletExecutionContext { * Uses the legacy transaction that only works with IndexedDB. */ runLegacyWalletDbTx<T>( - f: ( - tx: WalletDbAllStoresReadWriteTransaction, - wtx: WalletDbTransaction, - ) => Promise<T>, + f: (tx: LegacyWalletTxHandle) => Promise<T>, ): Promise<T>; /** * Start a database transaction. @@ -493,7 +489,7 @@ export interface WalletExecutionContext { readonly cryptoApi: TalerCryptoInterface; readonly cancellationToken: CancellationToken; readonly http: HttpRequestLibrary; - readonly db: DbAccess<typeof WalletStoresV1>; + readonly db: DbAccess<typeof WalletIndexedDbStoresV1>; readonly oc: ObservabilityContext; readonly cts: CancellationToken.Source | undefined; readonly taskScheduler: TaskScheduler; @@ -600,7 +596,7 @@ async function migrateMaterializedTransactions( export async function getDenomInfo( wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction<["denominations"]>, + tx: WalletIndexedDbTransaction, exchangeBaseUrl: string, denomPubHash: string, ): Promise<DenominationInfo | undefined> { @@ -2065,7 +2061,7 @@ export async function handleGetDiagnostics( ): Promise<TestingGetDiagnosticsResponse> { const cnt: Record<string, number> = {}; const exchangeEntries: TestingGetDiagnosticsResponse["exchangeEntries"] = []; - await wex.db.runAllStoresReadOnlyTx({}, async (tx) => { + await wex.db.runAllStoresReadWriteTx({}, async (tx) => { cnt["coinAvailability"] = await tx.coinAvailability.count(); cnt["coins"] = await tx.coins.count(); cnt["denominationFamilies"] = await tx.denominationFamilies.count(); @@ -2854,6 +2850,10 @@ async function handleTxRetries<T>( throw Error("not reached"); } +export type LegacyWalletTxHandle = WalletIndexedDbTransaction & { + wtx: WalletDbTransaction; +}; + async function runWalletDbTx<T>( wex: WalletExecutionContext, f: (tx: WalletDbTransaction) => Promise<T>, @@ -2868,15 +2868,13 @@ async function runWalletDbTx<T>( async function runLegacyWalletDbTx<T>( wex: WalletExecutionContext, - f: ( - tx: WalletDbAllStoresReadWriteTransaction, - wtx: WalletDbTransaction, - ) => Promise<T>, + f: (tx: LegacyWalletTxHandle) => Promise<T>, ): Promise<T> { return await handleTxRetries(wex, async () => { - return await wex.db.runAllStoresReadWriteTx({}, async (mytx) => { - const tx = new IdbWalletTransaction(mytx); - return await f(mytx, tx); + return await wex.db.runAllStoresReadWriteTx({}, async (tx) => { + const wtx = new IdbWalletTransaction(tx); + (tx as any).wtx = wtx; + return await f(tx as LegacyWalletTxHandle); }); }); } @@ -3220,11 +3218,11 @@ class WalletDbTriggerSpec implements TriggerSpec { ); const modified = info.accessedStores; if ( - modified.has(WalletStoresV1.exchanges.storeName) || - modified.has(WalletStoresV1.exchangeDetails.storeName) || - modified.has(WalletStoresV1.denominations.storeName) || - modified.has(WalletStoresV1.globalCurrencyAuditors.storeName) || - modified.has(WalletStoresV1.globalCurrencyExchanges.storeName) + modified.has(WalletIndexedDbStoresV1.exchanges.storeName) || + modified.has(WalletIndexedDbStoresV1.exchangeDetails.storeName) || + modified.has(WalletIndexedDbStoresV1.denominations.storeName) || + modified.has(WalletIndexedDbStoresV1.globalCurrencyAuditors.storeName) || + modified.has(WalletIndexedDbStoresV1.globalCurrencyExchanges.storeName) ) { this.ws.clearAllCaches(); } @@ -3278,7 +3276,7 @@ export class InternalWalletState { private _indexedDbHandle: IDBDatabase | undefined = undefined; - private _dbAccessHandle: DbAccess<typeof WalletStoresV1> | undefined; + private _dbAccessHandle: DbAccess<typeof WalletIndexedDbStoresV1> | undefined; private _http: HttpRequestLibrary | undefined = undefined; @@ -3313,10 +3311,7 @@ export class InternalWalletState { * Run a database transaction outside of a wallet execution context. */ async runStandaloneLegacyWalletDbTx<T>( - f: ( - tx: WalletDbAllStoresReadWriteTransaction, - wtx: WalletDbTransaction, - ) => Promise<T>, + f: (tx: WalletIndexedDbTransaction, wtx: WalletDbTransaction) => Promise<T>, ): Promise<T> { if (!this._dbAccessHandle) { this._dbAccessHandle = this.createDbAccessHandle( @@ -3389,14 +3384,14 @@ export class InternalWalletState { createDbAccessHandle( cancellationToken: CancellationToken, - ): DbAccess<typeof WalletStoresV1> { + ): DbAccess<typeof WalletIndexedDbStoresV1> { if (!this._indexedDbHandle) { throw Error("db not initialized"); } const iws = this; return new DbAccessImpl( this._indexedDbHandle, - WalletStoresV1, + WalletIndexedDbStoresV1, new WalletDbTriggerSpec(this), cancellationToken, (notifs: WalletNotification[]): void => { diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts @@ -140,10 +140,7 @@ import { OperationRetryRecord, PlanchetRecord, PlanchetStatus, - WalletDbAllStoresReadOnlyTransaction, - WalletDbAllStoresReadWriteTransaction, - WalletDbReadOnlyTransaction, - WalletDbReadWriteTransaction, + WalletIndexedDbTransaction, WgInfo, WithdrawalGroupRecord, WithdrawalGroupStatus, @@ -363,7 +360,7 @@ export class WithdrawTransactionContext implements TransactionContext { * transaction item (e.g. if it was deleted). */ async lookupFullTransaction( - tx: WalletDbAllStoresReadOnlyTransaction, + tx: WalletIndexedDbTransaction, ): Promise<Transaction | undefined> { const withdrawalGroupRecord = await tx.withdrawalGroups.get( this.withdrawalGroupId, @@ -436,7 +433,7 @@ export class WithdrawTransactionContext implements TransactionContext { * Update the metadata of the transaction in the database. */ async updateTransactionMeta( - tx: WalletDbReadWriteTransaction<["withdrawalGroups", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise<void> { const ctx = this; const wgRecord = await tx.withdrawalGroups.get(ctx.withdrawalGroupId); @@ -482,7 +479,7 @@ export class WithdrawTransactionContext implements TransactionContext { } async getRecordHandle( - tx: WalletDbReadWriteTransaction<["withdrawalGroups", "transactionsMeta"]>, + tx: WalletIndexedDbTransaction, ): Promise< [WithdrawalGroupRecord | undefined, RecordHandle<WithdrawalGroupRecord>] > { @@ -507,9 +504,7 @@ export class WithdrawTransactionContext implements TransactionContext { } async deleteTransactionInTx( - tx: WalletDbReadWriteTransaction< - ["withdrawalGroups", "planchets", "tombstones", "transactionsMeta"] - >, + tx: WalletIndexedDbTransaction, ): Promise<void> { const [rec, h] = await this.getRecordHandle(tx); if (!rec) { @@ -1258,7 +1253,7 @@ async function getWithdrawableDenoms( */ export async function getWithdrawableDenomsTx( _wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction<["denominations", "denominationFamilies"]>, + tx: WalletIndexedDbTransaction, exchangeBaseUrl: string, currency: string, maxAmount?: AmountLike, @@ -3084,9 +3079,7 @@ export function augmentPaytoUrisForKycTransfer( * Get payto URIs that can be used to fund a withdrawal operation. */ export async function getFundingPaytoUris( - tx: WalletDbReadOnlyTransaction< - ["withdrawalGroups", "exchanges", "exchangeDetails"] - >, + tx: WalletIndexedDbTransaction, withdrawalGroupId: string, ): Promise<string[]> { const withdrawalGroup = await tx.withdrawalGroups.get(withdrawalGroupId); @@ -3615,7 +3608,7 @@ export interface PerformCreateWithdrawalGroupResult { export async function internalPerformCreateWithdrawalGroup( wex: WalletExecutionContext, - tx: WalletDbAllStoresReadWriteTransaction, + tx: WalletIndexedDbTransaction, prep: PrepareCreateWithdrawalGroupResult, ): Promise<PerformCreateWithdrawalGroupResult> { const { withdrawalGroup } = prep; @@ -3654,7 +3647,7 @@ export async function internalPerformCreateWithdrawalGroup( */ async function internalPerformExchangeWasUsed( wex: WalletExecutionContext, - tx: WalletDbReadWriteTransaction<["exchanges"]>, + tx: WalletIndexedDbTransaction, canonExchange: string, ): Promise<void> { const exchange = await tx.exchanges.get(canonExchange); @@ -3797,7 +3790,7 @@ export async function prepareBankIntegratedWithdrawal( * if the account already exists. */ async function storeKnownBankAccount( - tx: WalletDbReadWriteTransaction<["bankAccountsV2"]>, + tx: WalletIndexedDbTransaction, instructedCurrency: string, senderWire: string, ): Promise<string | undefined> {