commit a37d90bf6fe6286caa6b406b854d72cd5a901445
parent af6c5877c41937eb580b2090ddedfde4212302b2
Author: Florian Dold <dold@taler.net>
Date: Sun, 19 Jul 2026 00:09:16 +0200
db: move remaining exchanges.ts access behind the DAL
Diffstat:
5 files changed, 162 insertions(+), 98 deletions(-)
diff --git a/packages/taler-wallet-core/src/db-common.ts b/packages/taler-wallet-core/src/db-common.ts
@@ -1380,6 +1380,20 @@ export interface WalletExchangeMigrationLog {
reason: ExchangeMigrationReason;
}
+export interface WalletGlobalCurrencyAuditor {
+ id?: number;
+ currency: string;
+ auditorBaseUrl: string;
+ auditorPub: string;
+}
+
+export interface WalletGlobalCurrencyExchange {
+ id?: number;
+ currency: string;
+ exchangeBaseUrl: string;
+ exchangeMasterPub: string;
+}
+
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
@@ -148,6 +148,8 @@ import {
WalletDenominationFamily,
WalletExchangeBaseUrlFixup,
WalletExchangeMigrationLog,
+ WalletGlobalCurrencyAuditor,
+ WalletGlobalCurrencyExchange,
WalletToken,
TokenFamilyInfo,
WalletRefundGroup,
@@ -542,20 +544,6 @@ export function passthroughCodec<T>(): Codec<T> {
return codecForAny();
}
-export interface GlobalCurrencyAuditorRecord {
- id?: number;
- currency: string;
- auditorBaseUrl: string;
- auditorPub: string;
-}
-
-export interface GlobalCurrencyExchangeRecord {
- id?: number;
- currency: string;
- exchangeBaseUrl: string;
- exchangeMasterPub: string;
-}
-
export interface CurrencyInfoRecord {
/**
* Stringified scope info.
@@ -666,7 +654,7 @@ export const WalletIndexedDbStoresV1 = {
versionAdded: 12,
}),
globalCurrencyAuditors: describeStoreV2({
- recordCodec: passthroughCodec<GlobalCurrencyAuditorRecord>(),
+ recordCodec: passthroughCodec<WalletGlobalCurrencyAuditor>(),
storeName: "globalCurrencyAuditors",
keyPath: "id",
autoIncrement: true,
@@ -683,7 +671,7 @@ export const WalletIndexedDbStoresV1 = {
},
}),
globalCurrencyExchanges: describeStoreV2({
- recordCodec: passthroughCodec<GlobalCurrencyExchangeRecord>(),
+ recordCodec: passthroughCodec<WalletGlobalCurrencyExchange>(),
storeName: "globalCurrencyExchanges",
keyPath: "id",
autoIncrement: true,
diff --git a/packages/taler-wallet-core/src/dbtx-indexeddb.ts b/packages/taler-wallet-core/src/dbtx-indexeddb.ts
@@ -78,6 +78,8 @@ import {
WalletDenominationFamily,
WalletExchangeBaseUrlFixup,
WalletExchangeMigrationLog,
+ WalletGlobalCurrencyExchange,
+ WalletGlobalCurrencyAuditor,
WalletExchangeDetails,
} from "./db-common.js";
import type {
@@ -354,6 +356,55 @@ export class IdbWalletTransaction implements WalletDbTransaction {
});
}
+ async countWithdrawalGroupsByExchange(
+ exchangeBaseUrl: string,
+ ): Promise<number> {
+ const tx = this.tx;
+ return await tx.withdrawalGroups.indexes.byExchangeBaseUrl.count(
+ exchangeBaseUrl,
+ );
+ }
+
+ async getWithdrawalGroupsByExchangeForRekey(
+ exchangeBaseUrl: string,
+ ): Promise<WalletWithdrawalGroup[]> {
+ const tx = this.tx;
+ return await tx.withdrawalGroups.indexes.byExchangeBaseUrl.getAll(
+ exchangeBaseUrl,
+ );
+ }
+
+ async getGlobalCurrencyExchange(
+ currency: string,
+ exchangeBaseUrl: string,
+ exchangeMasterPub: string,
+ ): Promise<WalletGlobalCurrencyExchange | undefined> {
+ const tx = this.tx;
+ return await tx.globalCurrencyExchanges.indexes.byCurrencyAndUrlAndPub.get([
+ currency,
+ exchangeBaseUrl,
+ exchangeMasterPub,
+ ]);
+ }
+
+ async getGlobalCurrencyAuditor(
+ currency: string,
+ auditorBaseUrl: string,
+ auditorPub: string,
+ ): Promise<WalletGlobalCurrencyAuditor | undefined> {
+ const tx = this.tx;
+ return await tx.globalCurrencyAuditors.indexes.byCurrencyAndUrlAndPub.get([
+ currency,
+ auditorBaseUrl,
+ auditorPub,
+ ]);
+ }
+
+ async listAllDenomLossEvents(): Promise<WalletDenomLossEvent[]> {
+ const tx = this.tx;
+ return await tx.denomLossEvents.iter().toArray();
+ }
+
async getCoinsByExchange(exchangeBaseUrl: string): Promise<WalletCoin[]> {
const tx = this.tx;
return await tx.coins.indexes.byBaseUrl.getAll(exchangeBaseUrl);
diff --git a/packages/taler-wallet-core/src/dbtx.ts b/packages/taler-wallet-core/src/dbtx.ts
@@ -78,6 +78,8 @@ import {
WalletDenominationFamily,
WalletExchangeBaseUrlFixup,
WalletExchangeMigrationLog,
+ WalletGlobalCurrencyExchange,
+ WalletGlobalCurrencyAuditor,
WalletExchangeDetails,
} from "./db-common.js";
export interface GetCurrencyInfoDbResult {
@@ -251,6 +253,26 @@ export interface WalletDbTransaction {
/**
* Look up a denomination family by its parameters.
*/
+ countWithdrawalGroupsByExchange(exchangeBaseUrl: string): Promise<number>;
+
+ getWithdrawalGroupsByExchangeForRekey(
+ exchangeBaseUrl: string,
+ ): Promise<WalletWithdrawalGroup[]>;
+
+ getGlobalCurrencyExchange(
+ currency: string,
+ exchangeBaseUrl: string,
+ exchangeMasterPub: string,
+ ): Promise<WalletGlobalCurrencyExchange | undefined>;
+
+ getGlobalCurrencyAuditor(
+ currency: string,
+ auditorBaseUrl: string,
+ auditorPub: string,
+ ): Promise<WalletGlobalCurrencyAuditor | undefined>;
+
+ listAllDenomLossEvents(): Promise<WalletDenomLossEvent[]>;
+
getCoinsByExchange(exchangeBaseUrl: string): Promise<WalletCoin[]>;
countCoinsByExchange(exchangeBaseUrl: string): Promise<number>;
diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts
@@ -261,7 +261,7 @@ export async function getScopeForAllCoins(
): Promise<ScopeInfo[]> {
let exchangeSet = new Set<string>();
for (const pub of coinPubs) {
- const coin = await tx.coins.get(pub);
+ const coin = await tx.wtx.getCoin(pub);
if (!coin) {
logger.warn(`coin ${coinPubs} not found, unable to compute full scope`);
continue;
@@ -326,11 +326,7 @@ async function internalGetExchangeScopeInfo(
exchangeDetails: WalletExchangeDetails,
): Promise<ScopeInfo> {
const globalExchangeRec =
- await tx.globalCurrencyExchanges.indexes.byCurrencyAndUrlAndPub.get([
- exchangeDetails.currency,
- exchangeDetails.exchangeBaseUrl,
- exchangeDetails.masterPublicKey,
- ]);
+ await tx.wtx.getGlobalCurrencyExchange(exchangeDetails.currency, exchangeDetails.exchangeBaseUrl, exchangeDetails.masterPublicKey);
if (globalExchangeRec) {
return {
currency: exchangeDetails.currency,
@@ -339,11 +335,7 @@ async function internalGetExchangeScopeInfo(
} else {
for (const aud of exchangeDetails.auditors) {
const globalAuditorRec =
- await tx.globalCurrencyAuditors.indexes.byCurrencyAndUrlAndPub.get([
- exchangeDetails.currency,
- aud.auditor_url,
- aud.auditor_pub,
- ]);
+ await tx.wtx.getGlobalCurrencyAuditor(exchangeDetails.currency, aud.auditor_url, aud.auditor_pub);
if (globalAuditorRec) {
return {
currency: exchangeDetails.currency,
@@ -2211,9 +2203,7 @@ async function doExchangeAutoRefresh(
if (!exchange || !exchange.detailsPointer) {
return;
}
- const coins = await tx.coins.indexes.byBaseUrl
- .iter(exchangeBaseUrl)
- .toArray();
+ const coins = await tx.wtx.getCoinsByExchange(exchangeBaseUrl);
const refreshCoins: CoinRefreshRequest[] = [];
for (const coin of coins) {
if (coin.status !== CoinStatus.Fresh) {
@@ -2323,7 +2313,7 @@ async function handleDenomLoss(
exchangeBaseUrl: string,
): Promise<void> {
const coinAvailabilityRecs =
- await tx.coinAvailability.indexes.byExchangeBaseUrl.getAll(exchangeBaseUrl);
+ await tx.wtx.getCoinAvailabilityByExchange(exchangeBaseUrl);
const denomsVanished: string[] = [];
const denomsUnoffered: string[] = [];
const denomsExpired: string[] = [];
@@ -2344,7 +2334,7 @@ async function handleDenomLoss(
// Remove availability
coinAv.freshCoinCount = 0;
coinAv.visibleCoinCount = 0;
- await tx.coinAvailability.put(coinAv);
+ await tx.wtx.upsertCoinAvailability(coinAv);
denomsVanished.push(coinAv.denomPubHash);
const total = Amount.from(coinAv.value).mult(n);
amountVanished = amountVanished.add(total);
@@ -2352,7 +2342,7 @@ async function handleDenomLoss(
// Remove availability
coinAv.freshCoinCount = 0;
coinAv.visibleCoinCount = 0;
- await tx.coinAvailability.put(coinAv);
+ await tx.wtx.upsertCoinAvailability(coinAv);
denomsUnoffered.push(coinAv.denomPubHash);
const total = Amount.from(coinAv.value).mult(n);
amountUnoffered = amountUnoffered.add(total);
@@ -2363,7 +2353,7 @@ async function handleDenomLoss(
// Remove availability
coinAv.freshCoinCount = 0;
coinAv.visibleCoinCount = 0;
- await tx.coinAvailability.put(coinAv);
+ await tx.wtx.upsertCoinAvailability(coinAv);
denomsExpired.push(coinAv.denomPubHash);
const total = Amount.from(coinAv.value).mult(n);
amountExpired = amountExpired.add(total);
@@ -2374,15 +2364,13 @@ async function handleDenomLoss(
logger.warn(`denomination ${coinAv.denomPubHash} is a loss`);
- const coins = await tx.coins.indexes.byDenomPubHash.getAll(
- coinAv.denomPubHash,
- );
+ const coins = await tx.wtx.getCoinsByDenomPubHash(coinAv.denomPubHash);
for (const coin of coins) {
switch (coin.status) {
case CoinStatus.Fresh:
case CoinStatus.FreshSuspended: {
coin.status = CoinStatus.DenomLoss;
- await tx.coins.put(coin);
+ await tx.wtx.upsertCoin(coin);
break;
}
}
@@ -2631,9 +2619,7 @@ async function handleRecoup(
logger.info("revoking denom", recoupInfo.h_denom_pub);
oldDenom.isRevoked = true;
await tx.wtx.upsertDenomination(oldDenom);
- const affectedCoins = await tx.coins.indexes.byDenomPubHash.getAll(
- recoupInfo.h_denom_pub,
- );
+ const affectedCoins = await tx.wtx.getCoinsByDenomPubHash(recoupInfo.h_denom_pub);
for (const ac of affectedCoins) {
newlyRevokedCoinPubs.push(ac.coinPub);
}
@@ -3128,9 +3114,9 @@ async function internalGetExchangeResources(
): Promise<GetExchangeResourcesResponse> {
let numWithdrawals = 0;
let numCoins = 0;
- numCoins = await tx.coins.indexes.byBaseUrl.count(exchangeBaseUrl);
+ numCoins = await tx.wtx.countCoinsByExchange(exchangeBaseUrl);
numWithdrawals =
- await tx.withdrawalGroups.indexes.byExchangeBaseUrl.count(exchangeBaseUrl);
+ await tx.wtx.countWithdrawalGroupsByExchange(exchangeBaseUrl);
const total = numWithdrawals + numCoins;
return {
hasResources: total != 0,
@@ -3452,9 +3438,7 @@ export async function checkIncomingAmountLegalUnderKycBalanceThreshold(
throw Error("exchange not found");
}
const coinAvRecs =
- await tx.coinAvailability.indexes.byExchangeBaseUrl.getAll(
- exchangeBaseUrl,
- );
+ await tx.wtx.getCoinAvailabilityByExchange(exchangeBaseUrl);
let balAmount = Amounts.zeroOfCurrency(det.currency);
for (const av of coinAvRecs) {
const n = av.freshCoinCount + (av.pendingRefreshOutputCount ?? 0);
@@ -4066,11 +4050,7 @@ export async function checkExchangeInScopeTx(
return false;
}
const gr =
- await tx.globalCurrencyExchanges.indexes.byCurrencyAndUrlAndPub.get([
- exchangeDetails.currency,
- exchangeBaseUrl,
- exchangeDetails.masterPublicKey,
- ]);
+ await tx.wtx.getGlobalCurrencyExchange(exchangeDetails.currency, exchangeBaseUrl, exchangeDetails.masterPublicKey);
logger.trace(`global currency record: ${j2s(gr)}`);
return gr != null;
}
@@ -4198,21 +4178,23 @@ export async function migrateExchange(
}
{
- await tx.denomLossEvents.iter().forEachAsync(async (rec) => {
+ const recs = await tx.wtx.listAllDenomLossEvents();
+ for (const rec of recs) {
if (rec.exchangeBaseUrl === req.oldExchangeBaseUrl) {
rec.exchangeBaseUrl = req.newExchangeBaseUrl;
await tx.wtx.upsertDenomLossEvent(rec);
}
- });
+ }
}
{
- await tx.recoupGroups.indexes.byExchangeBaseUrl
- .iter(req.oldExchangeBaseUrl)
- .forEachAsync(async (rec) => {
- rec.exchangeBaseUrl = req.newExchangeBaseUrl;
- await tx.wtx.upsertRecoupGroup(rec);
- });
+ const recs = await tx.wtx.getRecoupGroupsByExchange(
+ req.oldExchangeBaseUrl,
+ );
+ for (const rec of recs) {
+ rec.exchangeBaseUrl = req.newExchangeBaseUrl;
+ await tx.wtx.upsertRecoupGroup(rec);
+ }
}
{
@@ -4244,75 +4226,81 @@ export async function migrateExchange(
}
{
- await tx.coins.indexes.byBaseUrl
- .iter(req.oldExchangeBaseUrl)
- .forEachAsync(async (rec) => {
- rec.exchangeBaseUrl = req.newExchangeBaseUrl;
- await tx.coins.put(rec);
- });
+ const recs = await tx.wtx.getCoinsByExchange(req.oldExchangeBaseUrl);
+ for (const rec of recs) {
+ rec.exchangeBaseUrl = req.newExchangeBaseUrl;
+ await tx.wtx.upsertCoin(rec);
+ }
}
{
- await tx.coinAvailability.indexes.byExchangeBaseUrl
- .iter(req.oldExchangeBaseUrl)
- .forEachAsync(async (rec) => {
- await tx.coinAvailability.delete([
- rec.exchangeBaseUrl,
- rec.denomPubHash,
- rec.maxAge,
- ]);
- rec.exchangeBaseUrl = req.newExchangeBaseUrl;
- await tx.coinAvailability.put(rec);
- });
+ const recs = await tx.wtx.getCoinAvailabilityByExchange(
+ req.oldExchangeBaseUrl,
+ );
+ for (const rec of recs) {
+ await tx.wtx.deleteCoinAvailability(
+ rec.exchangeBaseUrl,
+ rec.denomPubHash,
+ rec.maxAge,
+ );
+ rec.exchangeBaseUrl = req.newExchangeBaseUrl;
+ await tx.wtx.upsertCoinAvailability(rec);
+ }
}
{
- await tx.peerPullCredit.iter().forEachAsync(async (rec) => {
+ const recs = await tx.wtx.listAllPeerPullCredits();
+ for (const rec of recs) {
if (rec.exchangeBaseUrl === req.oldExchangeBaseUrl) {
rec.exchangeBaseUrl = req.newExchangeBaseUrl;
- await tx.peerPullCredit.put(rec);
+ await tx.wtx.upsertPeerPullCredit(rec);
}
- });
+ }
}
{
- await tx.peerPullDebit.iter().forEachAsync(async (rec) => {
+ const recs = await tx.wtx.listAllPeerPullDebits();
+ for (const rec of recs) {
if (rec.exchangeBaseUrl === req.oldExchangeBaseUrl) {
rec.exchangeBaseUrl = req.newExchangeBaseUrl;
- await tx.peerPullDebit.put(rec);
+ await tx.wtx.upsertPeerPullDebit(rec);
}
- });
+ }
}
{
- await tx.peerPushCredit.iter().forEachAsync(async (rec) => {
+ const recs = await tx.wtx.listAllPeerPushCredits();
+ for (const rec of recs) {
if (rec.exchangeBaseUrl === req.oldExchangeBaseUrl) {
rec.exchangeBaseUrl = req.newExchangeBaseUrl;
- await tx.peerPushCredit.put(rec);
+ await tx.wtx.upsertPeerPushCredit(rec);
}
- });
+ }
}
{
- await tx.peerPushDebit.iter().forEachAsync(async (rec) => {
+ const recs = await tx.wtx.listAllPeerPushDebits();
+ for (const rec of recs) {
if (rec.exchangeBaseUrl === req.oldExchangeBaseUrl) {
rec.exchangeBaseUrl = req.newExchangeBaseUrl;
- await tx.peerPushDebit.put(rec);
+ await tx.wtx.upsertPeerPushDebit(rec);
}
- });
+ }
}
{
- await tx.withdrawalGroups.indexes.byExchangeBaseUrl
- .iter(req.oldExchangeBaseUrl)
- .forEachAsync(async (rec) => {
- rec.exchangeBaseUrl = req.newExchangeBaseUrl;
- await tx.wtx.upsertWithdrawalGroup(rec);
- });
+ const recs = await tx.wtx.getWithdrawalGroupsByExchangeForRekey(
+ req.oldExchangeBaseUrl,
+ );
+ for (const rec of recs) {
+ rec.exchangeBaseUrl = req.newExchangeBaseUrl;
+ await tx.wtx.upsertWithdrawalGroup(rec);
+ }
}
{
- await tx.refreshGroups.iter().forEachAsync(async (rec) => {
+ const recs = await tx.wtx.listAllRefreshGroups();
+ for (const rec of recs) {
if (
rec.infoPerExchange &&
rec.infoPerExchange[req.oldExchangeBaseUrl] != null
@@ -4322,11 +4310,12 @@ export async function migrateExchange(
delete rec.infoPerExchange[req.oldExchangeBaseUrl];
await tx.wtx.upsertRefreshGroup(rec);
}
- });
+ }
}
{
- await tx.depositGroups.iter().forEachAsync(async (rec) => {
+ const recs = await tx.wtx.listAllDepositGroups();
+ for (const rec of recs) {
if (
rec.infoPerExchange &&
rec.infoPerExchange[req.oldExchangeBaseUrl] != null
@@ -4336,7 +4325,7 @@ export async function migrateExchange(
delete rec.infoPerExchange[req.oldExchangeBaseUrl];
await tx.wtx.upsertDepositGroup(rec);
}
- });
+ }
}
tx.notify({