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:
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;
}