taler-typescript-core

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

commit e5673419eb668491568075f06590a357a174bb0a
parent 3b91c7c8c1d0db457ad28cd832cbcc61e7b1cd2a
Author: Florian Dold <dold@taler.net>
Date:   Sat, 18 Jul 2026 20:40:04 +0200

db: add refresh group and session accessors

Diffstat:
Mpackages/taler-wallet-core/src/balance.ts | 4++--
Mpackages/taler-wallet-core/src/common.ts | 4++--
Mpackages/taler-wallet-core/src/db-common.ts | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackages/taler-wallet-core/src/db-indexeddb.ts | 119++++---------------------------------------------------------------------------
Mpackages/taler-wallet-core/src/dbtx-indexeddb.ts | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mpackages/taler-wallet-core/src/dbtx.ts | 30++++++++++++++++++++++++++++--
Mpackages/taler-wallet-core/src/deposits.ts | 4++--
Mpackages/taler-wallet-core/src/dev-experiments.ts | 6+++---
Mpackages/taler-wallet-core/src/exchanges.ts | 2+-
Mpackages/taler-wallet-core/src/pay-peer-pull-debit.ts | 2+-
Mpackages/taler-wallet-core/src/refresh.ts | 126++++++++++++++++++++++++++++++++++++++++----------------------------------------
11 files changed, 280 insertions(+), 192 deletions(-)

