commit a8e9305b055010fef7dfeb746d081ca4eab81a82
parent c778af04f43871530c1cac29b281950f1a87dff7
Author: Florian Dold <dold@taler.net>
Date: Sat, 18 Jul 2026 00:29:31 +0200
wallet-core: migrate denominations.ts and balance.ts to abstract database access layer
Diffstat:
8 files changed, 698 insertions(+), 448 deletions(-)
diff --git a/packages/taler-wallet-core/src/balance.ts b/packages/taler-wallet-core/src/balance.ts
@@ -59,7 +59,7 @@
/**
* Imports.
*/
-import { GlobalIDB } from "@gnu-taler/idb-bridge";
+
import {
AllowedExchangeInfo,
AmountJson,
@@ -82,8 +82,6 @@ import { ExchangeRestrictionSpec, findMatchingWire } from "./coinSelection.js";
import {
DepositOperationStatus,
ExchangeEntryDbRecordStatus,
- OPERATION_STATUS_NONFINAL_FIRST,
- OPERATION_STATUS_NONFINAL_LAST,
PeerPullDebitRecordStatus,
PeerPullPaymentCreditStatus,
PeerPushCreditStatus,
@@ -93,20 +91,16 @@ import {
WithdrawalGroupStatus,
} from "./db-common.js";
import {
+ WithdrawalRecordType,
DonationSummaryRecord,
RefreshGroupRecord,
- WithdrawalRecordType,
} from "./db-indexeddb.js";
import {
- checkExchangeInScopeTx,
- getExchangeDetailsInTx,
- getExchangeScopeInfo,
-} from "./exchanges.js";
-import {
getDenomInfo,
- LegacyWalletTxHandle,
WalletExecutionContext,
+ LegacyWalletTxHandle,
} from "./wallet.js";
+import { WalletDbTransaction } from "./dbtx.js";
/**
* Logger.
@@ -184,7 +178,7 @@ class BalancesStore {
constructor(
private wex: WalletExecutionContext,
- private tx: LegacyWalletTxHandle,
+ private tx: WalletDbTransaction,
) {}
setNonDemoExchange(baseUrl: string) {
@@ -202,11 +196,7 @@ class BalancesStore {
let scopeInfo: ScopeInfo | undefined =
this.exchangeScopeCache[exchangeBaseUrl];
if (!scopeInfo) {
- scopeInfo = await getExchangeScopeInfo(
- this.tx,
- exchangeBaseUrl,
- currency,
- );
+ scopeInfo = await this.tx.getExchangeScopeInfo(exchangeBaseUrl, currency);
this.exchangeScopeCache[exchangeBaseUrl] = scopeInfo;
}
const balanceKey = getBalanceKey(scopeInfo);
@@ -401,25 +391,22 @@ class BalancesStore {
*/
export async function getBalancesInsideTransaction(
wex: WalletExecutionContext,
- tx: LegacyWalletTxHandle,
+ tx: WalletDbTransaction,
): Promise<BalancesResponse> {
const balanceStore: BalancesStore = new BalancesStore(wex, tx);
- const keyRangeActive = GlobalIDB.KeyRange.bound(
- OPERATION_STATUS_NONFINAL_FIRST,
- OPERATION_STATUS_NONFINAL_LAST,
- );
-
- await tx.donationSummaries.iter().forEachAsync(async (rec) => {
+ const donationSummaries = await tx.getDonationSummaries();
+ for (const rec of donationSummaries) {
balanceStore.addDonauItem(rec);
- });
+ }
- await tx.exchanges.iter().forEachAsync(async (ex) => {
+ const exchanges = await tx.getExchanges();
+ for (const ex of exchanges) {
if (
ex.entryStatus === ExchangeEntryDbRecordStatus.Used ||
ex.tosAcceptedTimestamp != null
) {
- const det = await getExchangeDetailsInTx(tx, ex.baseUrl);
+ const det = await tx.getExchangeDetails(ex.baseUrl);
if (det) {
if (ex.presetType == null || ex.presetType === "prod") {
balanceStore.setNonDemoExchange(ex.baseUrl);
@@ -443,9 +430,10 @@ export async function getBalancesInsideTransaction(
}
}
}
- });
+ }
- await tx.coinAvailability.iter().forEachAsync(async (ca) => {
+ const coinAvailability = await tx.getCoinAvailabilities();
+ for (const ca of coinAvailability) {
const count = ca.visibleCoinCount ?? 0;
await balanceStore.addZero(ca.currency, ca.exchangeBaseUrl);
for (let i = 0; i < count; i++) {
@@ -455,297 +443,290 @@ export async function getBalancesInsideTransaction(
ca.value,
);
}
- });
+ }
- await tx.refreshGroups.indexes.byStatus
- .iter(keyRangeActive)
- .forEachAsync(async (r) => {
- switch (r.operationStatus) {
- case RefreshOperationStatus.Pending:
- case RefreshOperationStatus.Suspended:
- break;
- default:
- return;
- }
- const perExchange = r.infoPerExchange;
- if (!perExchange) {
- return;
- }
- for (const [e, x] of Object.entries(perExchange)) {
- await balanceStore.addAvailable(r.currency, e, x.outputEffective);
- }
- });
+ const refreshGroups = await tx.getActiveRefreshGroups();
+ for (const r of refreshGroups) {
+ switch (r.operationStatus) {
+ case RefreshOperationStatus.Pending:
+ case RefreshOperationStatus.Suspended:
+ break;
+ default:
+ continue;
+ }
+ const perExchange = r.infoPerExchange;
+ if (!perExchange) {
+ continue;
+ }
+ for (const [e, x] of Object.entries(perExchange)) {
+ await balanceStore.addAvailable(r.currency, e, x.outputEffective);
+ }
+ }
- await tx.withdrawalGroups.indexes.byStatus
- .iter(keyRangeActive)
- .forEachAsync(async (wg) => {
- switch (wg.status) {
- case WithdrawalGroupStatus.AbortedBank:
- case WithdrawalGroupStatus.AbortedExchange:
- case WithdrawalGroupStatus.FailedAbortingBank:
- case WithdrawalGroupStatus.FailedBankAborted:
- case WithdrawalGroupStatus.AbortedOtherWallet:
- case WithdrawalGroupStatus.AbortedUserRefused:
- case WithdrawalGroupStatus.DialogProposed:
- case WithdrawalGroupStatus.Done:
- // Does not count as pendingIncoming
- return;
- case WithdrawalGroupStatus.PendingReady:
- case WithdrawalGroupStatus.PendingRedenominate:
- case WithdrawalGroupStatus.SuspendedRedenominate:
- case WithdrawalGroupStatus.AbortingBank:
- case WithdrawalGroupStatus.PendingQueryingStatus:
- case WithdrawalGroupStatus.SuspendedWaitConfirmBank:
- case WithdrawalGroupStatus.SuspendedReady:
- case WithdrawalGroupStatus.SuspendedRegisteringBank:
- case WithdrawalGroupStatus.SuspendedAbortingBank:
- case WithdrawalGroupStatus.SuspendedQueryingStatus:
- // Pending, but no special flag.
- break;
- case WithdrawalGroupStatus.PendingBalanceKyc:
- case WithdrawalGroupStatus.SuspendedBalanceKyc:
- case WithdrawalGroupStatus.SuspendedKyc:
- case WithdrawalGroupStatus.PendingBalanceKycInit:
- case WithdrawalGroupStatus.SuspendedBalanceKycInit:
- case WithdrawalGroupStatus.PendingKyc: {
- checkDbInvariant(
- wg.denomsSel !== undefined,
- "wg in kyc state should have been initialized",
- );
- checkDbInvariant(
- wg.exchangeBaseUrl !== undefined,
- "wg in kyc state should have been initialized",
- );
+ const withdrawalGroups = await tx.getActiveWithdrawalGroups();
+ for (const wg of withdrawalGroups) {
+ switch (wg.status) {
+ case WithdrawalGroupStatus.AbortedBank:
+ case WithdrawalGroupStatus.AbortedExchange:
+ case WithdrawalGroupStatus.FailedAbortingBank:
+ case WithdrawalGroupStatus.FailedBankAborted:
+ case WithdrawalGroupStatus.AbortedOtherWallet:
+ case WithdrawalGroupStatus.AbortedUserRefused:
+ case WithdrawalGroupStatus.DialogProposed:
+ case WithdrawalGroupStatus.Done:
+ // Does not count as pendingIncoming
+ continue;
+ case WithdrawalGroupStatus.PendingReady:
+ case WithdrawalGroupStatus.PendingRedenominate:
+ case WithdrawalGroupStatus.SuspendedRedenominate:
+ case WithdrawalGroupStatus.AbortingBank:
+ case WithdrawalGroupStatus.PendingQueryingStatus:
+ case WithdrawalGroupStatus.SuspendedWaitConfirmBank:
+ case WithdrawalGroupStatus.SuspendedReady:
+ case WithdrawalGroupStatus.SuspendedRegisteringBank:
+ case WithdrawalGroupStatus.SuspendedAbortingBank:
+ case WithdrawalGroupStatus.SuspendedQueryingStatus:
+ // Pending, but no special flag.
+ break;
+ case WithdrawalGroupStatus.PendingBalanceKyc:
+ case WithdrawalGroupStatus.SuspendedBalanceKyc:
+ case WithdrawalGroupStatus.SuspendedKyc:
+ case WithdrawalGroupStatus.PendingBalanceKycInit:
+ case WithdrawalGroupStatus.SuspendedBalanceKycInit:
+ case WithdrawalGroupStatus.PendingKyc: {
+ checkDbInvariant(
+ wg.denomsSel !== undefined,
+ "wg in kyc state should have been initialized",
+ );
+ checkDbInvariant(
+ wg.exchangeBaseUrl !== undefined,
+ "wg in kyc state should have been initialized",
+ );
+ const currency = Amounts.currencyOf(wg.denomsSel.totalCoinValue);
+ await balanceStore.setFlagIncomingKyc(currency, wg.exchangeBaseUrl);
+ break;
+ }
+ case WithdrawalGroupStatus.PendingRegisteringBank: {
+ if (wg.denomsSel && wg.exchangeBaseUrl) {
const currency = Amounts.currencyOf(wg.denomsSel.totalCoinValue);
- await balanceStore.setFlagIncomingKyc(currency, wg.exchangeBaseUrl);
- break;
- }
- case WithdrawalGroupStatus.PendingRegisteringBank: {
- if (wg.denomsSel && wg.exchangeBaseUrl) {
- const currency = Amounts.currencyOf(wg.denomsSel.totalCoinValue);
- await balanceStore.setFlagIncomingConfirmation(
- currency,
- wg.exchangeBaseUrl,
- );
- }
- break;
- }
- case WithdrawalGroupStatus.PendingWaitConfirmBank: {
- checkDbInvariant(
- wg.exchangeBaseUrl !== undefined,
- "withdrawal group in PendingWaitConfirmBank state should have been initialized",
- );
-
- // FIXME: Consider just having the currency as a fixed field in the DB
- // instead of having it in many locations.
- let currency: string;
-
- if (wg.denomsSel) {
- currency = Amounts.currencyOf(wg.denomsSel.totalCoinValue);
- } else if (
- wg.wgInfo.withdrawalType === WithdrawalRecordType.BankIntegrated &&
- wg.wgInfo.bankInfo.currency
- ) {
- currency = wg.wgInfo.bankInfo.currency;
- } else {
- logger.warn(
- "could not determine currency for confirmed withdrawal group",
- );
- break;
- }
await balanceStore.setFlagIncomingConfirmation(
currency,
wg.exchangeBaseUrl,
);
- break;
}
- default:
- assertUnreachable(wg.status);
+ break;
}
- if (wg.denomsSel && wg.exchangeBaseUrl) {
- // only inform pending incoming if amount and exchange has been selected
- const currency = Amounts.currencyOf(wg.denomsSel.totalCoinValue);
- await balanceStore.addPendingIncoming(
- currency,
- wg.exchangeBaseUrl,
- wg.denomsSel.totalCoinValue,
+ case WithdrawalGroupStatus.PendingWaitConfirmBank: {
+ checkDbInvariant(
+ wg.exchangeBaseUrl !== undefined,
+ "withdrawal group in PendingWaitConfirmBank state should have been initialized",
);
- }
- });
- await tx.peerPushDebit.indexes.byStatus
- .iter(keyRangeActive)
- .forEachAsync(async (ppdRecord) => {
- switch (ppdRecord.status) {
- case PeerPushDebitStatus.AbortingDeletePurse:
- case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
- case PeerPushDebitStatus.PendingReady:
- case PeerPushDebitStatus.SuspendedReady:
- case PeerPushDebitStatus.PendingCreatePurse:
- case PeerPushDebitStatus.SuspendedCreatePurse: {
- const currency = Amounts.currencyOf(ppdRecord.amount);
- await balanceStore.addPendingOutgoing(
- currency,
- ppdRecord.exchangeBaseUrl,
- ppdRecord.totalCost,
+ // FIXME: Consider just having the currency as a fixed field in the DB
+ // instead of having it in many locations.
+ let currency: string;
+
+ if (wg.denomsSel) {
+ currency = Amounts.currencyOf(wg.denomsSel.totalCoinValue);
+ } else if (
+ wg.wgInfo.withdrawalType === WithdrawalRecordType.BankIntegrated &&
+ wg.wgInfo.bankInfo.currency
+ ) {
+ currency = wg.wgInfo.bankInfo.currency;
+ } else {
+ logger.warn(
+ "could not determine currency for confirmed withdrawal group",
);
break;
}
- case PeerPushDebitStatus.Failed:
- case PeerPushDebitStatus.Aborted:
- case PeerPushDebitStatus.Done:
- case PeerPushDebitStatus.Expired:
- break;
- default:
- assertUnreachable(ppdRecord.status);
+ await balanceStore.setFlagIncomingConfirmation(
+ currency,
+ wg.exchangeBaseUrl,
+ );
+ break;
}
- });
+ default:
+ assertUnreachable(wg.status);
+ }
+ if (wg.denomsSel && wg.exchangeBaseUrl) {
+ // only inform pending incoming if amount and exchange has been selected
+ const currency = Amounts.currencyOf(wg.denomsSel.totalCoinValue);
+ await balanceStore.addPendingIncoming(
+ currency,
+ wg.exchangeBaseUrl,
+ wg.denomsSel.totalCoinValue,
+ );
+ }
+ }
- await tx.peerPushCredit.indexes.byStatus
- .iter(keyRangeActive)
- .forEachAsync(async (rec) => {
- switch (rec.status) {
- case PeerPushCreditStatus.PendingMerge:
- case PeerPushCreditStatus.PendingBalanceKycInit:
- case PeerPushCreditStatus.PendingBalanceKycRequired:
- case PeerPushCreditStatus.SuspendedBalanceKycInit:
- case PeerPushCreditStatus.PendingMergeKycRequired:
- case PeerPushCreditStatus.SuspendedMerge:
- case PeerPushCreditStatus.SuspendedBalanceKycRequired:
- case PeerPushCreditStatus.SuspendedMergeKycRequired: {
- const currency = Amounts.currencyOf(rec.estimatedAmountEffective);
- const amount = rec.estimatedAmountEffective;
- await balanceStore.addPendingIncoming(
- currency,
- rec.exchangeBaseUrl,
- amount,
- );
- break;
- }
- case PeerPushCreditStatus.PendingWithdrawing:
- case PeerPushCreditStatus.SuspendedWithdrawing: {
- // Here, incoming balance should be covered by internal withdrawal tx
- break;
- }
+ const peerPushDebits = await tx.getActivePeerPushDebits();
+ for (const ppdRecord of peerPushDebits) {
+ switch (ppdRecord.status) {
+ case PeerPushDebitStatus.AbortingDeletePurse:
+ case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
+ case PeerPushDebitStatus.PendingReady:
+ case PeerPushDebitStatus.SuspendedReady:
+ case PeerPushDebitStatus.PendingCreatePurse:
+ case PeerPushDebitStatus.SuspendedCreatePurse: {
+ const currency = Amounts.currencyOf(ppdRecord.amount);
+ await balanceStore.addPendingOutgoing(
+ currency,
+ ppdRecord.exchangeBaseUrl,
+ ppdRecord.totalCost,
+ );
+ break;
}
- });
+ case PeerPushDebitStatus.Failed:
+ case PeerPushDebitStatus.Aborted:
+ case PeerPushDebitStatus.Done:
+ case PeerPushDebitStatus.Expired:
+ break;
+ default:
+ assertUnreachable(ppdRecord.status);
+ }
+ }
- await tx.peerPullCredit.indexes.byStatus
- .iter(keyRangeActive)
- .forEachAsync(async (rec) => {
- switch (rec.status) {
- case PeerPullPaymentCreditStatus.SuspendedBalanceKycInit:
- case PeerPullPaymentCreditStatus.PendingBalanceKycInit:
- case PeerPullPaymentCreditStatus.PendingBalanceKycRequired:
- case PeerPullPaymentCreditStatus.SuspendedBalanceKycRequired:
- case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired:
- case PeerPullPaymentCreditStatus.PendingMergeKycRequired:
- case PeerPullPaymentCreditStatus.PendingReady:
- case PeerPullPaymentCreditStatus.PendingCreatePurse:
- case PeerPullPaymentCreditStatus.SuspendedReady:
- case PeerPullPaymentCreditStatus.SuspendedCreatePurse: {
- const currency = Amounts.currencyOf(rec.amount);
- const amount = rec.estimatedAmountEffective;
- await balanceStore.addPendingIncoming(
- currency,
- rec.exchangeBaseUrl,
- amount,
- );
- break;
- }
+ const peerPushCredits = await tx.getActivePeerPushCredits();
+ for (const rec of peerPushCredits) {
+ switch (rec.status) {
+ case PeerPushCreditStatus.PendingMerge:
+ case PeerPushCreditStatus.PendingBalanceKycInit:
+ case PeerPushCreditStatus.PendingBalanceKycRequired:
+ case PeerPushCreditStatus.SuspendedBalanceKycInit:
+ case PeerPushCreditStatus.PendingMergeKycRequired:
+ case PeerPushCreditStatus.SuspendedMerge:
+ case PeerPushCreditStatus.SuspendedBalanceKycRequired:
+ case PeerPushCreditStatus.SuspendedMergeKycRequired: {
+ const currency = Amounts.currencyOf(rec.estimatedAmountEffective);
+ const amount = rec.estimatedAmountEffective;
+ await balanceStore.addPendingIncoming(
+ currency,
+ rec.exchangeBaseUrl,
+ amount,
+ );
+ break;
}
- });
+ case PeerPushCreditStatus.PendingWithdrawing:
+ case PeerPushCreditStatus.SuspendedWithdrawing: {
+ // Here, incoming balance should be covered by internal withdrawal tx
+ break;
+ }
+ }
+ }
- await tx.peerPullDebit.indexes.byStatus
- .iter(keyRangeActive)
- .forEachAsync(async (rec) => {
- switch (rec.status) {
- case PeerPullDebitRecordStatus.PendingDeposit:
- case PeerPullDebitRecordStatus.AbortingRefresh:
- case PeerPullDebitRecordStatus.SuspendedAbortingRefresh:
- case PeerPullDebitRecordStatus.SuspendedDeposit:
- const currency = Amounts.currencyOf(rec.amount);
- const amount = rec.coinSel?.totalCost ?? rec.amount;
- await balanceStore.addPendingOutgoing(
- currency,
- rec.exchangeBaseUrl,
- amount,
- );
- break;
+ const peerPullCredits = await tx.getActivePeerPullCredits();
+ for (const rec of peerPullCredits) {
+ switch (rec.status) {
+ case PeerPullPaymentCreditStatus.SuspendedBalanceKycInit:
+ case PeerPullPaymentCreditStatus.PendingBalanceKycInit:
+ case PeerPullPaymentCreditStatus.PendingBalanceKycRequired:
+ case PeerPullPaymentCreditStatus.SuspendedBalanceKycRequired:
+ case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired:
+ case PeerPullPaymentCreditStatus.PendingMergeKycRequired:
+ case PeerPullPaymentCreditStatus.PendingReady:
+ case PeerPullPaymentCreditStatus.PendingCreatePurse:
+ case PeerPullPaymentCreditStatus.SuspendedReady:
+ case PeerPullPaymentCreditStatus.SuspendedCreatePurse: {
+ const currency = Amounts.currencyOf(rec.amount);
+ const amount = rec.estimatedAmountEffective;
+ await balanceStore.addPendingIncoming(
+ currency,
+ rec.exchangeBaseUrl,
+ amount,
+ );
+ break;
}
- });
+ }
+ }
- await tx.purchases.indexes.byStatus
- .iter(keyRangeActive)
- .forEachAsync(async (rec) => {
- switch (rec.purchaseStatus) {
- case PurchaseStatus.SuspendedPaying:
- case PurchaseStatus.PendingPaying:
- if (!rec.payInfo || !rec.payInfo.payCoinSelection?.coinPubs) {
- break;
- }
- // FIXME: This is pretty slow, cache?
- const currency = Amounts.currencyOf(rec.payInfo.totalPayCost);
- const sel = rec.payInfo.payCoinSelection;
- for (let i = 0; i < sel.coinPubs.length; i++) {
- const coinPub = sel.coinPubs[i];
- const coinRec = await tx.coins.get(coinPub);
- if (!coinRec) {
- continue;
- }
- await balanceStore.addPendingOutgoing(
- currency,
- coinRec.exchangeBaseUrl,
- sel.coinContributions[i],
- );
+ const peerPullDebits = await tx.getActivePeerPullDebits();
+ for (const rec of peerPullDebits) {
+ switch (rec.status) {
+ case PeerPullDebitRecordStatus.PendingDeposit:
+ case PeerPullDebitRecordStatus.AbortingRefresh:
+ case PeerPullDebitRecordStatus.SuspendedAbortingRefresh:
+ case PeerPullDebitRecordStatus.SuspendedDeposit:
+ const currency = Amounts.currencyOf(rec.amount);
+ const amount = rec.coinSel?.totalCost ?? rec.amount;
+ await balanceStore.addPendingOutgoing(
+ currency,
+ rec.exchangeBaseUrl,
+ amount,
+ );
+ break;
+ }
+ }
+
+ const purchases = await tx.getActivePurchases();
+ for (const rec of purchases) {
+ switch (rec.purchaseStatus) {
+ case PurchaseStatus.SuspendedPaying:
+ case PurchaseStatus.PendingPaying:
+ if (!rec.payInfo || !rec.payInfo.payCoinSelection?.coinPubs) {
+ break;
+ }
+ const currency = Amounts.currencyOf(rec.payInfo.totalPayCost);
+ const sel = rec.payInfo.payCoinSelection;
+ const coins = await tx.getCoinsByPubs(sel.coinPubs);
+ const coinsByPub = new Map(coins.map((c) => [c.coinPub, c]));
+ for (let i = 0; i < sel.coinPubs.length; i++) {
+ const coinPub = sel.coinPubs[i];
+ const coinRec = coinsByPub.get(coinPub);
+ if (!coinRec) {
+ continue;
}
- }
- });
+ await balanceStore.addPendingOutgoing(
+ currency,
+ coinRec.exchangeBaseUrl,
+ sel.coinContributions[i],
+ );
+ }
+ }
+ }
- await tx.depositGroups.indexes.byStatus
- .iter(keyRangeActive)
- .forEachAsync(async (dgRecord) => {
- const perExchange = dgRecord.infoPerExchange;
- if (!perExchange) {
- return;
+ const activeDepositGroups = await tx.getActiveDepositGroups();
+ for (const dgRecord of activeDepositGroups) {
+ const perExchange = dgRecord.infoPerExchange;
+ if (!perExchange) {
+ continue;
+ }
+ for (const [e, x] of Object.entries(perExchange)) {
+ const currency = Amounts.currencyOf(dgRecord.amount);
+ switch (dgRecord.operationStatus) {
+ case DepositOperationStatus.SuspendedAggregateKyc:
+ case DepositOperationStatus.PendingAggregateKyc:
+ case DepositOperationStatus.PendingDepositKyc:
+ case DepositOperationStatus.PendingDepositKycAuth:
+ case DepositOperationStatus.SuspendedDepositKyc:
+ case DepositOperationStatus.SuspendedDepositKycAuth:
+ await balanceStore.setFlagOutgoingKyc(currency, e);
}
- for (const [e, x] of Object.entries(perExchange)) {
- const currency = Amounts.currencyOf(dgRecord.amount);
- switch (dgRecord.operationStatus) {
- case DepositOperationStatus.SuspendedAggregateKyc:
- case DepositOperationStatus.PendingAggregateKyc:
- case DepositOperationStatus.PendingDepositKyc:
- case DepositOperationStatus.PendingDepositKycAuth:
- case DepositOperationStatus.SuspendedDepositKyc:
- case DepositOperationStatus.SuspendedDepositKycAuth:
- await balanceStore.setFlagOutgoingKyc(currency, e);
- }
- switch (dgRecord.operationStatus) {
- case DepositOperationStatus.SuspendedAggregateKyc:
- case DepositOperationStatus.PendingAggregateKyc:
- case DepositOperationStatus.LegacyPendingTrack:
- case DepositOperationStatus.SuspendedAborting:
- case DepositOperationStatus.SuspendedDeposit:
- case DepositOperationStatus.LegacySuspendedTrack:
- case DepositOperationStatus.PendingDepositKyc:
- case DepositOperationStatus.PendingDepositKycAuth:
- case DepositOperationStatus.SuspendedDepositKyc:
- case DepositOperationStatus.SuspendedDepositKycAuth:
- case DepositOperationStatus.PendingDeposit: {
- const perExchange = dgRecord.infoPerExchange;
- if (perExchange) {
- for (const [e, v] of Object.entries(perExchange)) {
- await balanceStore.addPendingOutgoing(
- currency,
- e,
- v.amountEffective,
- );
- }
+ switch (dgRecord.operationStatus) {
+ case DepositOperationStatus.SuspendedAggregateKyc:
+ case DepositOperationStatus.PendingAggregateKyc:
+ case DepositOperationStatus.LegacyPendingTrack:
+ case DepositOperationStatus.SuspendedAborting:
+ case DepositOperationStatus.SuspendedDeposit:
+ case DepositOperationStatus.LegacySuspendedTrack:
+ case DepositOperationStatus.PendingDepositKyc:
+ case DepositOperationStatus.PendingDepositKycAuth:
+ case DepositOperationStatus.SuspendedDepositKyc:
+ case DepositOperationStatus.SuspendedDepositKycAuth:
+ case DepositOperationStatus.PendingDeposit: {
+ const perExchange = dgRecord.infoPerExchange;
+ if (perExchange) {
+ for (const [e, v] of Object.entries(perExchange)) {
+ await balanceStore.addPendingOutgoing(
+ currency,
+ e,
+ v.amountEffective,
+ );
}
}
}
}
- });
+ }
+ }
return balanceStore.toBalancesResponse();
}
@@ -758,7 +739,7 @@ export async function getBalances(
): Promise<BalancesResponse> {
logger.trace("starting to compute balance");
- const wbal = await wex.runLegacyWalletDbTx(async (tx) => {
+ const wbal = await wex.runWalletDbTx(async (tx) => {
return getBalancesInsideTransaction(wex, tx);
});
@@ -776,7 +757,7 @@ export async function getBalanceOfScope(
): Promise<BalancesResponse> {
logger.trace("starting to compute balance");
- const wbal = await wex.runLegacyWalletDbTx(async (tx) => {
+ const wbal = await wex.runWalletDbTx(async (tx) => {
return getBalancesInsideTransaction(wex, tx);
});
@@ -885,16 +866,17 @@ export async function getPaymentBalanceDetails(
wex: WalletExecutionContext,
req: PaymentRestrictionsForBalance,
): Promise<PaymentBalanceDetails> {
- return await wex.runLegacyWalletDbTx(async (tx) => {
+ return await wex.runWalletDbTx(async (tx) => {
return getPaymentBalanceDetailsInTx(wex, tx, req);
});
}
export async function getPaymentBalanceDetailsInTx(
wex: WalletExecutionContext,
- tx: LegacyWalletTxHandle,
+ tx: WalletDbTransaction | LegacyWalletTxHandle,
req: PaymentRestrictionsForBalance,
): Promise<PaymentBalanceDetails> {
+ const dbTx = "wtx" in tx ? tx.wtx : tx;
const d: PaymentBalanceDetails = {
balanceAvailable: Amounts.zeroOfCurrency(req.currency),
balanceMaterial: Amounts.zeroOfCurrency(req.currency),
@@ -910,7 +892,7 @@ export async function getPaymentBalanceDetailsInTx(
logger.trace(`computing balance details for ${j2s(req)}`);
- const availableCoins = await tx.coinAvailability.getAll();
+ const availableCoins = await dbTx.getCoinAvailabilities();
for (const ca of availableCoins) {
if (ca.currency != req.currency) {
@@ -919,7 +901,7 @@ export async function getPaymentBalanceDetailsInTx(
const denom = await getDenomInfo(
wex,
- tx,
+ dbTx,
ca.exchangeBaseUrl,
ca.denomPubHash,
);
@@ -930,8 +912,7 @@ export async function getPaymentBalanceDetailsInTx(
// Skip exchanges if excluded by the receiver.
if (
req.restrictSenderScope &&
- !(await checkExchangeInScopeTx(
- tx,
+ !(await dbTx.checkExchangeInScope(
ca.exchangeBaseUrl,
req.restrictSenderScope,
))
@@ -939,7 +920,7 @@ export async function getPaymentBalanceDetailsInTx(
continue;
}
- const wireDetails = await getExchangeDetailsInTx(tx, ca.exchangeBaseUrl);
+ const wireDetails = await dbTx.getExchangeDetails(ca.exchangeBaseUrl);
if (!wireDetails) {
continue;
}
@@ -1066,16 +1047,17 @@ export async function getPaymentBalanceDetailsInTx(
}
}
- await tx.refreshGroups.iter().forEach((r) => {
+ const refreshGroups = await dbTx.getActiveRefreshGroups();
+ for (const r of refreshGroups) {
if (r.currency != req.currency) {
- return;
+ continue;
}
const balRefresh = computeRefreshGroupAvailableAmountForExchanges(
r,
req.restrictReceiverExchanges?.exchanges,
);
d.balanceAvailable = Amounts.add(d.balanceAvailable, balRefresh).amount;
- });
+ }
return d;
}
@@ -1086,10 +1068,10 @@ export async function getBalanceDetail(
): Promise<PaymentBalanceDetails> {
const exchanges: { exchangeBaseUrl: string; exchangePub: string }[] = [];
const wires = new Array<string>();
- await wex.runLegacyWalletDbTx(async (tx) => {
- const allExchanges = await tx.exchanges.iter().toArray();
+ await wex.runWalletDbTx(async (tx) => {
+ const allExchanges = await tx.getExchanges();
for (const e of allExchanges) {
- const details = await getExchangeDetailsInTx(tx, e.baseUrl);
+ const details = await tx.getExchangeDetails(e.baseUrl);
if (!details || req.currency !== details.currency) {
continue;
}
diff --git a/packages/taler-wallet-core/src/coinSelection.ts b/packages/taler-wallet-core/src/coinSelection.ts
@@ -55,6 +55,7 @@ import {
SelectedCoin,
strcmp,
TalerProtocolTimestamp,
+ WireInfo,
} from "@gnu-taler/taler-util";
import {
getPaymentBalanceDetailsInTx,
@@ -837,7 +838,7 @@ export type AvailableCoinsOfDenom = DenominationInfo & {
export function findMatchingWire(
wireMethod: string,
depositPaytoUri: string | undefined,
- exchangeWireDetails: ExchangeDetails,
+ exchangeWireDetails: { wireInfo: WireInfo },
):
| { ok: true; wireFee: AmountJson }
| { ok: false; accountRestrictions: Record<string, AccountRestriction[]> }
diff --git a/packages/taler-wallet-core/src/db-common.ts b/packages/taler-wallet-core/src/db-common.ts
@@ -26,6 +26,9 @@ import {
MerchantContractTokenDetails,
ScopeInfo,
TalerErrorDetail,
+ DenominationPubKey,
+ Amounts,
+ DenominationInfo,
} from "@gnu-taler/taler-util";
declare const symDbProtocolTimestamp: unique symbol;
@@ -1082,3 +1085,140 @@ export interface WalletToken {
*/
tokenUseSig?: TokenUseSig;
}
+
+/**
+ * Denomination record as stored in the wallet's database.
+ */
+export interface DenominationRecord {
+ /**
+ * Currency of the denomination.
+ *
+ * Stored separately as we have an index on it.
+ */
+ currency: string;
+
+ value: AmountString;
+
+ /**
+ * The denomination public key.
+ */
+ denomPub: DenominationPubKey;
+
+ /**
+ * Hash of the denomination public key.
+ * Stored in the database for faster lookups.
+ */
+ denomPubHash: string;
+
+ fees: DenomFees;
+
+ denominationFamilySerial: number;
+
+ /**
+ * Validity start date of the denomination.
+ */
+ stampStart: DbProtocolTimestamp;
+
+ /**
+ * Date after which the currency can't be withdrawn anymore.
+ */
+ stampExpireWithdraw: DbProtocolTimestamp;
+
+ /**
+ * Date after the denomination officially doesn't exist anymore.
+ */
+ stampExpireLegal: DbProtocolTimestamp;
+
+ /**
+ * Data after which coins of this denomination can't be deposited anymore.
+ */
+ stampExpireDeposit: DbProtocolTimestamp;
+
+ /**
+ * Signature by the exchange's master key over the denomination
+ * information.
+ */
+ masterSig: string;
+
+ /**
+ * Did we verify the signature on the denomination?
+ */
+ verificationStatus: DenominationVerificationStatus;
+
+ /**
+ * Was this denomination still offered by the exchange the last time
+ * we checked?
+ * Only false when the exchange redacts a previously published denomination.
+ */
+ isOffered: boolean;
+
+ /**
+ * Did the exchange revoke the denomination?
+ * When this field is set to true in the database, the same transaction
+ * should also mark all affected coins as revoked.
+ */
+ isRevoked: boolean;
+
+ /**
+ * If set to true, the exchange announced that the private key for this
+ * denomination is lost. Thus it can't be used to sign new coins
+ * during withdrawal/refresh/..., but the coins can still be spent.
+ */
+ isLost?: boolean;
+
+ /**
+ * Base URL of the exchange.
+ */
+ exchangeBaseUrl: string;
+
+ /**
+ * Master public key of the exchange that made the signature
+ * on the denomination.
+ */
+ exchangeMasterPub: string;
+}
+
+export interface DenomFees {
+ /**
+ * Fee for withdrawing.
+ */
+ feeWithdraw: AmountString;
+
+ /**
+ * Fee for depositing.
+ */
+ feeDeposit: AmountString;
+
+ /**
+ * Fee for refreshing.
+ */
+ feeRefresh: AmountString;
+
+ /**
+ * Fee for refunding.
+ */
+ feeRefund: AmountString;
+}
+
+export namespace DenominationRecord {
+ export function toDenomInfo(d: DenominationRecord): DenominationInfo {
+ return {
+ denomPub: d.denomPub,
+ exchangeMasterPub: d.exchangeMasterPub,
+ denomPubHash: d.denomPubHash,
+ feeDeposit: Amounts.stringify(d.fees.feeDeposit),
+ feeRefresh: Amounts.stringify(d.fees.feeRefresh),
+ feeRefund: Amounts.stringify(d.fees.feeRefund),
+ feeWithdraw: Amounts.stringify(d.fees.feeWithdraw),
+ stampExpireDeposit: timestampProtocolFromDb(d.stampExpireDeposit),
+ stampExpireLegal: timestampProtocolFromDb(d.stampExpireLegal),
+ stampExpireWithdraw: timestampProtocolFromDb(d.stampExpireWithdraw),
+ stampStart: timestampProtocolFromDb(d.stampStart),
+ value: Amounts.stringify(d.value),
+ exchangeBaseUrl: d.exchangeBaseUrl,
+ isLost: d.isLost ?? false,
+ masterSig: d.masterSig,
+ isOffered: d.isOffered,
+ };
+ }
+}
diff --git a/packages/taler-wallet-core/src/db-indexeddb.ts b/packages/taler-wallet-core/src/db-indexeddb.ts
@@ -110,7 +110,10 @@ import {
WalletBackupConfState,
WithdrawalGroupStatus,
timestampProtocolFromDb,
+ DenominationRecord,
+ DenomFees,
} from "./db-common.js";
+export { DenominationRecord, DenomFees };
import {
DbAccess,
DbAccessImpl,
@@ -254,28 +257,6 @@ export interface ReserveBankInfo {
senderWire?: string;
}
-export interface DenomFees {
- /**
- * Fee for withdrawing.
- */
- feeWithdraw: AmountString;
-
- /**
- * Fee for depositing.
- */
- feeDeposit: AmountString;
-
- /**
- * Fee for refreshing.
- */
- feeRefresh: AmountString;
-
- /**
- * Fee for refunding.
- */
- feeRefund: AmountString;
-}
-
export interface DenominationFamilyRecord {
denominationFamilySerial?: number;
familyParams: DenomFamilyParams;
@@ -287,117 +268,6 @@ export interface DenominationFamilyRecord {
/**
* Denomination record as stored in the wallet's database.
*/
-export interface DenominationRecord {
- /**
- * Currency of the denomination.
- *
- * Stored separately as we have an index on it.
- */
- currency: string;
-
- value: AmountString;
-
- /**
- * The denomination public key.
- */
- denomPub: DenominationPubKey;
-
- /**
- * Hash of the denomination public key.
- * Stored in the database for faster lookups.
- */
- denomPubHash: string;
-
- fees: DenomFees;
-
- denominationFamilySerial: number;
-
- /**
- * Validity start date of the denomination.
- */
- stampStart: DbProtocolTimestamp;
-
- /**
- * Date after which the currency can't be withdrawn anymore.
- */
- stampExpireWithdraw: DbProtocolTimestamp;
-
- /**
- * Date after the denomination officially doesn't exist anymore.
- */
- stampExpireLegal: DbProtocolTimestamp;
-
- /**
- * Data after which coins of this denomination can't be deposited anymore.
- */
- stampExpireDeposit: DbProtocolTimestamp;
-
- /**
- * Signature by the exchange's master key over the denomination
- * information.
- */
- masterSig: string;
-
- /**
- * Did we verify the signature on the denomination?
- */
- verificationStatus: DenominationVerificationStatus;
-
- /**
- * Was this denomination still offered by the exchange the last time
- * we checked?
- * Only false when the exchange redacts a previously published denomination.
- */
- isOffered: boolean;
-
- /**
- * Did the exchange revoke the denomination?
- * When this field is set to true in the database, the same transaction
- * should also mark all affected coins as revoked.
- */
- isRevoked: boolean;
-
- /**
- * If set to true, the exchange announced that the private key for this
- * denomination is lost. Thus it can't be used to sign new coins
- * during withdrawal/refresh/..., but the coins can still be spent.
- */
- isLost?: boolean;
-
- /**
- * Base URL of the exchange.
- */
- exchangeBaseUrl: string;
-
- /**
- * Master public key of the exchange that made the signature
- * on the denomination.
- */
- exchangeMasterPub: string;
-}
-
-export namespace DenominationRecord {
- export function toDenomInfo(d: DenominationRecord): DenominationInfo {
- return {
- denomPub: d.denomPub,
- exchangeMasterPub: d.exchangeMasterPub,
- denomPubHash: d.denomPubHash,
- feeDeposit: Amounts.stringify(d.fees.feeDeposit),
- feeRefresh: Amounts.stringify(d.fees.feeRefresh),
- feeRefund: Amounts.stringify(d.fees.feeRefund),
- feeWithdraw: Amounts.stringify(d.fees.feeWithdraw),
- stampExpireDeposit: timestampProtocolFromDb(d.stampExpireDeposit),
- stampExpireLegal: timestampProtocolFromDb(d.stampExpireLegal),
- stampExpireWithdraw: timestampProtocolFromDb(d.stampExpireWithdraw),
- stampStart: timestampProtocolFromDb(d.stampStart),
- value: Amounts.stringify(d.value),
- exchangeBaseUrl: d.exchangeBaseUrl,
- isLost: d.isLost ?? false,
- masterSig: d.masterSig,
- isOffered: d.isOffered,
- };
- }
-}
export interface ExchangeSignkeysRecord {
stampStart: DbProtocolTimestamp;
diff --git a/packages/taler-wallet-core/src/dbtx.ts b/packages/taler-wallet-core/src/dbtx.ts
@@ -21,7 +21,10 @@ import {
MailboxMessageRecord,
ScopeInfo,
stringifyScopeInfo,
+ assertUnreachable,
+ ScopeType,
} from "@gnu-taler/taler-util";
+import { GlobalIDB } from "@gnu-taler/idb-bridge";
import {
ConfigRecord,
WalletPeerPullCredit,
@@ -29,7 +32,21 @@ import {
WalletPeerPushCredit,
WalletPeerPullDebit,
WalletToken,
+ DenominationRecord,
+ DenominationVerificationStatus,
+ OPERATION_STATUS_NONFINAL_FIRST,
+ OPERATION_STATUS_NONFINAL_LAST,
} from "./db-common.js";
+import type {
+ ExchangeEntryRecord,
+ CoinAvailabilityRecord,
+ RefreshGroupRecord,
+ WithdrawalGroupRecord,
+ CoinRecord,
+ DepositGroupRecord,
+ ExchangeDetailsRecord,
+ DonationSummaryRecord,
+} from "./db-indexeddb.js";
import {
PeerPullCreditRecord,
PeerPushDebitRecord,
@@ -198,6 +215,62 @@ export interface WalletDbTransaction {
exchangeBaseUrl: string,
contractPriv: string,
): Promise<WalletPeerPullDebit | undefined>;
+
+ upsertDenomination(rec: DenominationRecord): Promise<void>;
+
+ getDenomination(
+ exchangeBaseUrl: string,
+ denomPubHash: string,
+ ): Promise<DenominationRecord | undefined>;
+
+ getDenominationsByVerificationStatus(
+ verificationStatus: DenominationVerificationStatus,
+ ): Promise<DenominationRecord[]>;
+
+ getDonationSummaries(): Promise<DonationSummaryRecord[]>;
+
+ getExchanges(): Promise<ExchangeEntryRecord[]>;
+
+ getCoinAvailabilities(): Promise<CoinAvailabilityRecord[]>;
+
+ getActiveRefreshGroups(): Promise<RefreshGroupRecord[]>;
+
+ getActiveWithdrawalGroups(): Promise<WithdrawalGroupRecord[]>;
+
+ getActivePeerPushDebits(): Promise<PeerPushDebitRecord[]>;
+
+ getActivePeerPushCredits(): Promise<PeerPushCreditRecord[]>;
+
+ getActivePeerPullCredits(): Promise<PeerPullCreditRecord[]>;
+
+ getActivePeerPullDebits(): Promise<PeerPullPaymentIncomingRecord[]>;
+
+ getActivePurchases(): Promise<PurchaseRecord[]>;
+
+ getCoinsByPubs(coinPubs: string[]): Promise<CoinRecord[]>;
+
+ getActiveDepositGroups(): Promise<DepositGroupRecord[]>;
+
+ getExchangeDetails(
+ exchangeBaseUrl: string,
+ ): Promise<ExchangeDetailsRecord | undefined>;
+
+ checkExchangeInScope(
+ exchangeBaseUrl: string,
+ scope: ScopeInfo,
+ ): Promise<boolean>;
+
+ getExchangeScopeInfo(
+ exchangeBaseUrl: string,
+ currency: string,
+ ): Promise<ScopeInfo>;
+}
+
+function getActiveKeyRange() {
+ return GlobalIDB.KeyRange.bound(
+ OPERATION_STATUS_NONFINAL_FIRST,
+ OPERATION_STATUS_NONFINAL_LAST,
+ );
}
export class IdbWalletTransaction implements WalletDbTransaction {
@@ -661,4 +734,184 @@ export class IdbWalletTransaction implements WalletDbTransaction {
}
return this.getPeerPullDebit(r.peerPullDebitId);
}
+
+ async upsertDenomination(rec: DenominationRecord): Promise<void> {
+ const tx = this.tx;
+ await tx.denominations.put(rec);
+ }
+
+ async getDenomination(
+ exchangeBaseUrl: string,
+ denomPubHash: string,
+ ): Promise<DenominationRecord | undefined> {
+ const tx = this.tx;
+ return await tx.denominations.get([exchangeBaseUrl, denomPubHash]);
+ }
+
+ async getDenominationsByVerificationStatus(
+ verificationStatus: DenominationVerificationStatus,
+ ): Promise<DenominationRecord[]> {
+ const tx = this.tx;
+ return await tx.denominations.indexes.byVerificationStatus.getAll(
+ verificationStatus,
+ );
+ }
+
+ async getDonationSummaries(): Promise<DonationSummaryRecord[]> {
+ return await this.tx.donationSummaries.iter().toArray();
+ }
+
+ async getExchanges(): Promise<ExchangeEntryRecord[]> {
+ return await this.tx.exchanges.iter().toArray();
+ }
+
+ async getCoinAvailabilities(): Promise<CoinAvailabilityRecord[]> {
+ return await this.tx.coinAvailability.iter().toArray();
+ }
+
+ async getActiveRefreshGroups(): Promise<RefreshGroupRecord[]> {
+ return await this.tx.refreshGroups.indexes.byStatus.getAll(
+ getActiveKeyRange(),
+ );
+ }
+
+ async getActiveWithdrawalGroups(): Promise<WithdrawalGroupRecord[]> {
+ return await this.tx.withdrawalGroups.indexes.byStatus.getAll(
+ getActiveKeyRange(),
+ );
+ }
+
+ async getActivePeerPushDebits(): Promise<PeerPushDebitRecord[]> {
+ return await this.tx.peerPushDebit.indexes.byStatus.getAll(
+ getActiveKeyRange(),
+ );
+ }
+
+ async getActivePeerPushCredits(): Promise<PeerPushCreditRecord[]> {
+ return await this.tx.peerPushCredit.indexes.byStatus.getAll(
+ getActiveKeyRange(),
+ );
+ }
+
+ async getActivePeerPullCredits(): Promise<PeerPullCreditRecord[]> {
+ return await this.tx.peerPullCredit.indexes.byStatus.getAll(
+ getActiveKeyRange(),
+ );
+ }
+
+ async getActivePeerPullDebits(): Promise<PeerPullPaymentIncomingRecord[]> {
+ return await this.tx.peerPullDebit.indexes.byStatus.getAll(
+ getActiveKeyRange(),
+ );
+ }
+
+ async getActivePurchases(): Promise<PurchaseRecord[]> {
+ return await this.tx.purchases.indexes.byStatus.getAll(getActiveKeyRange());
+ }
+
+ async getCoinsByPubs(coinPubs: string[]): Promise<CoinRecord[]> {
+ const coins: CoinRecord[] = [];
+ for (const pub of coinPubs) {
+ const coin = await this.tx.coins.get(pub);
+ if (coin) {
+ coins.push(coin);
+ }
+ }
+ return coins;
+ }
+
+ async getActiveDepositGroups(): Promise<DepositGroupRecord[]> {
+ return await this.tx.depositGroups.indexes.byStatus.getAll(
+ getActiveKeyRange(),
+ );
+ }
+
+ async getExchangeDetails(
+ exchangeBaseUrl: string,
+ ): Promise<ExchangeDetailsRecord | undefined> {
+ const r = await this.tx.exchanges.get(exchangeBaseUrl);
+ if (!r || !r.detailsPointer) {
+ return undefined;
+ }
+ return await this.tx.exchangeDetails.indexes.byPointer.get([
+ r.baseUrl,
+ r.detailsPointer.currency,
+ r.detailsPointer.masterPublicKey,
+ ]);
+ }
+
+ async checkExchangeInScope(
+ exchangeBaseUrl: string,
+ scope: ScopeInfo,
+ ): Promise<boolean> {
+ switch (scope.type) {
+ case ScopeType.Exchange: {
+ return scope.url === exchangeBaseUrl;
+ }
+ case ScopeType.Global: {
+ const exchangeDetails = await this.getExchangeDetails(exchangeBaseUrl);
+ if (!exchangeDetails) {
+ return false;
+ }
+ const gr =
+ await this.tx.globalCurrencyExchanges.indexes.byCurrencyAndUrlAndPub.get(
+ [
+ exchangeDetails.currency,
+ exchangeBaseUrl,
+ exchangeDetails.masterPublicKey,
+ ],
+ );
+ return gr != null;
+ }
+ case ScopeType.Auditor:
+ throw Error("auditor scope not supported yet");
+ default:
+ assertUnreachable(scope);
+ }
+ }
+
+ async getExchangeScopeInfo(
+ exchangeBaseUrl: string,
+ currency: string,
+ ): Promise<ScopeInfo> {
+ const det = await this.getExchangeDetails(exchangeBaseUrl);
+ if (!det) {
+ return {
+ type: ScopeType.Exchange,
+ currency: currency,
+ url: exchangeBaseUrl,
+ };
+ }
+ const globalExchangeRec =
+ await this.tx.globalCurrencyExchanges.indexes.byCurrencyAndUrlAndPub.get([
+ det.currency,
+ det.exchangeBaseUrl,
+ det.masterPublicKey,
+ ]);
+ if (globalExchangeRec) {
+ return {
+ currency: det.currency,
+ type: ScopeType.Global,
+ };
+ } else {
+ for (const aud of det.auditors) {
+ const globalAuditorRec =
+ await this.tx.globalCurrencyAuditors.indexes.byCurrencyAndUrlAndPub.get(
+ [det.currency, aud.auditor_url, aud.auditor_pub],
+ );
+ if (globalAuditorRec) {
+ return {
+ currency: det.currency,
+ type: ScopeType.Auditor,
+ url: aud.auditor_url,
+ };
+ }
+ }
+ }
+ return {
+ currency: det.currency,
+ type: ScopeType.Exchange,
+ url: det.exchangeBaseUrl,
+ };
+ }
}
diff --git a/packages/taler-wallet-core/src/denominations.ts b/packages/taler-wallet-core/src/denominations.ts
@@ -35,8 +35,9 @@ import { TaskRunResult } from "./common.js";
import {
DenominationVerificationStatus,
timestampProtocolFromDb,
+ DenominationRecord,
} from "./db-common.js";
-import { DenominationRecord } from "./db-indexeddb.js";
+import { WalletDbTransaction } from "./dbtx.js";
import { WalletExecutionContext } from "./index.js";
/**
@@ -622,10 +623,10 @@ export async function validateDenoms(
if (updatedDenoms.length > 0) {
logger.trace("writing denomination batch to db");
- await wex.runLegacyWalletDbTx(async (tx) => {
+ await wex.runWalletDbTx(async (tx) => {
for (let i = 0; i < updatedDenoms.length; i++) {
const denom = updatedDenoms[i];
- await tx.denominations.put(denom);
+ await tx.upsertDenomination(denom);
}
});
@@ -640,8 +641,8 @@ export async function processValidateDenoms(
): Promise<TaskRunResult> {
logger.trace("validating denoms in background task");
- const denoms = await wex.runLegacyWalletDbTx(async (tx) => {
- return tx.denominations.indexes.byVerificationStatus.getAll(
+ const denoms = await wex.runWalletDbTx(async (tx) => {
+ return tx.getDenominationsByVerificationStatus(
DenominationVerificationStatus.Unverified,
);
});
diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts
@@ -478,8 +478,8 @@ export interface ExchangeDetails {
auditors: ExchangeAuditor[];
globalFees: ExchangeGlobalFees[];
reserveClosingDelay: TalerProtocolDuration;
- defaultPeerPushExpiration: TalerProtocolDuration | undefined;
- shoppingUrl: string | undefined;
+ defaultPeerPushExpiration?: TalerProtocolDuration;
+ shoppingUrl?: string;
}
export async function getExchangeDetailsInTx(
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
@@ -601,13 +601,16 @@ async function migrateMaterializedTransactions(
export async function getDenomInfo(
wex: WalletExecutionContext,
- tx: WalletIndexedDbTransaction,
+ tx: WalletDbTransaction | LegacyWalletTxHandle,
exchangeBaseUrl: string,
denomPubHash: string,
): Promise<DenominationInfo | undefined> {
const key = `${exchangeBaseUrl}:${denomPubHash}`;
return wex.ws.denomInfoCache.getOrPut(key, async () => {
- const d = await tx.denominations.get([exchangeBaseUrl, denomPubHash]);
+ const d =
+ "getDenomination" in tx
+ ? await tx.getDenomination(exchangeBaseUrl, denomPubHash)
+ : await tx.denominations.get([exchangeBaseUrl, denomPubHash]);
if (d != null) {
return DenominationRecord.toDenomInfo(d);
} else {