taler-typescript-core

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

commit 34b2d212ca47409358764bfc12bf2045523ad66f
parent b381cce803bf456f83290b06ead4e33dd8e58744
Author: Florian Dold <dold@taler.net>
Date:   Sun, 19 Jul 2026 00:02:31 +0200

db: add denomination family and base-URL fixup accessors

Diffstat:
Mpackages/taler-wallet-core/src/db-common.ts | 33+++++++++++++++++++++++++++++++++
Mpackages/taler-wallet-core/src/db-indexeddb.ts | 45++++++++-------------------------------------
Mpackages/taler-wallet-core/src/dbtx-indexeddb.ts | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackages/taler-wallet-core/src/dbtx.ts | 38++++++++++++++++++++++++++++++++++++++
Mpackages/taler-wallet-core/src/exchanges.ts | 52+++++++++++++++++++++++-----------------------------
Mpackages/taler-wallet-core/src/withdraw.ts | 6+++---
6 files changed, 187 insertions(+), 69 deletions(-)

diff --git a/packages/taler-wallet-core/src/db-common.ts b/packages/taler-wallet-core/src/db-common.ts @@ -1347,6 +1347,39 @@ export interface WalletExchangeSignkeys { exchangeDetailsRowId: number; } +export interface WalletDenomFamilyParams { + exchangeBaseUrl: string; + exchangeMasterPub: string; + value: AmountString; + feeWithdraw: AmountString; + feeDeposit: AmountString; + feeRefresh: AmountString; + feeRefund: AmountString; +} + +export interface WalletDenominationFamily { + denominationFamilySerial?: number; + familyParams: WalletDenomFamilyParams; + + // Reserved legacy fields: + // * familyParamsHash +} + +export interface WalletExchangeBaseUrlFixup { + exchangeBaseUrl: string; + replacement: string; +} + +export interface WalletExchangeMigrationLog { + oldExchangeBaseUrl: string; + newExchangeBaseUrl: string; + timestamp: DbPreciseTimestamp; + /** + * Reason that triggered the exchange base URL migration. + */ + reason: ExchangeMigrationReason; +} + 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 @@ -144,6 +144,10 @@ import { WalletSlate, WalletDenomLossEvent, WalletExchangeSignkeys, + WalletDenomFamilyParams, + WalletDenominationFamily, + WalletExchangeBaseUrlFixup, + WalletExchangeMigrationLog, WalletToken, TokenFamilyInfo, WalletRefundGroup, @@ -256,24 +260,6 @@ export const CURRENT_DB_CONFIG_KEY = "currentMainDbName"; */ export const WALLET_DB_MINOR_VERSION = 29; -export interface DenomFamilyParams { - exchangeBaseUrl: string; - exchangeMasterPub: string; - value: AmountString; - feeWithdraw: AmountString; - feeDeposit: AmountString; - feeRefresh: AmountString; - feeRefund: AmountString; -} - -export interface DenominationFamilyRecord { - denominationFamilySerial?: number; - familyParams: DenomFamilyParams; - - // Reserved legacy fields: - // * familyParamsHash -} - // FIXME: Should these be numeric codes? export type KycUserType = "individual" | "business"; @@ -587,21 +573,6 @@ export interface CurrencyInfoRecord { source: "exchange" | "user" | "preset"; } -export interface ExchangeMigrationLogRecord { - oldExchangeBaseUrl: string; - newExchangeBaseUrl: string; - timestamp: DbPreciseTimestamp; - /** - * Reason that triggered the exchange base URL migration. - */ - reason: ExchangeMigrationReason; -} - -export interface ExchangeBaseUrlFixupRecord { - exchangeBaseUrl: string; - replacement: string; -} - export interface ContactRecord { /** * The mailbox URI of this contact @@ -640,14 +611,14 @@ export interface ContactRecord { */ export const WalletIndexedDbStoresV1 = { exchangeBaseUrlMigrationLog: describeStoreV2({ - recordCodec: passthroughCodec<ExchangeMigrationLogRecord>(), + recordCodec: passthroughCodec<WalletExchangeMigrationLog>(), storeName: "exchangeBaseUrlMigrationLog", keyPath: ["oldExchangeBaseUrl", "newExchangeBaseUrl"], versionAdded: 18, indexes: {}, }), exchangeBaseUrlFixups: describeStoreV2({ - recordCodec: passthroughCodec<ExchangeBaseUrlFixupRecord>(), + recordCodec: passthroughCodec<WalletExchangeBaseUrlFixup>(), storeName: "exchangeBaseUrlFixups", keyPath: "exchangeBaseUrl", versionAdded: 19, @@ -868,7 +839,7 @@ export const WalletIndexedDbStoresV1 = { ), denominationFamilies: describeStore( "denominationFamilies", - describeContents<DenominationFamilyRecord>({ + describeContents<WalletDenominationFamily>({ keyPath: "denominationFamilySerial", versionAdded: 27, autoIncrement: true, @@ -1708,7 +1679,7 @@ async function fixup20260203DenomFamilyMigration( ); for (const r of batch) { - const fp: DenomFamilyParams = { + const fp: WalletDenomFamilyParams = { exchangeBaseUrl: r.exchangeBaseUrl, exchangeMasterPub: r.exchangeMasterPub, feeDeposit: r.fees.feeDeposit, diff --git a/packages/taler-wallet-core/src/dbtx-indexeddb.ts b/packages/taler-wallet-core/src/dbtx-indexeddb.ts @@ -74,6 +74,10 @@ import { WalletExchangeEntry, WalletDenomLossEvent, WalletExchangeSignkeys, + WalletDenomFamilyParams, + WalletDenominationFamily, + WalletExchangeBaseUrlFixup, + WalletExchangeMigrationLog, WalletExchangeDetails, } from "./db-common.js"; import type { @@ -350,6 +354,84 @@ export class IdbWalletTransaction implements WalletDbTransaction { }); } + async getDenominationFamilyByParams( + params: WalletDenomFamilyParams, + ): Promise<WalletDenominationFamily | undefined> { + const tx = this.tx; + // The byFamilyParms index key is a 7-component array whose component + // order is part of the schema; it is built here so exactly one place + // knows it. + return await tx.denominationFamilies.indexes.byFamilyParms.get([ + params.exchangeBaseUrl, + params.exchangeMasterPub, + params.value, + params.feeWithdraw, + params.feeDeposit, + params.feeRefresh, + params.feeRefund, + ]); + } + + async upsertDenominationFamily( + rec: WalletDenominationFamily, + ): Promise<number> { + const tx = this.tx; + const res = await tx.denominationFamilies.put(rec); + checkDbInvariant( + typeof res.key === "number", + "denomination family serial must be a number", + ); + return res.key; + } + + async getDenominationFamiliesByExchange( + exchangeBaseUrl: string, + ): Promise<WalletDenominationFamily[]> { + const tx = this.tx; + return await tx.denominationFamilies.indexes.byExchangeBaseUrl.getAll( + exchangeBaseUrl, + ); + } + + async deleteDenominationFamily( + denominationFamilySerial: number, + ): Promise<void> { + const tx = this.tx; + await tx.denominationFamilies.delete(denominationFamilySerial); + } + + async getExchangeBaseUrlFixup( + exchangeBaseUrl: string, + ): Promise<WalletExchangeBaseUrlFixup | undefined> { + const tx = this.tx; + return await tx.exchangeBaseUrlFixups.get(exchangeBaseUrl); + } + + async upsertExchangeBaseUrlFixup( + rec: WalletExchangeBaseUrlFixup, + ): Promise<void> { + const tx = this.tx; + await tx.exchangeBaseUrlFixups.put(rec); + } + + async getExchangeMigrationLog( + oldExchangeBaseUrl: string, + newExchangeBaseUrl: string, + ): Promise<WalletExchangeMigrationLog | undefined> { + const tx = this.tx; + return await tx.exchangeBaseUrlMigrationLog.get([ + oldExchangeBaseUrl, + newExchangeBaseUrl, + ]); + } + + async upsertExchangeMigrationLog( + rec: WalletExchangeMigrationLog, + ): Promise<void> { + const tx = this.tx; + await tx.exchangeBaseUrlMigrationLog.put(rec); + } + async getExchangeDetailsByPointer( exchangeBaseUrl: string, currency: string, diff --git a/packages/taler-wallet-core/src/dbtx.ts b/packages/taler-wallet-core/src/dbtx.ts @@ -74,6 +74,10 @@ import { WalletExchangeEntry, WalletDenomLossEvent, WalletExchangeSignkeys, + WalletDenomFamilyParams, + WalletDenominationFamily, + WalletExchangeBaseUrlFixup, + WalletExchangeMigrationLog, WalletExchangeDetails, } from "./db-common.js"; export interface GetCurrencyInfoDbResult { @@ -244,6 +248,40 @@ export interface WalletDbTransaction { /** * Get exchange details by the (baseUrl, currency, masterPub) pointer. */ + /** + * Look up a denomination family by its parameters. + */ + getDenominationFamilyByParams( + params: WalletDenomFamilyParams, + ): Promise<WalletDenominationFamily | undefined>; + + /** + * Create or update a denomination family, returning its serial. + * + * The denominationFamilies store is auto-incrementing on + * denominationFamilySerial. + */ + upsertDenominationFamily(rec: WalletDenominationFamily): Promise<number>; + + getDenominationFamiliesByExchange( + exchangeBaseUrl: string, + ): Promise<WalletDenominationFamily[]>; + + deleteDenominationFamily(denominationFamilySerial: number): Promise<void>; + + getExchangeBaseUrlFixup( + exchangeBaseUrl: string, + ): Promise<WalletExchangeBaseUrlFixup | undefined>; + + upsertExchangeBaseUrlFixup(rec: WalletExchangeBaseUrlFixup): Promise<void>; + + getExchangeMigrationLog( + oldExchangeBaseUrl: string, + newExchangeBaseUrl: string, + ): Promise<WalletExchangeMigrationLog | undefined>; + + upsertExchangeMigrationLog(rec: WalletExchangeMigrationLog): Promise<void>; + getExchangeDetailsByPointer( exchangeBaseUrl: string, currency: string, diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts @@ -153,10 +153,10 @@ import { WalletExchangeDetails, WalletExchangeEntry, WalletDenomLossEvent, + WalletDenomFamilyParams, + WalletDenominationFamily, } from "./db-common.js"; import { - DenomFamilyParams, - DenominationFamilyRecord, ExchangeMigrationReason, } from "./db-indexeddb.js"; import { @@ -1015,7 +1015,7 @@ export async function startUpdateExchangeEntry( ); await wex.runLegacyWalletDbTx(async (tx) => { - const rec = await tx.exchangeBaseUrlFixups.get(exchangeBaseUrl); + const rec = await tx.wtx.getExchangeBaseUrlFixup(exchangeBaseUrl); if (rec) { logger.warn( `using replacement ${rec.replacement} for ${exchangeBaseUrl}`, @@ -1296,7 +1296,7 @@ export async function waitReadyExchange( logger.trace(`waiting for exchange ${exchangeBaseUrl} to become ready`); await wex.runLegacyWalletDbTx(async (tx) => { - const rec = await tx.exchangeBaseUrlFixups.get(exchangeBaseUrl); + const rec = await tx.wtx.getExchangeBaseUrlFixup(exchangeBaseUrl); if (rec) { logger.warn( `using replacement ${rec.replacement} for ${exchangeBaseUrl}`, @@ -1851,7 +1851,7 @@ export async function updateExchangeFromUrlHandler( rsa_public_key: denom.rsa_pub, }; const denomPubHash = encodeCrock(hashDenomPub(denomPub)); - const fp: DenomFamilyParams = { + const fp: WalletDenomFamilyParams = { exchangeBaseUrl: exchangeBaseUrl, exchangeMasterPub: keysInfo.master_public_key, feeDeposit: denomFamily.fee_deposit, @@ -2041,22 +2041,19 @@ export async function updateExchangeFromUrlHandler( for (const currentDenom of denomInfos) { // FIXME: Check if we really already need the denomination. - const familyParamsIndexKey = [ - currentDenom.exchangeBaseUrl, - currentDenom.exchangeMasterPub, - currentDenom.value, - currentDenom.feeWithdraw, - currentDenom.feeDeposit, - currentDenom.feeRefresh, - currentDenom.feeRefund, - ]; - let fpRec: DenominationFamilyRecord | undefined = - await tx.denominationFamilies.indexes.byFamilyParms.get( - familyParamsIndexKey, - ); + let fpRec: WalletDenominationFamily | undefined = + await tx.wtx.getDenominationFamilyByParams({ + exchangeBaseUrl: currentDenom.exchangeBaseUrl, + exchangeMasterPub: currentDenom.exchangeMasterPub, + value: currentDenom.value, + feeWithdraw: currentDenom.feeWithdraw, + feeDeposit: currentDenom.feeDeposit, + feeRefresh: currentDenom.feeRefresh, + feeRefund: currentDenom.feeRefund, + }); let denominationFamilySerial; if (fpRec == null) { - const fp: DenomFamilyParams = { + const fp: WalletDenomFamilyParams = { exchangeBaseUrl: exchangeBaseUrl, exchangeMasterPub: keysInfo.master_public_key, feeDeposit: currentDenom.feeDeposit, @@ -2068,8 +2065,8 @@ export async function updateExchangeFromUrlHandler( fpRec = { familyParams: fp, }; - const insRes = await tx.denominationFamilies.put(fpRec); - denominationFamilySerial = insRes.key; + denominationFamilySerial = + await tx.wtx.upsertDenominationFamily(fpRec); } else { denominationFamilySerial = fpRec.denominationFamilySerial; } @@ -3214,7 +3211,7 @@ async function purgeExchange( { const denomFamilyRecs = - await tx.denominationFamilies.indexes.byExchangeBaseUrl.getAll( + await tx.wtx.getDenominationFamiliesByExchange( exchangeBaseUrl, ); for (const rec of denomFamilyRecs) { @@ -3222,7 +3219,7 @@ async function purgeExchange( rec.denominationFamilySerial != null, "denominationFamilySerial", ); - await tx.denominationFamilies.delete(rec.denominationFamilySerial); + await tx.wtx.deleteDenominationFamily(rec.denominationFamilySerial); } } @@ -4157,10 +4154,7 @@ export async function migrateExchange( req: MigrateExchangeRequest, ): Promise<void> { await wex.runLegacyWalletDbTx(async (tx) => { - const migrationRec = await tx.exchangeBaseUrlMigrationLog.get([ - req.oldExchangeBaseUrl, - req.newExchangeBaseUrl, - ]); + const migrationRec = await tx.wtx.getExchangeMigrationLog(req.oldExchangeBaseUrl, req.newExchangeBaseUrl); if (migrationRec) { logger.warn( `exchange ${migrationRec.oldExchangeBaseUrl} already migrated`, @@ -4176,14 +4170,14 @@ export async function migrateExchange( let existingNewExchangeSt: ExchangeEntryState | undefined = undefined; - await tx.exchangeBaseUrlMigrationLog.put({ + await tx.wtx.upsertExchangeMigrationLog({ oldExchangeBaseUrl: req.oldExchangeBaseUrl, newExchangeBaseUrl: req.newExchangeBaseUrl, timestamp: timestampPreciseToDb(TalerPreciseTimestamp.now()), reason: req.trigger, }); - await tx.exchangeBaseUrlFixups.put({ + await tx.wtx.upsertExchangeBaseUrlFixup({ exchangeBaseUrl: req.oldExchangeBaseUrl, replacement: req.newExchangeBaseUrl, }); diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts @@ -1279,7 +1279,7 @@ export async function getWithdrawableDenomsTx( ): Promise<WalletDenomination[]> { const dbNow = timestampProtocolToDb(TalerProtocolTimestamp.now()); const allFamilies = - await tx.denominationFamilies.indexes.byExchangeBaseUrl.getAll( + await tx.wtx.getDenominationFamiliesByExchange( exchangeBaseUrl, ); if (logger.shouldLogTrace()) { @@ -2012,7 +2012,7 @@ export async function updateWithdrawalDenomsForExchange( const denoms = await wex.runLegacyWalletDbTx(async (tx) => { const allFamilies = - await tx.denominationFamilies.indexes.byExchangeBaseUrl.getAll( + await tx.wtx.getDenominationFamiliesByExchange( exchangeBaseUrl, ); const denominations: WalletDenomination[] | undefined = []; @@ -3828,7 +3828,7 @@ export async function confirmWithdrawal( } await wex.runLegacyWalletDbTx(async (tx) => { - const rec = await tx.exchangeBaseUrlFixups.get(selectedExchange); + const rec = await tx.wtx.getExchangeBaseUrlFixup(selectedExchange); if (rec) { selectedExchange = rec.replacement; }