diff --git a/packages/taler-wallet-core/src/balance.ts b/packages/taler-wallet-core/src/balance.ts @@ -89,10 +89,10 @@ import { PurchaseStatus, RefreshOperationStatus, WithdrawalGroupStatus, + WalletRefreshGroup, } from "./db-common.js"; import { DonationSummaryRecord, - RefreshGroupRecord, WithdrawalRecordType, } from "./db-indexeddb.js"; import { WalletDbTransaction } from "./dbtx.js"; @@ -121,7 +121,7 @@ interface WalletBalance { } function computeRefreshGroupAvailableAmountForExchanges( - r: RefreshGroupRecord, + r: WalletRefreshGroup, restrictExchanges: AllowedExchangeInfo[] | undefined, ): AmountJson { // Don't count finished refreshes, since the refresh already resulted diff --git a/packages/taler-wallet-core/src/common.ts b/packages/taler-wallet-core/src/common.ts @@ -70,6 +70,7 @@ import { WalletCoin, WalletDepositGroup, WalletRecoupGroup, + WalletRefreshGroup, } from "./db-common.js"; import { ExchangeEntryRecord, @@ -78,7 +79,6 @@ import { PeerPushCreditRecord, PeerPushDebitRecord, PurchaseRecord, - RefreshGroupRecord, WithdrawalGroupRecord, } from "./db-indexeddb.js"; import { WalletDbTransaction } from "./dbtx.js"; @@ -822,7 +822,7 @@ export namespace TaskIdentifiers { )}` as TaskIdStr; } export function forRefresh( - refreshGroupRecord: RefreshGroupRecord, + refreshGroupRecord: WalletRefreshGroup, ): TaskIdStr { return `${PendingTaskType.Refresh}:${refreshGroupRecord.refreshGroupId}` as TaskIdStr; } diff --git a/packages/taler-wallet-core/src/db-common.ts b/packages/taler-wallet-core/src/db-common.ts @@ -31,6 +31,8 @@ import { DenominationInfo, TransferOptionRaw, CoinRefreshRequest, + ExchangeRefundRequest, + RefreshReason, CoinStatus, AgeCommitmentProof, TransactionIdStr, @@ -389,6 +391,118 @@ export interface WalletReserve { amlReview?: boolean; } +export interface WalletRefreshGroupPerExchangeInfo { + /** + * (Expected) output once the refresh group succeeded. + */ + outputEffective: AmountString; +} + +/** + * Group of refresh operations. The refreshed coins do not + * have to belong to the same exchange, but must have the same + * currency. + */ +export interface WalletRefreshGroup { + operationStatus: RefreshOperationStatus; + + /** + * Unique, randomly generated identifier for this group of + * refresh operations. + */ + refreshGroupId: string; + + /** + * Currency of this refresh group. + */ + currency: string; + + /** + * Reason why this refresh group has been created. + */ + reason: RefreshReason; + + originatingTransactionId?: string; + + oldCoinPubs: string[]; + + inputPerCoin: AmountString[]; + + expectedOutputPerCoin: AmountString[]; + + infoPerExchange?: Record<string, WalletRefreshGroupPerExchangeInfo>; + + /** + * Flag for each coin whether refreshing finished. + * If a coin can't be refreshed (remaining value too small), + * it will be marked as finished, but no refresh session will + * be created. + */ + statusPerCoin: RefreshCoinStatus[]; + + /** + * Refund requests that might still be necessary + * before the refresh can work. + */ + refundRequests: { [n: number]: ExchangeRefundRequest }; + + timestampCreated: DbPreciseTimestamp; + + failReason?: TalerErrorDetail; + + /** + * Timestamp when the refresh session finished. + */ + timestampFinished: DbPreciseTimestamp | undefined; +} + +/** + * Ongoing refresh + */ +export interface WalletRefreshSession { + refreshGroupId: string; + + /** + * Index of the coin in the refresh group. + */ + coinIndex: number; + + /** + * If this field is set, it's a V2 refresh session. + */ + sessionPublicSeed?: string; + + /** + * Sum of the value of denominations we want + * to withdraw in this session, without fees. + */ + amountRefreshOutput: AmountString; + + /** + * Hashed denominations of the newly requested coins. + */ + newDenoms: { + denomPubHash: string; + count: number; + }[]; + + /** + * The no-reveal-index after we've done the melting. + */ + norevealIndex?: number; + + /** + * Last error response from the exchange. + * + * FIXME: We don't store the last HTTP status yet. + */ + lastError?: TalerErrorDetail; + + // Reserved legacy fields: + // * sessionSecretSeed: string + // (legacy v1 refresh) +} + export interface WalletWithdrawCoinSource { type: CoinSourceType.Withdraw; diff --git a/packages/taler-wallet-core/src/db-indexeddb.ts b/packages/taler-wallet-core/src/db-indexeddb.ts @@ -122,6 +122,9 @@ import { WalletDepositTrackingInfo, WalletDepositInfoPerExchange, WalletRecoupGroup, + WalletRefreshGroup, + WalletRefreshSession, + WalletRefreshGroupPerExchangeInfo, WalletReserve, WalletCoin, WalletCoinAvailability, @@ -703,118 +706,6 @@ export namespace TokenRecord { } } -export interface RefreshGroupPerExchangeInfo { - /** - * (Expected) output once the refresh group succeeded. - */ - outputEffective: AmountString; -} - -/** - * Group of refresh operations. The refreshed coins do not - * have to belong to the same exchange, but must have the same - * currency. - */ -export interface RefreshGroupRecord { - operationStatus: RefreshOperationStatus; - - /** - * Unique, randomly generated identifier for this group of - * refresh operations. - */ - refreshGroupId: string; - - /** - * Currency of this refresh group. - */ - currency: string; - - /** - * Reason why this refresh group has been created. - */ - reason: RefreshReason; - - originatingTransactionId?: string; - - oldCoinPubs: string[]; - - inputPerCoin: AmountString[]; - - expectedOutputPerCoin: AmountString[]; - - infoPerExchange?: Record<string, RefreshGroupPerExchangeInfo>; - - /** - * Flag for each coin whether refreshing finished. - * If a coin can't be refreshed (remaining value too small), - * it will be marked as finished, but no refresh session will - * be created. - */ - statusPerCoin: RefreshCoinStatus[]; - - /** - * Refund requests that might still be necessary - * before the refresh can work. - */ - refundRequests: { [n: number]: ExchangeRefundRequest }; - - timestampCreated: DbPreciseTimestamp; - - failReason?: TalerErrorDetail; - - /** - * Timestamp when the refresh session finished. - */ - timestampFinished: DbPreciseTimestamp | undefined; -} - -/** - * Ongoing refresh - */ -export interface RefreshSessionRecord { - refreshGroupId: string; - - /** - * Index of the coin in the refresh group. - */ - coinIndex: number; - - /** - * If this field is set, it's a V2 refresh session. - */ - sessionPublicSeed?: string; - - /** - * Sum of the value of denominations we want - * to withdraw in this session, without fees. - */ - amountRefreshOutput: AmountString; - - /** - * Hashed denominations of the newly requested coins. - */ - newDenoms: { - denomPubHash: string; - count: number; - }[]; - - /** - * The no-reveal-index after we've done the melting. - */ - norevealIndex?: number; - - /** - * Last error response from the exchange. - * - * FIXME: We don't store the last HTTP status yet. - */ - lastError?: TalerErrorDetail; - - // Reserved legacy fields: - // * sessionSecretSeed: string - // (legacy v1 refresh) -} - /** * Partial information about the downloaded proposal. * Only contains data that is relevant for indexing on the @@ -2023,7 +1914,7 @@ export const WalletIndexedDbStoresV1 = { }), refreshGroups: describeStore( "refreshGroups", - describeContents<RefreshGroupRecord>({ + describeContents<WalletRefreshGroup>({ keyPath: "refreshGroupId", }), { @@ -2039,7 +1930,7 @@ export const WalletIndexedDbStoresV1 = { ), refreshSessions: describeStore( "refreshSessions", - describeContents<RefreshSessionRecord>({ + describeContents<WalletRefreshSession>({ keyPath: ["refreshGroupId", "coinIndex"], }), { diff --git a/packages/taler-wallet-core/src/dbtx-indexeddb.ts b/packages/taler-wallet-core/src/dbtx-indexeddb.ts @@ -57,10 +57,11 @@ import { WalletDepositGroup, WalletRecoupGroup, WalletReserve, + WalletRefreshGroup, + WalletRefreshSession, } from "./db-common.js"; import type { ExchangeEntryRecord, - RefreshGroupRecord, WithdrawalGroupRecord, ExchangeDetailsRecord, DonationSummaryRecord, @@ -268,6 +269,62 @@ export class IdbWalletTransaction implements WalletDbTransaction { }); } + async getRefreshGroup( + refreshGroupId: string, + ): Promise<WalletRefreshGroup | undefined> { + const tx = this.tx; + return await tx.refreshGroups.get(refreshGroupId); + } + + async upsertRefreshGroup(rec: WalletRefreshGroup): Promise<void> { + const tx = this.tx; + await tx.refreshGroups.put(rec); + } + + async deleteRefreshGroup(refreshGroupId: string): Promise<void> { + const tx = this.tx; + await tx.refreshGroups.delete(refreshGroupId); + } + + async getRefreshGroupsByOriginatingTransaction( + transactionId: string, + ): Promise<WalletRefreshGroup[]> { + const tx = this.tx; + return await tx.refreshGroups.indexes.byOriginatingTransactionId.getAll( + transactionId, + ); + } + + async getRefreshSession( + refreshGroupId: string, + coinIndex: number, + ): Promise<WalletRefreshSession | undefined> { + const tx = this.tx; + return await tx.refreshSessions.get([refreshGroupId, coinIndex]); + } + + async upsertRefreshSession(rec: WalletRefreshSession): Promise<void> { + const tx = this.tx; + await tx.refreshSessions.put(rec); + } + + async deleteRefreshSession( + refreshGroupId: string, + coinIndex: number, + ): Promise<void> { + const tx = this.tx; + await tx.refreshSessions.delete([refreshGroupId, coinIndex]); + } + + async getRefreshSessionsByGroup( + refreshGroupId: string, + ): Promise<WalletRefreshSession[]> { + const tx = this.tx; + return await tx.refreshSessions.indexes.byRefreshGroupId.getAll( + refreshGroupId, + ); + } + async getRecoupGroup( recoupGroupId: string, ): Promise<WalletRecoupGroup | undefined> { @@ -761,7 +818,7 @@ export class IdbWalletTransaction implements WalletDbTransaction { return await this.tx.coinAvailability.iter().toArray(); } - async getActiveRefreshGroups(): Promise<RefreshGroupRecord[]> { + async getActiveRefreshGroups(): Promise<WalletRefreshGroup[]> { return await this.tx.refreshGroups.indexes.byStatus.getAll( getActiveKeyRange(), ); diff --git a/packages/taler-wallet-core/src/dbtx.ts b/packages/taler-wallet-core/src/dbtx.ts @@ -61,10 +61,11 @@ import { WalletDepositGroup, WalletRecoupGroup, WalletReserve, + WalletRefreshGroup, + WalletRefreshSession, } from "./db-common.js"; import type { ExchangeEntryRecord, - RefreshGroupRecord, WithdrawalGroupRecord, ExchangeDetailsRecord, DonationSummaryRecord, @@ -176,6 +177,31 @@ export interface WalletDbTransaction { */ upsertContractTerms(rec: WalletContractTerms): Promise<void>; + getRefreshGroup( + refreshGroupId: string, + ): Promise<WalletRefreshGroup | undefined>; + + upsertRefreshGroup(rec: WalletRefreshGroup): Promise<void>; + + deleteRefreshGroup(refreshGroupId: string): Promise<void>; + + getRefreshGroupsByOriginatingTransaction( + transactionId: string, + ): Promise<WalletRefreshGroup[]>; + + getRefreshSession( + refreshGroupId: string, + coinIndex: number, + ): Promise<WalletRefreshSession | undefined>; + + upsertRefreshSession(rec: WalletRefreshSession): Promise<void>; + + deleteRefreshSession(refreshGroupId: string, coinIndex: number): Promise<void>; + + getRefreshSessionsByGroup( + refreshGroupId: string, + ): Promise<WalletRefreshSession[]>; + getRecoupGroup( recoupGroupId: string, ): Promise<WalletRecoupGroup | undefined>; @@ -365,7 +391,7 @@ export interface WalletDbTransaction { getCoinAvailabilities(): Promise<WalletCoinAvailability[]>; - getActiveRefreshGroups(): Promise<RefreshGroupRecord[]>; + getActiveRefreshGroups(): Promise<WalletRefreshGroup[]>; getActiveWithdrawalGroups(): Promise<WithdrawalGroupRecord[]>; diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts @@ -988,7 +988,7 @@ async function waitForRefreshOnDepositGroup( await genericWaitForState(wex, { async checkState() { return await wex.runLegacyWalletDbTx(async (tx) => { - const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId); + const refreshGroup = await tx.wtx.getRefreshGroup(abortRefreshGroupId); switch (refreshGroup?.operationStatus) { case undefined: case RefreshOperationStatus.Failed: @@ -1007,7 +1007,7 @@ async function waitForRefreshOnDepositGroup( }); const didTransition = await wex.runLegacyWalletDbTx(async (tx) => { - const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId); + const refreshGroup = await tx.wtx.getRefreshGroup(abortRefreshGroupId); let newOpState: DepositOperationStatus | undefined; switch (refreshGroup?.operationStatus) { case undefined: { diff --git a/packages/taler-wallet-core/src/dev-experiments.ts b/packages/taler-wallet-core/src/dev-experiments.ts @@ -63,10 +63,10 @@ import { WithdrawalGroupStatus, timestampPreciseToDb, timestampProtocolToDb, + WalletRefreshGroup, } from "./db-common.js"; import { DenomLossEventRecord, - RefreshGroupRecord, WithdrawalRecordType, } from "./db-indexeddb.js"; import { @@ -185,7 +185,7 @@ export async function applyDevExperiment( case "insert-pending-refresh": { const refreshGroupId = encodeCrock(getRandomBytes(32)); await wex.runLegacyWalletDbTx(async (tx) => { - const newRg: RefreshGroupRecord = { + const newRg: WalletRefreshGroup = { currency: "TESTKUDOS", expectedOutputPerCoin: [], inputPerCoin: [], @@ -200,7 +200,7 @@ export async function applyDevExperiment( infoPerExchange: {}, refundRequests: {}, }; - await tx.refreshGroups.put(newRg); + await tx.wtx.upsertRefreshGroup(newRg); const ctx = new RefreshTransactionContext(wex, refreshGroupId); await ctx.updateTransactionMeta(tx); }); diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts @@ -4351,7 +4351,7 @@ export async function migrateExchange( rec.infoPerExchange[req.newExchangeBaseUrl] = rec.infoPerExchange[req.oldExchangeBaseUrl]; delete rec.infoPerExchange[req.oldExchangeBaseUrl]; - await tx.refreshGroups.put(rec); + await tx.wtx.upsertRefreshGroup(rec); } }); } diff --git a/packages/taler-wallet-core/src/pay-peer-pull-debit.ts b/packages/taler-wallet-core/src/pay-peer-pull-debit.ts @@ -645,7 +645,7 @@ async function processPeerPullDebitAbortingRefresh( checkLogicInvariant(!!abortRefreshGroupId); const ctx = new PeerPullDebitTransactionContext(wex, peerPullDebitId); await wex.runLegacyWalletDbTx(async (tx) => { - const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId); + const refreshGroup = await tx.wtx.getRefreshGroup(abortRefreshGroupId); const [rec, h] = await ctx.getRecordHandle(tx); if (!rec) { return; diff --git a/packages/taler-wallet-core/src/refresh.ts b/packages/taler-wallet-core/src/refresh.ts @@ -99,12 +99,12 @@ import { WalletCoinAvailability, WalletCoinHistory, WalletCoin, + WalletRefreshGroupPerExchangeInfo, + WalletRefreshGroup, + WalletRefreshSession, } from "./db-common.js"; import { CoinSourceType, - RefreshGroupPerExchangeInfo, - RefreshGroupRecord, - RefreshSessionRecord, } from "./db-indexeddb.js"; import { selectWithdrawalDenominations } from "./denomSelection.js"; import { @@ -151,7 +151,7 @@ export class RefreshTransactionContext implements TransactionContext { } async updateTransactionMeta(tx: LegacyWalletTxHandle): Promise<void> { - const rgRec = await tx.refreshGroups.get(this.refreshGroupId); + const rgRec = await tx.wtx.getRefreshGroup(this.refreshGroupId); if (!rgRec) { await tx.wtx.deleteTransactionMeta(this.transactionId); return; @@ -174,7 +174,7 @@ export class RefreshTransactionContext implements TransactionContext { async lookupFullTransaction( tx: LegacyWalletTxHandle, ): Promise<Transaction | undefined> { - const refreshGroupRecord = await tx.refreshGroups.get(this.refreshGroupId); + const refreshGroupRecord = await tx.wtx.getRefreshGroup(this.refreshGroupId); if (!refreshGroupRecord) { return undefined; } @@ -222,16 +222,16 @@ export class RefreshTransactionContext implements TransactionContext { async getRecordHandle( tx: LegacyWalletTxHandle, ): Promise< - [RefreshGroupRecord | undefined, RecordHandle<RefreshGroupRecord>] + [WalletRefreshGroup | undefined, RecordHandle<WalletRefreshGroup>] > { - return getGenericRecordHandle<RefreshGroupRecord>( + return getGenericRecordHandle<WalletRefreshGroup>( this, tx.wtx, - async () => tx.refreshGroups.get(this.refreshGroupId), + async () => tx.wtx.getRefreshGroup(this.refreshGroupId), async (r) => { - await tx.refreshGroups.put(r); + await tx.wtx.upsertRefreshGroup(r); }, - async () => tx.refreshGroups.delete(this.refreshGroupId), + async () => tx.wtx.deleteRefreshGroup(this.refreshGroupId), (r) => computeRefreshTransactionState(r), (r) => r.operationStatus, () => this.updateTransactionMeta(tx), @@ -252,14 +252,14 @@ export class RefreshTransactionContext implements TransactionContext { ); return; } - const sessions = await tx.refreshSessions.indexes.byRefreshGroupId.getAll( + const sessions = await tx.wtx.getRefreshSessionsByGroup( rg.refreshGroupId, ); for (const s of sessions) { - await tx.refreshSessions.delete([s.refreshGroupId, s.coinIndex]); + await tx.wtx.deleteRefreshSession(s.refreshGroupId, s.coinIndex); } await h.update(undefined); - await tx.refreshGroups.delete(rg.refreshGroupId); + await tx.wtx.deleteRefreshGroup(rg.refreshGroupId); } async userSuspendTransaction(): Promise<void> { @@ -448,7 +448,7 @@ async function getCoinAvailabilityForDenom( async function initRefreshSession( wex: WalletExecutionContext, tx: LegacyWalletTxHandle, - refreshGroup: RefreshGroupRecord, + refreshGroup: WalletRefreshGroup, coinIndex: number, ): Promise<void> { const refreshGroupId = refreshGroup.refreshGroupId; @@ -525,7 +525,7 @@ async function initRefreshSession( await tx.coinAvailability.put(car); } - const newSession: RefreshSessionRecord = { + const newSession: WalletRefreshSession = { coinIndex, refreshGroupId, norevealIndex: undefined, @@ -535,7 +535,7 @@ async function initRefreshSession( })), amountRefreshOutput: Amounts.stringify(newCoinDenoms.totalCoinValue), }; - await tx.refreshSessions.put(newSession); + await tx.wtx.upsertRefreshSession(newSession); } /** @@ -546,8 +546,8 @@ async function initRefreshSession( async function destroyRefreshSession( wex: WalletExecutionContext, tx: LegacyWalletTxHandle, - refreshGroup: RefreshGroupRecord, - refreshSession: RefreshSessionRecord, + refreshGroup: WalletRefreshGroup, + refreshSession: WalletRefreshSession, ): Promise<void> { for (let i = 0; i < refreshSession.newDenoms.length; i++) { const oldCoin = await tx.coins.get( @@ -578,7 +578,7 @@ async function destroyRefreshSession( } } -function getRefreshRequestTimeout(rg: RefreshGroupRecord): Duration { +function getRefreshRequestTimeout(rg: WalletRefreshGroup): Duration { return Duration.fromSpec({ seconds: 60, }); @@ -599,14 +599,14 @@ async function refreshMelt( ): Promise<void> { const ctx = new RefreshTransactionContext(wex, refreshGroupId); const d = await wex.runLegacyWalletDbTx(async (tx) => { - const refreshGroup = await tx.refreshGroups.get(refreshGroupId); + const refreshGroup = await tx.wtx.getRefreshGroup(refreshGroupId); if (!refreshGroup) { return; } - const refreshSession = await tx.refreshSessions.get([ + const refreshSession = await tx.wtx.getRefreshSession( refreshGroupId, coinIndex, - ]); + ); if (!refreshSession) { return; } @@ -664,10 +664,10 @@ async function refreshMelt( } const seed = encodeCrock(getRandomBytes(64)); const updatedSession = await wex.runLegacyWalletDbTx(async (tx) => { - const refreshSession = await tx.refreshSessions.get([ + const refreshSession = await tx.wtx.getRefreshSession( refreshGroupId, coinIndex, - ]); + ); if (!refreshSession) { return undefined; } @@ -675,7 +675,7 @@ async function refreshMelt( return refreshSession; } refreshSession.sessionPublicSeed = seed; - await tx.refreshSessions.put(refreshSession); + await tx.wtx.upsertRefreshSession(refreshSession); return refreshSession; }); if (!updatedSession) { @@ -784,14 +784,14 @@ async function refreshMelt( refreshSession.norevealIndex = norevealIndex; await wex.runLegacyWalletDbTx(async (tx) => { - const rg = await tx.refreshGroups.get(refreshGroupId); + const rg = await tx.wtx.getRefreshGroup(refreshGroupId); if (!rg) { return; } if (rg.timestampFinished) { return; } - const rs = await tx.refreshSessions.get([refreshGroupId, coinIndex]); + const rs = await tx.wtx.getRefreshSession(refreshGroupId, coinIndex); if (!rs) { return; } @@ -799,7 +799,7 @@ async function refreshMelt( return; } rs.norevealIndex = norevealIndex; - await tx.refreshSessions.put(rs); + await tx.wtx.upsertRefreshSession(rs); }); } @@ -834,22 +834,22 @@ async function handleRefreshMeltGone( return; } rg.statusPerCoin[coinIndex] = RefreshCoinStatus.PendingRedenominate; - const refreshSession = await tx.refreshSessions.get([ + const refreshSession = await tx.wtx.getRefreshSession( ctx.refreshGroupId, coinIndex, - ]); + ); if (!refreshSession) { throw Error("db invariant failed: missing refresh session in database"); } refreshSession.lastError = errDetails; - await tx.refreshSessions.put(refreshSession); + await tx.wtx.upsertRefreshSession(refreshSession); await h.update(rg); }); } async function handleRefreshMeltConflict( ctx: RefreshTransactionContext, - refreshGroup: RefreshGroupRecord, + refreshGroup: WalletRefreshGroup, coinIndex: number, errDetails: TalerErrorDetail, meltValueWithFee: AmountLike, @@ -877,12 +877,12 @@ async function handleRefreshMeltConflict( switch (httpResp.status) { case HttpStatusCode.Ok: await ctx.wex.runLegacyWalletDbTx(async (tx) => { - const rg = await tx.refreshGroups.get(refreshGroup.refreshGroupId); + const rg = await tx.wtx.getRefreshGroup(refreshGroup.refreshGroupId); if (!rg || rg.operationStatus != RefreshOperationStatus.Pending) { return; } delete rg.refundRequests[coinIndex]; - await tx.refreshGroups.put(rg); + await tx.wtx.upsertRefreshGroup(rg); }); break; default: @@ -935,28 +935,28 @@ async function handleRefreshMeltConflict( } if (Amounts.isZero(historyJson.balance)) { rg.statusPerCoin[coinIndex] = RefreshCoinStatus.Failed; - const refreshSession = await tx.refreshSessions.get([ + const refreshSession = await tx.wtx.getRefreshSession( ctx.refreshGroupId, coinIndex, - ]); + ); if (!refreshSession) { throw Error("db invariant failed: missing refresh session in database"); } refreshSession.lastError = errDetails; - await tx.refreshSessions.put(refreshSession); + await tx.wtx.upsertRefreshSession(refreshSession); await h.update(rg); } else { // Try again with new denoms! rg.inputPerCoin[coinIndex] = historyJson.balance; - const refreshSession = await tx.refreshSessions.get([ + const refreshSession = await tx.wtx.getRefreshSession( ctx.refreshGroupId, coinIndex, - ]); + ); if (!refreshSession) { throw Error("db invariant failed: missing refresh session in database"); } await destroyRefreshSession(ctx.wex, tx, rg, refreshSession); - await tx.refreshSessions.delete([ctx.refreshGroupId, coinIndex]); + await tx.wtx.deleteRefreshSession(ctx.refreshGroupId, coinIndex); await initRefreshSession(ctx.wex, tx, rg, coinIndex); } }); @@ -989,15 +989,15 @@ async function handleRefreshMeltNotFound( return; } rg.statusPerCoin[coinIndex] = RefreshCoinStatus.Failed; - const refreshSession = await tx.refreshSessions.get([ + const refreshSession = await tx.wtx.getRefreshSession( ctx.refreshGroupId, coinIndex, - ]); + ); if (!refreshSession) { throw Error("db invariant failed: missing refresh session in database"); } refreshSession.lastError = errDetails; - await tx.refreshSessions.put(refreshSession); + await tx.wtx.upsertRefreshSession(refreshSession); await destroyRefreshSession(ctx.wex, tx, rg, refreshSession); await h.update(rg); }); @@ -1013,14 +1013,14 @@ async function refreshReveal( ); const ctx = new RefreshTransactionContext(wex, refreshGroupId); const d = await wex.runLegacyWalletDbTx(async (tx) => { - const refreshGroup = await tx.refreshGroups.get(refreshGroupId); + const refreshGroup = await tx.wtx.getRefreshGroup(refreshGroupId); if (!refreshGroup) { return; } - const refreshSession = await tx.refreshSessions.get([ + const refreshSession = await tx.wtx.getRefreshSession( refreshGroupId, coinIndex, - ]); + ); if (!refreshSession) { return; } @@ -1186,7 +1186,7 @@ async function refreshReveal( if (rg.statusPerCoin[coinIndex] !== RefreshCoinStatus.Pending) { return; } - const rs = await tx.refreshSessions.get([refreshGroupId, coinIndex]); + const rs = await tx.wtx.getRefreshSession(refreshGroupId, coinIndex); if (!rs) { return; } @@ -1241,16 +1241,16 @@ async function handleRefreshRevealError( return; } rg.statusPerCoin[coinIndex] = RefreshCoinStatus.Failed; - const refreshSession = await tx.refreshSessions.get([ + const refreshSession = await tx.wtx.getRefreshSession( ctx.refreshGroupId, coinIndex, - ]); + ); if (!refreshSession) { throw Error("db invariant failed: missing refresh session in database"); } refreshSession.lastError = errDetails; await destroyRefreshSession(ctx.wex, tx, rg, refreshSession); - await tx.refreshSessions.put(refreshSession); + await tx.wtx.upsertRefreshSession(refreshSession); await h.update(rg); }); } @@ -1266,7 +1266,7 @@ export async function processRefreshGroup( logger.trace(`processing refresh group ${refreshGroupId}`); const refreshGroup = await wex.runLegacyWalletDbTx(async (tx) => - tx.refreshGroups.get(refreshGroupId), + tx.wtx.getRefreshGroup(refreshGroupId), ); if (!refreshGroup) { return TaskRunResult.finished(); @@ -1420,17 +1420,17 @@ async function processRefreshSession( ); let { refreshGroup, refreshSession } = await wex.runLegacyWalletDbTx( async (tx) => { - const rg = await tx.refreshGroups.get(refreshGroupId); - const rs = await tx.refreshSessions.get([refreshGroupId, coinIndex]); + const rg = await tx.wtx.getRefreshGroup(refreshGroupId); + const rs = await tx.wtx.getRefreshSession(refreshGroupId, coinIndex); if ( rg != null && rg.statusPerCoin[coinIndex] === RefreshCoinStatus.PendingRedenominate ) { - await tx.refreshSessions.delete([refreshGroupId, coinIndex]); + await tx.wtx.deleteRefreshSession(refreshGroupId, coinIndex); await initRefreshSession(wex, tx, rg, coinIndex); rg.statusPerCoin[coinIndex] = RefreshCoinStatus.Pending; - await tx.refreshGroups.put(rg); + await tx.wtx.upsertRefreshGroup(rg); } return { @@ -1457,7 +1457,7 @@ async function processRefreshSession( export interface RefreshOutputInfo { outputPerCoin: AmountJson[]; - perExchangeInfo: Record<string, RefreshGroupPerExchangeInfo>; + perExchangeInfo: Record<string, WalletRefreshGroupPerExchangeInfo>; } export async function calculateRefreshOutput( @@ -1468,7 +1468,7 @@ export async function calculateRefreshOutput( ): Promise<RefreshOutputInfo> { const estimatedOutputPerCoin: AmountJson[] = []; - const infoPerExchange: Record<string, RefreshGroupPerExchangeInfo> = {}; + const infoPerExchange: Record<string, WalletRefreshGroupPerExchangeInfo> = {}; for (const ocp of oldCoinPubs) { const coin = await tx.coins.get(ocp.coinPub); @@ -1648,7 +1648,7 @@ export async function createRefreshGroup( } } - const refreshGroup: RefreshGroupRecord = { + const refreshGroup: WalletRefreshGroup = { operationStatus: RefreshOperationStatus.Pending, currency, timestampFinished: undefined, @@ -1680,7 +1680,7 @@ export async function createRefreshGroup( const ctx = new RefreshTransactionContext(wex, refreshGroupId); - await tx.refreshGroups.put(refreshGroup); + await tx.wtx.upsertRefreshGroup(refreshGroup); await ctx.updateTransactionMeta(tx); const newTxState = computeRefreshTransactionState(refreshGroup); @@ -1778,7 +1778,7 @@ async function redenominateRefresh( } export function computeRefreshTransactionState( - rg: RefreshGroupRecord, + rg: WalletRefreshGroup, ): TransactionState { switch (rg.operationStatus) { case RefreshOperationStatus.Finished: @@ -1813,7 +1813,7 @@ export function computeRefreshTransactionState( } export function computeRefreshTransactionActions( - rg: RefreshGroupRecord, + rg: WalletRefreshGroup, ): TransactionAction[] { switch (rg.operationStatus) { case RefreshOperationStatus.Finished: @@ -1839,7 +1839,7 @@ export function getRefreshesForTransaction( ): Promise<string[]> { return wex.runLegacyWalletDbTx(async (tx) => { const groups = - await tx.refreshGroups.indexes.byOriginatingTransactionId.getAll( + await tx.wtx.getRefreshGroupsByOriginatingTransaction( transactionId, ); return groups.map((x) => @@ -1907,7 +1907,7 @@ export async function waitRefreshFinal( // Check if refresh is final const res = await ctx.wex.runLegacyWalletDbTx(async (tx) => { return { - rg: await tx.refreshGroups.get(ctx.refreshGroupId), + rg: await tx.wtx.getRefreshGroup(ctx.refreshGroupId), }; }); const { rg } = res;