taler-typescript-core

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

commit 540cd68e6ef2d56559d91657ecde3b789a441119
parent f6b59b8b96fcdd4d87fb1e76f32f6b03dcb506be
Author: Florian Dold <dold@taler.net>
Date:   Thu,  6 Aug 2026 19:02:40 +0200

wallet: look up denomination families once per exchange update

An exchange has a handful of families but lists a denomination per family and
rotation period, so querying per denomination was a query per row of /keys.

Issue: https://bugs.taler.net/n/11334

Diffstat:
Mpackages/taler-wallet-core/src/exchanges.ts | 86++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
1 file changed, 63 insertions(+), 23 deletions(-)

diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts @@ -952,6 +952,27 @@ async function checkExchangeEntryOutdated( } /** + * Key identifying a denomination family, for looking one up without a query + * per denomination. Mirrors the unique index the DB layer has on these + * columns; amounts are canonical strings, so comparing them as text is what + * the query would do anyway. + * + * Joined on a character none of the components can contain, so that two + * different tuples cannot spell the same key. + */ +function denomFamilyParamsKey(p: WalletDenomFamilyParams): string { + return [ + p.exchangeBaseUrl, + p.exchangeMasterPub, + p.value, + p.feeWithdraw, + p.feeDeposit, + p.feeRefresh, + p.feeRefund, + ].join("\u0000"); +} + +/** * Timestamp (in seconds) to ask the exchange to cherry-pick /keys by, or * undefined when the whole response should be downloaded. * @@ -2143,33 +2164,54 @@ export async function updateExchangeFromUrlHandler( logger.trace("updating denominations in database"); + // One lookup for all of them: an exchange has a handful of families but + // its /keys lists a denomination per family and rotation period, so a + // query per denomination is a query per row of the response. + const familySerialByParams = new Map<string, number>(); + for (const fam of await tx.getDenominationFamiliesByExchange( + exchangeBaseUrl, + )) { + checkDbInvariant( + typeof fam.denominationFamilySerial === "number", + "denominationFamilySerial", + ); + familySerialByParams.set( + denomFamilyParamsKey(fam.familyParams), + fam.denominationFamilySerial, + ); + } + for (const currentDenom of denomInfos) { - // FIXME: Check if we really already need the denomination. - let fpRec: WalletDenominationFamily | undefined = - await tx.getDenominationFamilyByParams({ - exchangeBaseUrl: currentDenom.exchangeBaseUrl, - exchangeMasterPub: currentDenom.exchangeMasterPub, - value: currentDenom.value, - feeWithdraw: currentDenom.feeWithdraw, - feeDeposit: currentDenom.feeDeposit, - feeRefresh: currentDenom.feeRefresh, - feeRefund: currentDenom.feeRefund, - }); + const familyParams: WalletDenomFamilyParams = { + exchangeBaseUrl: currentDenom.exchangeBaseUrl, + exchangeMasterPub: currentDenom.exchangeMasterPub, + value: currentDenom.value, + feeWithdraw: currentDenom.feeWithdraw, + feeDeposit: currentDenom.feeDeposit, + feeRefresh: currentDenom.feeRefresh, + feeRefund: currentDenom.feeRefund, + }; + let fpRec: WalletDenominationFamily | undefined = undefined; + const knownSerial = familySerialByParams.get( + denomFamilyParamsKey(familyParams), + ); + if (knownSerial != null) { + fpRec = { familyParams, denominationFamilySerial: knownSerial }; + } let denominationFamilySerial; if (fpRec == null) { - const fp: WalletDenomFamilyParams = { - exchangeBaseUrl: exchangeBaseUrl, - exchangeMasterPub: keysInfo.master_public_key, - feeDeposit: currentDenom.feeDeposit, - feeRefresh: currentDenom.feeRefresh, - feeRefund: currentDenom.feeRefund, - feeWithdraw: currentDenom.feeWithdraw, - value: currentDenom.value, - }; fpRec = { - familyParams: fp, + familyParams, }; denominationFamilySerial = await tx.upsertDenominationFamily(fpRec); + checkDbInvariant( + typeof denominationFamilySerial === "number", + "denominationFamilySerial", + ); + familySerialByParams.set( + denomFamilyParamsKey(familyParams), + denominationFamilySerial, + ); } else { denominationFamilySerial = fpRec.denominationFamilySerial; } @@ -2179,8 +2221,6 @@ export async function updateExchangeFromUrlHandler( "denominationFamilySerial", ); - // First, find denom family - const denomRec: WalletDenomination = { currency: keysInfo.currency, denominationFamilySerial,