commit 715d71e22b31ab481bd460edec67b54bf8a23001
parent 68b90ab875e8c68619b778209b2562f58a90c150
Author: Florian Dold <dold@taler.net>
Date: Sat, 18 Jul 2026 20:01:41 +0200
db: move IdbWalletTransaction to dbtx-indexeddb.ts
Diffstat:
3 files changed, 746 insertions(+), 664 deletions(-)
diff --git a/packages/taler-wallet-core/src/dbtx-indexeddb.ts b/packages/taler-wallet-core/src/dbtx-indexeddb.ts
@@ -0,0 +1,727 @@
+/*
+ This file is part of GNU Taler
+ (C) 2026 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * IndexedDB implementation of the backend-neutral {@link WalletDbTransaction}
+ * data access layer.
+ *
+ * All IndexedDB specifics (key ranges, store/index names, the
+ * <Name>Record storage types) belong here. The interface itself lives in
+ * dbtx.ts and must stay free of them.
+ */
+
+import {
+ ContactEntry,
+ CurrencySpecification,
+ MailboxConfiguration,
+ MailboxMessageRecord,
+ ScopeInfo,
+ stringifyScopeInfo,
+ assertUnreachable,
+ ScopeType,
+ WalletNotification,
+} from "@gnu-taler/taler-util";
+import { GlobalIDB } from "@gnu-taler/idb-bridge";
+import {
+ ConfigRecord,
+ WalletPeerPullCredit,
+ WalletPeerPushDebit,
+ WalletPeerPushCredit,
+ WalletPeerPullDebit,
+ WalletToken,
+ WalletDenomination,
+ 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,
+ PeerPushCreditRecord,
+ PeerPullPaymentIncomingRecord,
+ PurchaseRecord,
+ WalletIndexedDbTransaction,
+} from "./db-indexeddb.js";
+import type {
+ GetCurrencyInfoDbResult,
+ StoreCurrencyInfoDbRequest,
+ WalletDbTransaction,
+} from "./dbtx.js";
+
+
+function getActiveKeyRange() {
+ return GlobalIDB.KeyRange.bound(
+ OPERATION_STATUS_NONFINAL_FIRST,
+ OPERATION_STATUS_NONFINAL_LAST,
+ );
+}
+
+export class IdbWalletTransaction implements WalletDbTransaction {
+ tx: WalletIndexedDbTransaction;
+ constructor(tx: WalletIndexedDbTransaction) {
+ this.tx = tx;
+ }
+
+ notify(notif: WalletNotification): void {
+ this.tx.notify(notif);
+ }
+ async getCurrencyInfo(
+ scopeInfo: ScopeInfo,
+ ): Promise<GetCurrencyInfoDbResult | undefined> {
+ const tx = this.tx;
+ const s = stringifyScopeInfo(scopeInfo);
+ const res = await tx.currencyInfo.get(s);
+ if (!res) {
+ return undefined;
+ }
+ return {
+ currencySpec: res.currencySpec,
+ source: res.source,
+ };
+ }
+
+ async getConfig<T extends ConfigRecord["key"]>(
+ key: T,
+ ): Promise<Extract<ConfigRecord, { key: T }> | undefined> {
+ const tx = this.tx;
+ return (await tx.config.get(key)) as any;
+ }
+
+ async upsertConfig(record: ConfigRecord): Promise<void> {
+ const tx = this.tx;
+ await tx.config.put(record);
+ }
+
+ async upsertCurrencyInfo(req: StoreCurrencyInfoDbRequest): Promise<void> {
+ const tx = this.tx;
+ await tx.currencyInfo.put({
+ scopeInfoStr: stringifyScopeInfo(req.scopeInfo),
+ currencySpec: req.currencySpec,
+ source: req.source,
+ });
+ }
+
+ async insertCurrencyInfoUnlessExists(
+ req: StoreCurrencyInfoDbRequest,
+ ): Promise<void> {
+ const tx = this.tx;
+ const scopeInfoStr = stringifyScopeInfo(req.scopeInfo);
+ const oldRec = await tx.currencyInfo.get(scopeInfoStr);
+ if (oldRec) {
+ return;
+ }
+ await tx.currencyInfo.put({
+ scopeInfoStr: stringifyScopeInfo(req.scopeInfo),
+ currencySpec: req.currencySpec,
+ source: req.source,
+ });
+ }
+
+ async addContact(contact: ContactEntry): Promise<void> {
+ const tx = this.tx;
+ await tx.contacts.put({
+ alias: contact.alias,
+ aliasType: contact.aliasType,
+ mailboxBaseUri: contact.mailboxBaseUri,
+ mailboxAddress: contact.mailboxAddress,
+ source: contact.source,
+ petname: contact.petname,
+ });
+ }
+
+ async deleteContact(alias: string, aliasType: string): Promise<void> {
+ const tx = this.tx;
+ await tx.contacts.delete([alias, aliasType]);
+ }
+
+ async listContacts(): Promise<ContactEntry[]> {
+ const tx = this.tx;
+ const records = await tx.contacts.iter().toArray();
+ return records.map((r) => ({
+ alias: r.alias,
+ aliasType: r.aliasType,
+ mailboxBaseUri: r.mailboxBaseUri,
+ mailboxAddress: r.mailboxAddress,
+ source: r.source,
+ petname: r.petname,
+ }));
+ }
+
+ async upsertMailboxMessage(message: MailboxMessageRecord): Promise<void> {
+ const tx = this.tx;
+ await tx.mailboxMessages.put(message);
+ }
+
+ async deleteMailboxMessage(
+ originMailboxBaseUrl: string,
+ talerUri: string,
+ ): Promise<void> {
+ const tx = this.tx;
+ await tx.mailboxMessages.delete([originMailboxBaseUrl, talerUri]);
+ }
+
+ async listMailboxMessages(): Promise<MailboxMessageRecord[]> {
+ const tx = this.tx;
+ return await tx.mailboxMessages.getAll();
+ }
+
+ async getMailboxConfiguration(
+ mailboxBaseUrl: string,
+ ): Promise<MailboxConfiguration | undefined> {
+ const tx = this.tx;
+ return await tx.mailboxConfigurations.get(mailboxBaseUrl);
+ }
+
+ async upsertMailboxConfiguration(
+ mailboxConf: MailboxConfiguration,
+ ): Promise<void> {
+ const tx = this.tx;
+ await tx.mailboxConfigurations.put(mailboxConf);
+ }
+
+ async getPurchase(proposalId: string): Promise<PurchaseRecord | undefined> {
+ const tx = this.tx;
+ return await tx.purchases.get(proposalId);
+ }
+
+ async listTokens(): Promise<WalletToken[]> {
+ const tx = this.tx;
+ const records = await tx.tokens.getAll();
+ return records.map((r) => ({
+ slug: r.slug,
+ name: r.name,
+ description: r.description,
+ descriptionI18n: r.descriptionI18n,
+ extraData: r.extraData,
+ tokenIssuePub: r.tokenIssuePub,
+ purchaseId: r.purchaseId,
+ transactionId: r.transactionId,
+ choiceIndex: r.choiceIndex,
+ outputIndex: r.outputIndex,
+ repeatIndex: r.repeatIndex,
+ merchantBaseUrl: r.merchantBaseUrl,
+ kind: r.kind,
+ tokenIssuePubHash: r.tokenIssuePubHash,
+ tokenFamilyHash: r.tokenFamilyHash,
+ validAfter: r.validAfter,
+ validBefore: r.validBefore,
+ tokenIssueSig: r.tokenIssueSig,
+ tokenUsePub: r.tokenUsePub,
+ tokenUsePriv: r.tokenUsePriv,
+ tokenUseSig: r.tokenUseSig,
+ }));
+ }
+
+ async deleteToken(tokenUsePub: string): Promise<void> {
+ const tx = this.tx;
+ await tx.tokens.delete(tokenUsePub);
+ }
+
+ async getTokensByIssuePubHash(
+ tokenIssuePubHash: string,
+ ): Promise<WalletToken[]> {
+ const tx = this.tx;
+ const records =
+ await tx.tokens.indexes.byTokenIssuePubHash.getAll(tokenIssuePubHash);
+ return records.map((r) => ({
+ slug: r.slug,
+ name: r.name,
+ description: r.description,
+ descriptionI18n: r.descriptionI18n,
+ extraData: r.extraData,
+ tokenIssuePub: r.tokenIssuePub,
+ purchaseId: r.purchaseId,
+ transactionId: r.transactionId,
+ choiceIndex: r.choiceIndex,
+ outputIndex: r.outputIndex,
+ repeatIndex: r.repeatIndex,
+ merchantBaseUrl: r.merchantBaseUrl,
+ kind: r.kind,
+ tokenIssuePubHash: r.tokenIssuePubHash,
+ tokenFamilyHash: r.tokenFamilyHash,
+ validAfter: r.validAfter,
+ validBefore: r.validBefore,
+ tokenIssueSig: r.tokenIssueSig,
+ tokenUsePub: r.tokenUsePub,
+ tokenUsePriv: r.tokenUsePriv,
+ tokenUseSig: r.tokenUseSig,
+ }));
+ }
+
+ async getPeerPullCredit(
+ pursePub: string,
+ ): Promise<WalletPeerPullCredit | undefined> {
+ const tx = this.tx;
+ const r = await tx.peerPullCredit.get(pursePub);
+ if (!r) {
+ return undefined;
+ }
+ return {
+ exchangeBaseUrl: r.exchangeBaseUrl,
+ amount: r.amount,
+ estimatedAmountEffective: r.estimatedAmountEffective,
+ pursePub: r.pursePub,
+ pursePriv: r.pursePriv,
+ contractTermsHash: r.contractTermsHash,
+ mergePub: r.mergePub,
+ mergePriv: r.mergePriv,
+ contractPub: r.contractPub,
+ contractPriv: r.contractPriv,
+ contractEncNonce: r.contractEncNonce,
+ mergeTimestamp: r.mergeTimestamp,
+ mergeReserveRowId: r.mergeReserveRowId,
+ status: r.status,
+ kycPaytoHash: r.kycPaytoHash,
+ kycAccessToken: r.kycAccessToken,
+ kycLastCheckStatus: r.kycLastCheckStatus,
+ kycLastCheckCode: r.kycLastCheckCode,
+ kycLastRuleGen: r.kycLastRuleGen,
+ kycLastAmlReview: r.kycLastAmlReview,
+ kycLastDeny: r.kycLastDeny,
+ abortReason: r.abortReason,
+ failReason: r.failReason,
+ withdrawalGroupId: r.withdrawalGroupId,
+ };
+ }
+
+ async upsertPeerPullCredit(rec: WalletPeerPullCredit): Promise<void> {
+ const tx = this.tx;
+ await tx.peerPullCredit.put({
+ exchangeBaseUrl: rec.exchangeBaseUrl,
+ amount: rec.amount,
+ estimatedAmountEffective: rec.estimatedAmountEffective,
+ pursePub: rec.pursePub,
+ pursePriv: rec.pursePriv,
+ contractTermsHash: rec.contractTermsHash,
+ mergePub: rec.mergePub,
+ mergePriv: rec.mergePriv,
+ contractPub: rec.contractPub,
+ contractPriv: rec.contractPriv,
+ contractEncNonce: rec.contractEncNonce,
+ mergeTimestamp: rec.mergeTimestamp,
+ mergeReserveRowId: rec.mergeReserveRowId,
+ status: rec.status,
+ kycPaytoHash: rec.kycPaytoHash,
+ kycAccessToken: rec.kycAccessToken,
+ kycLastCheckStatus: rec.kycLastCheckStatus,
+ kycLastCheckCode: rec.kycLastCheckCode,
+ kycLastRuleGen: rec.kycLastRuleGen,
+ kycLastAmlReview: rec.kycLastAmlReview,
+ kycLastDeny: rec.kycLastDeny,
+ abortReason: rec.abortReason,
+ failReason: rec.failReason,
+ withdrawalGroupId: rec.withdrawalGroupId,
+ });
+ }
+
+ async deletePeerPullCredit(pursePub: string): Promise<void> {
+ const tx = this.tx;
+ await tx.peerPullCredit.delete(pursePub);
+ }
+
+ async getPeerPushDebit(
+ pursePub: string,
+ ): Promise<WalletPeerPushDebit | undefined> {
+ const tx = this.tx;
+ const r = await tx.peerPushDebit.get(pursePub);
+ if (!r) {
+ return undefined;
+ }
+ return {
+ exchangeBaseUrl: r.exchangeBaseUrl,
+ restrictScope: r.restrictScope,
+ amount: r.amount,
+ totalCost: r.totalCost,
+ coinSel: r.coinSel,
+ contractTermsHash: r.contractTermsHash,
+ pursePub: r.pursePub,
+ pursePriv: r.pursePriv,
+ mergePub: r.mergePub,
+ mergePriv: r.mergePriv,
+ contractPriv: r.contractPriv,
+ contractPub: r.contractPub,
+ contractEncNonce: r.contractEncNonce,
+ purseExpiration: r.purseExpiration,
+ timestampCreated: r.timestampCreated,
+ abortRefreshGroupId: r.abortRefreshGroupId,
+ abortReason: r.abortReason,
+ failReason: r.failReason,
+ status: r.status,
+ };
+ }
+
+ async upsertPeerPushDebit(rec: WalletPeerPushDebit): Promise<void> {
+ const tx = this.tx;
+ await tx.peerPushDebit.put({
+ exchangeBaseUrl: rec.exchangeBaseUrl,
+ restrictScope: rec.restrictScope,
+ amount: rec.amount,
+ totalCost: rec.totalCost,
+ coinSel: rec.coinSel,
+ contractTermsHash: rec.contractTermsHash,
+ pursePub: rec.pursePub,
+ pursePriv: rec.pursePriv,
+ mergePub: rec.mergePub,
+ mergePriv: rec.mergePriv,
+ contractPriv: rec.contractPriv,
+ contractPub: rec.contractPub,
+ contractEncNonce: rec.contractEncNonce,
+ purseExpiration: rec.purseExpiration,
+ timestampCreated: rec.timestampCreated,
+ abortRefreshGroupId: rec.abortRefreshGroupId,
+ abortReason: rec.abortReason,
+ failReason: rec.failReason,
+ status: rec.status,
+ });
+ }
+
+ async deletePeerPushDebit(pursePub: string): Promise<void> {
+ const tx = this.tx;
+ await tx.peerPushDebit.delete(pursePub);
+ }
+
+ async getPeerPushCredit(
+ peerPushCreditId: string,
+ ): Promise<WalletPeerPushCredit | undefined> {
+ const tx = this.tx;
+ const r = await tx.peerPushCredit.get(peerPushCreditId);
+ if (!r) {
+ return undefined;
+ }
+ return {
+ peerPushCreditId: r.peerPushCreditId,
+ exchangeBaseUrl: r.exchangeBaseUrl,
+ pursePub: r.pursePub,
+ mergePriv: r.mergePriv,
+ contractPriv: r.contractPriv,
+ timestamp: r.timestamp,
+ estimatedAmountEffective: r.estimatedAmountEffective,
+ contractTermsHash: r.contractTermsHash,
+ status: r.status,
+ abortReason: r.abortReason,
+ failReason: r.failReason,
+ withdrawalGroupId: r.withdrawalGroupId,
+ currency: r.currency,
+ kycPaytoHash: r.kycPaytoHash,
+ kycAccessToken: r.kycAccessToken,
+ kycLastCheckStatus: r.kycLastCheckStatus,
+ kycLastCheckCode: r.kycLastCheckCode,
+ kycLastRuleGen: r.kycLastRuleGen,
+ kycLastAmlReview: r.kycLastAmlReview,
+ kycLastDeny: r.kycLastDeny,
+ };
+ }
+
+ async upsertPeerPushCredit(rec: WalletPeerPushCredit): Promise<void> {
+ const tx = this.tx;
+ await tx.peerPushCredit.put({
+ peerPushCreditId: rec.peerPushCreditId,
+ exchangeBaseUrl: rec.exchangeBaseUrl,
+ pursePub: rec.pursePub,
+ mergePriv: rec.mergePriv,
+ contractPriv: rec.contractPriv,
+ timestamp: rec.timestamp,
+ estimatedAmountEffective: rec.estimatedAmountEffective,
+ contractTermsHash: rec.contractTermsHash,
+ status: rec.status,
+ abortReason: rec.abortReason,
+ failReason: rec.failReason,
+ withdrawalGroupId: rec.withdrawalGroupId,
+ currency: rec.currency,
+ kycPaytoHash: rec.kycPaytoHash,
+ kycAccessToken: rec.kycAccessToken,
+ kycLastCheckStatus: rec.kycLastCheckStatus,
+ kycLastCheckCode: rec.kycLastCheckCode,
+ kycLastRuleGen: rec.kycLastRuleGen,
+ kycLastAmlReview: rec.kycLastAmlReview,
+ kycLastDeny: rec.kycLastDeny,
+ });
+ }
+
+ async deletePeerPushCredit(peerPushCreditId: string): Promise<void> {
+ const tx = this.tx;
+ await tx.peerPushCredit.delete(peerPushCreditId);
+ }
+
+ async getPeerPushCreditByExchangeAndContractPriv(
+ exchangeBaseUrl: string,
+ contractPriv: string,
+ ): Promise<WalletPeerPushCredit | undefined> {
+ const tx = this.tx;
+ const r = await tx.peerPushCredit.indexes.byExchangeAndContractPriv.get([
+ exchangeBaseUrl,
+ contractPriv,
+ ]);
+ if (!r) {
+ return undefined;
+ }
+ return this.getPeerPushCredit(r.peerPushCreditId);
+ }
+
+ async getPeerPullDebit(
+ peerPullDebitId: string,
+ ): Promise<WalletPeerPullDebit | undefined> {
+ const tx = this.tx;
+ const r = await tx.peerPullDebit.get(peerPullDebitId);
+ if (!r) {
+ return undefined;
+ }
+ return {
+ peerPullDebitId: r.peerPullDebitId,
+ pursePub: r.pursePub,
+ exchangeBaseUrl: r.exchangeBaseUrl,
+ amount: r.amount,
+ contractTermsHash: r.contractTermsHash,
+ timestampCreated: r.timestampCreated,
+ contractPriv: r.contractPriv,
+ status: r.status,
+ totalCostEstimated: r.totalCostEstimated,
+ abortRefreshGroupId: r.abortRefreshGroupId,
+ abortReason: r.abortReason,
+ failReason: r.failReason,
+ coinSel: r.coinSel,
+ };
+ }
+
+ async upsertPeerPullDebit(rec: WalletPeerPullDebit): Promise<void> {
+ const tx = this.tx;
+ await tx.peerPullDebit.put({
+ peerPullDebitId: rec.peerPullDebitId,
+ pursePub: rec.pursePub,
+ exchangeBaseUrl: rec.exchangeBaseUrl,
+ amount: rec.amount,
+ contractTermsHash: rec.contractTermsHash,
+ timestampCreated: rec.timestampCreated,
+ contractPriv: rec.contractPriv,
+ status: rec.status,
+ totalCostEstimated: rec.totalCostEstimated,
+ abortRefreshGroupId: rec.abortRefreshGroupId,
+ abortReason: rec.abortReason,
+ failReason: rec.failReason,
+ coinSel: rec.coinSel,
+ });
+ }
+
+ async deletePeerPullDebit(peerPullDebitId: string): Promise<void> {
+ const tx = this.tx;
+ await tx.peerPullDebit.delete(peerPullDebitId);
+ }
+
+ async getPeerPullDebitByExchangeAndContractPriv(
+ exchangeBaseUrl: string,
+ contractPriv: string,
+ ): Promise<WalletPeerPullDebit | undefined> {
+ const tx = this.tx;
+ const r = await tx.peerPullDebit.indexes.byExchangeAndContractPriv.get([
+ exchangeBaseUrl,
+ contractPriv,
+ ]);
+ if (!r) {
+ return undefined;
+ }
+ return this.getPeerPullDebit(r.peerPullDebitId);
+ }
+
+ async upsertDenomination(rec: WalletDenomination): Promise<void> {
+ const tx = this.tx;
+ await tx.denominations.put(rec);
+ }
+
+ async getDenomination(
+ exchangeBaseUrl: string,
+ denomPubHash: string,
+ ): Promise<WalletDenomination | undefined> {
+ const tx = this.tx;
+ return await tx.denominations.get([exchangeBaseUrl, denomPubHash]);
+ }
+
+ async getDenominationsByVerificationStatus(
+ verificationStatus: DenominationVerificationStatus,
+ ): Promise<WalletDenomination[]> {
+ 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/dbtx.ts b/packages/taler-wallet-core/src/dbtx.ts
@@ -14,18 +14,31 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
+/**
+ * Backend-neutral data access layer for the wallet database.
+ *
+ * This file must only contain the {@link WalletDbTransaction} interface and the
+ * request/result types it uses. It must not depend on any storage backend:
+ * the IndexedDB implementation lives in dbtx-indexeddb.ts, and a sqlite3
+ * implementation will be added alongside it.
+ *
+ * Types crossing this interface belong in db-common.ts and are named
+ * Wallet<Name>. The <Name>Record types in db-indexeddb.ts describe the
+ * IndexedDB object stores and must not appear here.
+ *
+ * FIXME: The remaining imports from db-indexeddb.js below are a violation of
+ * that rule, and are removed in phase 0c of the DAL migration (see
+ * DB_MIGRATION_PLAN.md) by introducing Wallet<Name> counterparts for them.
+ */
+
import {
ContactEntry,
CurrencySpecification,
MailboxConfiguration,
MailboxMessageRecord,
ScopeInfo,
- stringifyScopeInfo,
- assertUnreachable,
- ScopeType,
WalletNotification,
} from "@gnu-taler/taler-util";
-import { GlobalIDB } from "@gnu-taler/idb-bridge";
import {
ConfigRecord,
WalletPeerPullCredit,
@@ -35,8 +48,6 @@ import {
WalletToken,
WalletDenomination,
DenominationVerificationStatus,
- OPERATION_STATUS_NONFINAL_FIRST,
- OPERATION_STATUS_NONFINAL_LAST,
} from "./db-common.js";
import type {
ExchangeEntryRecord,
@@ -47,14 +58,11 @@ import type {
DepositGroupRecord,
ExchangeDetailsRecord,
DonationSummaryRecord,
-} from "./db-indexeddb.js";
-import {
PeerPullCreditRecord,
PeerPushDebitRecord,
PeerPushCreditRecord,
PeerPullPaymentIncomingRecord,
PurchaseRecord,
- WalletIndexedDbTransaction,
} from "./db-indexeddb.js";
export interface GetCurrencyInfoDbResult {
@@ -268,657 +276,3 @@ export interface WalletDbTransaction {
notify(notif: WalletNotification): void;
}
-
-function getActiveKeyRange() {
- return GlobalIDB.KeyRange.bound(
- OPERATION_STATUS_NONFINAL_FIRST,
- OPERATION_STATUS_NONFINAL_LAST,
- );
-}
-
-export class IdbWalletTransaction implements WalletDbTransaction {
- tx: WalletIndexedDbTransaction;
- constructor(tx: WalletIndexedDbTransaction) {
- this.tx = tx;
- }
-
- notify(notif: WalletNotification): void {
- this.tx.notify(notif);
- }
- async getCurrencyInfo(
- scopeInfo: ScopeInfo,
- ): Promise<GetCurrencyInfoDbResult | undefined> {
- const tx = this.tx;
- const s = stringifyScopeInfo(scopeInfo);
- const res = await tx.currencyInfo.get(s);
- if (!res) {
- return undefined;
- }
- return {
- currencySpec: res.currencySpec,
- source: res.source,
- };
- }
-
- async getConfig<T extends ConfigRecord["key"]>(
- key: T,
- ): Promise<Extract<ConfigRecord, { key: T }> | undefined> {
- const tx = this.tx;
- return (await tx.config.get(key)) as any;
- }
-
- async upsertConfig(record: ConfigRecord): Promise<void> {
- const tx = this.tx;
- await tx.config.put(record);
- }
-
- async upsertCurrencyInfo(req: StoreCurrencyInfoDbRequest): Promise<void> {
- const tx = this.tx;
- await tx.currencyInfo.put({
- scopeInfoStr: stringifyScopeInfo(req.scopeInfo),
- currencySpec: req.currencySpec,
- source: req.source,
- });
- }
-
- async insertCurrencyInfoUnlessExists(
- req: StoreCurrencyInfoDbRequest,
- ): Promise<void> {
- const tx = this.tx;
- const scopeInfoStr = stringifyScopeInfo(req.scopeInfo);
- const oldRec = await tx.currencyInfo.get(scopeInfoStr);
- if (oldRec) {
- return;
- }
- await tx.currencyInfo.put({
- scopeInfoStr: stringifyScopeInfo(req.scopeInfo),
- currencySpec: req.currencySpec,
- source: req.source,
- });
- }
-
- async addContact(contact: ContactEntry): Promise<void> {
- const tx = this.tx;
- await tx.contacts.put({
- alias: contact.alias,
- aliasType: contact.aliasType,
- mailboxBaseUri: contact.mailboxBaseUri,
- mailboxAddress: contact.mailboxAddress,
- source: contact.source,
- petname: contact.petname,
- });
- }
-
- async deleteContact(alias: string, aliasType: string): Promise<void> {
- const tx = this.tx;
- await tx.contacts.delete([alias, aliasType]);
- }
-
- async listContacts(): Promise<ContactEntry[]> {
- const tx = this.tx;
- const records = await tx.contacts.iter().toArray();
- return records.map((r) => ({
- alias: r.alias,
- aliasType: r.aliasType,
- mailboxBaseUri: r.mailboxBaseUri,
- mailboxAddress: r.mailboxAddress,
- source: r.source,
- petname: r.petname,
- }));
- }
-
- async upsertMailboxMessage(message: MailboxMessageRecord): Promise<void> {
- const tx = this.tx;
- await tx.mailboxMessages.put(message);
- }
-
- async deleteMailboxMessage(
- originMailboxBaseUrl: string,
- talerUri: string,
- ): Promise<void> {
- const tx = this.tx;
- await tx.mailboxMessages.delete([originMailboxBaseUrl, talerUri]);
- }
-
- async listMailboxMessages(): Promise<MailboxMessageRecord[]> {
- const tx = this.tx;
- return await tx.mailboxMessages.getAll();
- }
-
- async getMailboxConfiguration(
- mailboxBaseUrl: string,
- ): Promise<MailboxConfiguration | undefined> {
- const tx = this.tx;
- return await tx.mailboxConfigurations.get(mailboxBaseUrl);
- }
-
- async upsertMailboxConfiguration(
- mailboxConf: MailboxConfiguration,
- ): Promise<void> {
- const tx = this.tx;
- await tx.mailboxConfigurations.put(mailboxConf);
- }
-
- async getPurchase(proposalId: string): Promise<PurchaseRecord | undefined> {
- const tx = this.tx;
- return await tx.purchases.get(proposalId);
- }
-
- async listTokens(): Promise<WalletToken[]> {
- const tx = this.tx;
- const records = await tx.tokens.getAll();
- return records.map((r) => ({
- slug: r.slug,
- name: r.name,
- description: r.description,
- descriptionI18n: r.descriptionI18n,
- extraData: r.extraData,
- tokenIssuePub: r.tokenIssuePub,
- purchaseId: r.purchaseId,
- transactionId: r.transactionId,
- choiceIndex: r.choiceIndex,
- outputIndex: r.outputIndex,
- repeatIndex: r.repeatIndex,
- merchantBaseUrl: r.merchantBaseUrl,
- kind: r.kind,
- tokenIssuePubHash: r.tokenIssuePubHash,
- tokenFamilyHash: r.tokenFamilyHash,
- validAfter: r.validAfter,
- validBefore: r.validBefore,
- tokenIssueSig: r.tokenIssueSig,
- tokenUsePub: r.tokenUsePub,
- tokenUsePriv: r.tokenUsePriv,
- tokenUseSig: r.tokenUseSig,
- }));
- }
-
- async deleteToken(tokenUsePub: string): Promise<void> {
- const tx = this.tx;
- await tx.tokens.delete(tokenUsePub);
- }
-
- async getTokensByIssuePubHash(
- tokenIssuePubHash: string,
- ): Promise<WalletToken[]> {
- const tx = this.tx;
- const records =
- await tx.tokens.indexes.byTokenIssuePubHash.getAll(tokenIssuePubHash);
- return records.map((r) => ({
- slug: r.slug,
- name: r.name,
- description: r.description,
- descriptionI18n: r.descriptionI18n,
- extraData: r.extraData,
- tokenIssuePub: r.tokenIssuePub,
- purchaseId: r.purchaseId,
- transactionId: r.transactionId,
- choiceIndex: r.choiceIndex,
- outputIndex: r.outputIndex,
- repeatIndex: r.repeatIndex,
- merchantBaseUrl: r.merchantBaseUrl,
- kind: r.kind,
- tokenIssuePubHash: r.tokenIssuePubHash,
- tokenFamilyHash: r.tokenFamilyHash,
- validAfter: r.validAfter,
- validBefore: r.validBefore,
- tokenIssueSig: r.tokenIssueSig,
- tokenUsePub: r.tokenUsePub,
- tokenUsePriv: r.tokenUsePriv,
- tokenUseSig: r.tokenUseSig,
- }));
- }
-
- async getPeerPullCredit(
- pursePub: string,
- ): Promise<WalletPeerPullCredit | undefined> {
- const tx = this.tx;
- const r = await tx.peerPullCredit.get(pursePub);
- if (!r) {
- return undefined;
- }
- return {
- exchangeBaseUrl: r.exchangeBaseUrl,
- amount: r.amount,
- estimatedAmountEffective: r.estimatedAmountEffective,
- pursePub: r.pursePub,
- pursePriv: r.pursePriv,
- contractTermsHash: r.contractTermsHash,
- mergePub: r.mergePub,
- mergePriv: r.mergePriv,
- contractPub: r.contractPub,
- contractPriv: r.contractPriv,
- contractEncNonce: r.contractEncNonce,
- mergeTimestamp: r.mergeTimestamp,
- mergeReserveRowId: r.mergeReserveRowId,
- status: r.status,
- kycPaytoHash: r.kycPaytoHash,
- kycAccessToken: r.kycAccessToken,
- kycLastCheckStatus: r.kycLastCheckStatus,
- kycLastCheckCode: r.kycLastCheckCode,
- kycLastRuleGen: r.kycLastRuleGen,
- kycLastAmlReview: r.kycLastAmlReview,
- kycLastDeny: r.kycLastDeny,
- abortReason: r.abortReason,
- failReason: r.failReason,
- withdrawalGroupId: r.withdrawalGroupId,
- };
- }
-
- async upsertPeerPullCredit(rec: WalletPeerPullCredit): Promise<void> {
- const tx = this.tx;
- await tx.peerPullCredit.put({
- exchangeBaseUrl: rec.exchangeBaseUrl,
- amount: rec.amount,
- estimatedAmountEffective: rec.estimatedAmountEffective,
- pursePub: rec.pursePub,
- pursePriv: rec.pursePriv,
- contractTermsHash: rec.contractTermsHash,
- mergePub: rec.mergePub,
- mergePriv: rec.mergePriv,
- contractPub: rec.contractPub,
- contractPriv: rec.contractPriv,
- contractEncNonce: rec.contractEncNonce,
- mergeTimestamp: rec.mergeTimestamp,
- mergeReserveRowId: rec.mergeReserveRowId,
- status: rec.status,
- kycPaytoHash: rec.kycPaytoHash,
- kycAccessToken: rec.kycAccessToken,
- kycLastCheckStatus: rec.kycLastCheckStatus,
- kycLastCheckCode: rec.kycLastCheckCode,
- kycLastRuleGen: rec.kycLastRuleGen,
- kycLastAmlReview: rec.kycLastAmlReview,
- kycLastDeny: rec.kycLastDeny,
- abortReason: rec.abortReason,
- failReason: rec.failReason,
- withdrawalGroupId: rec.withdrawalGroupId,
- });
- }
-
- async deletePeerPullCredit(pursePub: string): Promise<void> {
- const tx = this.tx;
- await tx.peerPullCredit.delete(pursePub);
- }
-
- async getPeerPushDebit(
- pursePub: string,
- ): Promise<WalletPeerPushDebit | undefined> {
- const tx = this.tx;
- const r = await tx.peerPushDebit.get(pursePub);
- if (!r) {
- return undefined;
- }
- return {
- exchangeBaseUrl: r.exchangeBaseUrl,
- restrictScope: r.restrictScope,
- amount: r.amount,
- totalCost: r.totalCost,
- coinSel: r.coinSel,
- contractTermsHash: r.contractTermsHash,
- pursePub: r.pursePub,
- pursePriv: r.pursePriv,
- mergePub: r.mergePub,
- mergePriv: r.mergePriv,
- contractPriv: r.contractPriv,
- contractPub: r.contractPub,
- contractEncNonce: r.contractEncNonce,
- purseExpiration: r.purseExpiration,
- timestampCreated: r.timestampCreated,
- abortRefreshGroupId: r.abortRefreshGroupId,
- abortReason: r.abortReason,
- failReason: r.failReason,
- status: r.status,
- };
- }
-
- async upsertPeerPushDebit(rec: WalletPeerPushDebit): Promise<void> {
- const tx = this.tx;
- await tx.peerPushDebit.put({
- exchangeBaseUrl: rec.exchangeBaseUrl,
- restrictScope: rec.restrictScope,
- amount: rec.amount,
- totalCost: rec.totalCost,
- coinSel: rec.coinSel,
- contractTermsHash: rec.contractTermsHash,
- pursePub: rec.pursePub,
- pursePriv: rec.pursePriv,
- mergePub: rec.mergePub,
- mergePriv: rec.mergePriv,
- contractPriv: rec.contractPriv,
- contractPub: rec.contractPub,
- contractEncNonce: rec.contractEncNonce,
- purseExpiration: rec.purseExpiration,
- timestampCreated: rec.timestampCreated,
- abortRefreshGroupId: rec.abortRefreshGroupId,
- abortReason: rec.abortReason,
- failReason: rec.failReason,
- status: rec.status,
- });
- }
-
- async deletePeerPushDebit(pursePub: string): Promise<void> {
- const tx = this.tx;
- await tx.peerPushDebit.delete(pursePub);
- }
-
- async getPeerPushCredit(
- peerPushCreditId: string,
- ): Promise<WalletPeerPushCredit | undefined> {
- const tx = this.tx;
- const r = await tx.peerPushCredit.get(peerPushCreditId);
- if (!r) {
- return undefined;
- }
- return {
- peerPushCreditId: r.peerPushCreditId,
- exchangeBaseUrl: r.exchangeBaseUrl,
- pursePub: r.pursePub,
- mergePriv: r.mergePriv,
- contractPriv: r.contractPriv,
- timestamp: r.timestamp,
- estimatedAmountEffective: r.estimatedAmountEffective,
- contractTermsHash: r.contractTermsHash,
- status: r.status,
- abortReason: r.abortReason,
- failReason: r.failReason,
- withdrawalGroupId: r.withdrawalGroupId,
- currency: r.currency,
- kycPaytoHash: r.kycPaytoHash,
- kycAccessToken: r.kycAccessToken,
- kycLastCheckStatus: r.kycLastCheckStatus,
- kycLastCheckCode: r.kycLastCheckCode,
- kycLastRuleGen: r.kycLastRuleGen,
- kycLastAmlReview: r.kycLastAmlReview,
- kycLastDeny: r.kycLastDeny,
- };
- }
-
- async upsertPeerPushCredit(rec: WalletPeerPushCredit): Promise<void> {
- const tx = this.tx;
- await tx.peerPushCredit.put({
- peerPushCreditId: rec.peerPushCreditId,
- exchangeBaseUrl: rec.exchangeBaseUrl,
- pursePub: rec.pursePub,
- mergePriv: rec.mergePriv,
- contractPriv: rec.contractPriv,
- timestamp: rec.timestamp,
- estimatedAmountEffective: rec.estimatedAmountEffective,
- contractTermsHash: rec.contractTermsHash,
- status: rec.status,
- abortReason: rec.abortReason,
- failReason: rec.failReason,
- withdrawalGroupId: rec.withdrawalGroupId,
- currency: rec.currency,
- kycPaytoHash: rec.kycPaytoHash,
- kycAccessToken: rec.kycAccessToken,
- kycLastCheckStatus: rec.kycLastCheckStatus,
- kycLastCheckCode: rec.kycLastCheckCode,
- kycLastRuleGen: rec.kycLastRuleGen,
- kycLastAmlReview: rec.kycLastAmlReview,
- kycLastDeny: rec.kycLastDeny,
- });
- }
-
- async deletePeerPushCredit(peerPushCreditId: string): Promise<void> {
- const tx = this.tx;
- await tx.peerPushCredit.delete(peerPushCreditId);
- }
-
- async getPeerPushCreditByExchangeAndContractPriv(
- exchangeBaseUrl: string,
- contractPriv: string,
- ): Promise<WalletPeerPushCredit | undefined> {
- const tx = this.tx;
- const r = await tx.peerPushCredit.indexes.byExchangeAndContractPriv.get([
- exchangeBaseUrl,
- contractPriv,
- ]);
- if (!r) {
- return undefined;
- }
- return this.getPeerPushCredit(r.peerPushCreditId);
- }
-
- async getPeerPullDebit(
- peerPullDebitId: string,
- ): Promise<WalletPeerPullDebit | undefined> {
- const tx = this.tx;
- const r = await tx.peerPullDebit.get(peerPullDebitId);
- if (!r) {
- return undefined;
- }
- return {
- peerPullDebitId: r.peerPullDebitId,
- pursePub: r.pursePub,
- exchangeBaseUrl: r.exchangeBaseUrl,
- amount: r.amount,
- contractTermsHash: r.contractTermsHash,
- timestampCreated: r.timestampCreated,
- contractPriv: r.contractPriv,
- status: r.status,
- totalCostEstimated: r.totalCostEstimated,
- abortRefreshGroupId: r.abortRefreshGroupId,
- abortReason: r.abortReason,
- failReason: r.failReason,
- coinSel: r.coinSel,
- };
- }
-
- async upsertPeerPullDebit(rec: WalletPeerPullDebit): Promise<void> {
- const tx = this.tx;
- await tx.peerPullDebit.put({
- peerPullDebitId: rec.peerPullDebitId,
- pursePub: rec.pursePub,
- exchangeBaseUrl: rec.exchangeBaseUrl,
- amount: rec.amount,
- contractTermsHash: rec.contractTermsHash,
- timestampCreated: rec.timestampCreated,
- contractPriv: rec.contractPriv,
- status: rec.status,
- totalCostEstimated: rec.totalCostEstimated,
- abortRefreshGroupId: rec.abortRefreshGroupId,
- abortReason: rec.abortReason,
- failReason: rec.failReason,
- coinSel: rec.coinSel,
- });
- }
-
- async deletePeerPullDebit(peerPullDebitId: string): Promise<void> {
- const tx = this.tx;
- await tx.peerPullDebit.delete(peerPullDebitId);
- }
-
- async getPeerPullDebitByExchangeAndContractPriv(
- exchangeBaseUrl: string,
- contractPriv: string,
- ): Promise<WalletPeerPullDebit | undefined> {
- const tx = this.tx;
- const r = await tx.peerPullDebit.indexes.byExchangeAndContractPriv.get([
- exchangeBaseUrl,
- contractPriv,
- ]);
- if (!r) {
- return undefined;
- }
- return this.getPeerPullDebit(r.peerPullDebitId);
- }
-
- async upsertDenomination(rec: WalletDenomination): Promise<void> {
- const tx = this.tx;
- await tx.denominations.put(rec);
- }
-
- async getDenomination(
- exchangeBaseUrl: string,
- denomPubHash: string,
- ): Promise<WalletDenomination | undefined> {
- const tx = this.tx;
- return await tx.denominations.get([exchangeBaseUrl, denomPubHash]);
- }
-
- async getDenominationsByVerificationStatus(
- verificationStatus: DenominationVerificationStatus,
- ): Promise<WalletDenomination[]> {
- 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/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
@@ -330,7 +330,8 @@ import {
openTalerDatabase,
walletDbFixups,
} from "./db-indexeddb.js";
-import { IdbWalletTransaction, WalletDbTransaction } from "./dbtx.js";
+import { WalletDbTransaction } from "./dbtx.js";
+import { IdbWalletTransaction } from "./dbtx-indexeddb.js";
import {
isCandidateWithdrawableDenomRec,
isWithdrawableDenom,