diff options
8 files changed, 169 insertions, 181 deletions
diff --git a/packages/taler-wallet-core/src/host-impl.qtart.ts b/packages/taler-wallet-core/src/host-impl.qtart.ts index 686374beb..0fc346b44 100644 --- a/packages/taler-wallet-core/src/host-impl.qtart.ts +++ b/packages/taler-wallet-core/src/host-impl.qtart.ts @@ -30,20 +30,19 @@ import type { } from "@gnu-taler/idb-bridge"; // eslint-disable-next-line no-duplicate-imports import { + AccessStats, BridgeIDBFactory, MemoryBackend, createSqliteBackend, shimIndexedDB, } from "@gnu-taler/idb-bridge"; -import { AccessStats } from "@gnu-taler/idb-bridge"; -import { SynchronousCryptoWorkerFactoryPlain } from "./crypto/workers/synchronousWorkerFactoryPlain.js"; -import { openTalerDatabase } from "./index.js"; -import { Logger, enableNativeLogging } from "@gnu-taler/taler-util"; +import { Logger } from "@gnu-taler/taler-util"; import { createPlatformHttpLib } from "@gnu-taler/taler-util/http"; -import { SetTimeoutTimerAPI } from "./util/timer.js"; -import { Wallet } from "./wallet.js"; import { qjsOs, qjsStd } from "@gnu-taler/taler-util/qtart"; +import { SynchronousCryptoWorkerFactoryPlain } from "./crypto/workers/synchronousWorkerFactoryPlain.js"; import { DefaultNodeWalletArgs, makeTempfileId } from "./host-common.js"; +import { SetTimeoutTimerAPI } from "./util/timer.js"; +import { Wallet } from "./wallet.js"; const logger = new Logger("host-impl.qtart.ts"); diff --git a/packages/taler-wallet-core/src/internal-wallet-state.ts b/packages/taler-wallet-core/src/internal-wallet-state.ts index 20f8a7511..b1389a359 100644 --- a/packages/taler-wallet-core/src/internal-wallet-state.ts +++ b/packages/taler-wallet-core/src/internal-wallet-state.ts @@ -55,6 +55,7 @@ import { import { TimerGroup } from "./util/timer.js"; import { WalletConfig } from "./wallet-api-types.js"; import { IDBFactory } from "@gnu-taler/idb-bridge"; +import { ReadyExchangeSummary } from "./index.js"; export const EXCHANGE_COINS_LOCK = "exchange-coins-lock"; export const EXCHANGE_RESERVES_LOCK = "exchange-reserves-lock"; @@ -107,17 +108,14 @@ export interface ExchangeOperations { }>, exchangeBaseUrl: string, ): Promise<ExchangeDetailsRecord | undefined>; - updateExchangeFromUrl( + fetchFreshExchange( ws: InternalWalletState, baseUrl: string, options?: { forceNow?: boolean; cancellationToken?: CancellationToken; }, - ): Promise<{ - exchange: ExchangeEntryRecord; - exchangeDetails: ExchangeDetailsRecord; - }>; + ): Promise<ReadyExchangeSummary>; } export interface RecoupOperations { diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index bf7d4424a..fe6060499 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -37,6 +37,7 @@ import { ExchangeGlobalFees, ExchangeListItem, ExchangeSignKeyJson, + ExchangeTosStatus, ExchangeWireAccount, ExchangesListResponse, FeeDescription, @@ -84,12 +85,15 @@ import { ExchangeEntryDbRecordStatus, ExchangeEntryDbUpdateStatus, OpenedPromise, + PendingTaskType, WalletDbReadWriteTransaction, createTimeline, isWithdrawableDenom, openPromise, selectBestForOverlappingDenominations, selectMinimumFee, + timestampOptionalAbsoluteFromDb, + timestampOptionalPreciseFromDb, timestampPreciseFromDb, timestampPreciseToDb, timestampProtocolToDb, @@ -102,9 +106,11 @@ import { TaskIdentifiers, TaskRunResult, TaskRunResultType, + constructTaskIdentifier, getExchangeState, getExchangeTosStatusFromRecord, makeExchangeListItem, + runTaskWithErrorReporting, } from "./common.js"; const logger = new Logger("exchanges.ts"); @@ -629,7 +635,7 @@ async function downloadTosFromAcceptedFormat( * If the update is forced, the exchange is put into an updating state * even if the old information should still be up to date. * - * For backwards compatibility, if the exchange entry doesn't exist, + * If the exchange entry doesn't exist, * a new ephemeral entry is created. */ export async function startUpdateExchangeEntry( @@ -740,76 +746,18 @@ export function createNotificationWaiter( } /** - * Wait until an exchange entry got successfully updated. - * - * Reject with an exception if the update encountered an error. + * Basic information about an exchange in a ready state. */ -export async function waitExchangeEntryUpdated( - ws: InternalWalletState, - exchangeBaseUrl: string, - cancellationToken?: CancellationToken, -): Promise<{ - exchange: ExchangeEntryRecord; - exchangeDetails: ExchangeDetailsRecord; -}> { - exchangeBaseUrl = canonicalizeBaseUrl(exchangeBaseUrl); - - const waiter = createNotificationWaiter( - ws, - (notif) => - notif.type == NotificationType.ExchangeStateTransition && - notif.exchangeBaseUrl === exchangeBaseUrl, - ); - - const taskId = TaskIdentifiers.forExchangeUpdateFromUrl(exchangeBaseUrl); - - while (1) { - const { exchange, retryRecord } = await ws.db - .mktx((x) => [x.exchanges, x.exchangeDetails, x.operationRetries]) - .runReadOnly(async (tx) => { - const exchange = await tx.exchanges.get(exchangeBaseUrl); - const retryRecord = await tx.operationRetries.get(taskId); - return { exchange, retryRecord }; - }); - - if (!exchange) { - throw Error("exchange does not exist anymore"); - } - - switch (exchange.updateStatus) { - case ExchangeEntryDbUpdateStatus.Ready: - const details = await ws.db - .mktx((x) => [x.exchanges, x.exchangeDetails]) - .runReadOnly(async (tx) => { - return getExchangeDetails(tx, exchangeBaseUrl); - }); - if (!details) { - throw Error("exchange entry inconsistent"); - } - waiter.cancel(); - return { exchange, exchangeDetails: details }; - case ExchangeEntryDbUpdateStatus.ReadyUpdate: - case ExchangeEntryDbUpdateStatus.InitialUpdate: { - waiter.cancel(); - if (retryRecord?.lastError) { - throw TalerError.fromUncheckedDetail(retryRecord.lastError); - } - break; - } - case ExchangeEntryDbUpdateStatus.UnavailableUpdate: - waiter.cancel(); - if (retryRecord?.lastError) { - throw TalerError.fromUncheckedDetail(retryRecord.lastError); - } else { - throw Error( - "updating exchange failed, error info unavailable (bug!)", - ); - } - } - - await waiter.waitNext(); - } - throw Error("not reached"); +export interface ReadyExchangeSummary { + exchangeBaseUrl: string; + currency: string; + masterPub: string; + tosStatus: ExchangeTosStatus; + tosAcceptedEtag: string | undefined; + tosCurrentEtag: string | undefined; + wireInfo: WireInfo; + protocolVersionRange: string; + tosAcceptedTimestamp: TalerPreciseTimestamp | undefined; } /** @@ -834,21 +782,81 @@ export async function fetchFreshExchange( forceUpdate?: boolean; expectedMasterPub?: string; } = {}, -): Promise<{ - exchange: ExchangeEntryRecord; - exchangeDetails: ExchangeDetailsRecord; -}> { +): Promise<ReadyExchangeSummary> { const canonUrl = canonicalizeBaseUrl(baseUrl); - await startUpdateExchangeEntry(ws, canonUrl, { - forceUpdate: options.forceUpdate, + const operationId = constructTaskIdentifier({ + tag: PendingTaskType.ExchangeUpdate, + exchangeBaseUrl: canonUrl, }); - const res = await waitExchangeEntryUpdated( - ws, - canonUrl, - options.cancellationToken, - ); + + const oldExchange = await ws.db + .mktx((x) => [x.exchanges]) + .runReadOnly(async (tx) => { + return tx.exchanges.get(canonUrl); + }); + + let needsUpdate = false; + + if (!oldExchange || options.forceUpdate) { + needsUpdate = true; + await startUpdateExchangeEntry(ws, canonUrl, { + forceUpdate: options.forceUpdate, + }); + } else { + const nextUpdate = timestampOptionalAbsoluteFromDb( + oldExchange.nextUpdateStamp, + ); + if (nextUpdate == null || AbsoluteTime.isExpired(nextUpdate)) { + needsUpdate = true; + } + } + + if (needsUpdate) { + await runTaskWithErrorReporting(ws, operationId, () => + updateExchangeFromUrlHandler(ws, canonUrl), + ); + } + + const { exchange, exchangeDetails } = await ws.db + .mktx((x) => [x.exchanges, x.exchangeDetails]) + .runReadOnly(async (tx) => { + const exchange = await tx.exchanges.get(canonUrl); + const exchangeDetails = await getExchangeDetails(tx, canonUrl); + return { exchange, exchangeDetails }; + }); + + if (!exchange) { + throw Error("exchange entry does not exist anymore"); + } + + switch (exchange.updateStatus) { + case ExchangeEntryDbUpdateStatus.Ready: + case ExchangeEntryDbUpdateStatus.ReadyUpdate: + break; + default: + throw Error("unable to update exchange"); + } + + if (!exchangeDetails) { + throw Error("invariant failed"); + } + + const res: ReadyExchangeSummary = { + currency: exchangeDetails.currency, + exchangeBaseUrl: canonUrl, + masterPub: exchangeDetails.masterPublicKey, + tosStatus: getExchangeTosStatusFromRecord(exchange), + tosAcceptedEtag: exchange.tosAcceptedEtag, + wireInfo: exchangeDetails.wireInfo, + protocolVersionRange: exchangeDetails.protocolVersionRange, + tosCurrentEtag: exchange.tosCurrentEtag, + tosAcceptedTimestamp: timestampOptionalPreciseFromDb( + exchange.tosAcceptedTimestamp, + ), + }; + if (options.expectedMasterPub) { - if (res.exchangeDetails.masterPublicKey !== options.expectedMasterPub) { + if (res.masterPub !== options.expectedMasterPub) { throw Error( "public key of the exchange does not match expected public key", ); @@ -1187,10 +1195,7 @@ export async function getExchangeTos( acceptLanguage?: string, ): Promise<GetExchangeTosResult> { // FIXME: download ToS in acceptable format if passed! - const { exchange, exchangeDetails } = await fetchFreshExchange( - ws, - exchangeBaseUrl, - ); + const exch = await fetchFreshExchange(ws, exchangeBaseUrl); const tosDownload = await downloadTosFromAcceptedFormat( ws, @@ -1211,12 +1216,12 @@ export async function getExchangeTos( }); return { - acceptedEtag: exchange.tosAcceptedEtag, + acceptedEtag: exch.tosAcceptedEtag, currentEtag: tosDownload.tosEtag, content: tosDownload.tosText, contentType: tosDownload.tosContentType, contentLanguage: tosDownload.tosContentLanguage, - tosStatus: getExchangeTosStatusFromRecord(exchange), + tosStatus: exch.tosStatus, tosAvailableLanguages: tosDownload.tosAvailableLanguages, }; } @@ -1364,26 +1369,29 @@ export async function getExchangeDetailedInfo( const transferFees = Object.entries( exchange.info.wireInfo.feesForType, - ).reduce((prev, [wireType, infoForType]) => { - const feesByGroup = [ - ...infoForType.map((w) => ({ - ...w, - fee: Amounts.stringify(w.closingFee), - group: "closing", - })), - ...infoForType.map((w) => ({ ...w, fee: w.wireFee, group: "wire" })), - ]; - prev[wireType] = createTimeline( - feesByGroup, - "sig", - "startStamp", - "endStamp", - "fee", - "group", - selectMinimumFee, - ); - return prev; - }, {} as Record<string, FeeDescription[]>); + ).reduce( + (prev, [wireType, infoForType]) => { + const feesByGroup = [ + ...infoForType.map((w) => ({ + ...w, + fee: Amounts.stringify(w.closingFee), + group: "closing", + })), + ...infoForType.map((w) => ({ ...w, fee: w.wireFee, group: "wire" })), + ]; + prev[wireType] = createTimeline( + feesByGroup, + "sig", + "startStamp", + "endStamp", + "fee", + "group", + selectMinimumFee, + ); + return prev; + }, + {} as Record<string, FeeDescription[]>, + ); const globalFeesByGroup = [ ...exchange.info.globalFees.map((w) => ({ diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts b/packages/taler-wallet-core/src/operations/pay-merchant.ts index 17a879d90..078f0faf9 100644 --- a/packages/taler-wallet-core/src/operations/pay-merchant.ts +++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts @@ -1278,7 +1278,7 @@ export async function generateDepositPermissions( * Run the operation handler for a payment * and return the result as a {@link ConfirmPayResult}. */ -export async function runPayForConfirmPay( +async function runPayForConfirmPay( ws: InternalWalletState, proposalId: string, ): Promise<ConfirmPayResult> { diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index 3bbbc2a4b..5ea8fae23 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -99,11 +99,7 @@ import { TaskRunResult, TaskRunResultType, } from "./common.js"; -import { - fetchFreshExchange, - startUpdateExchangeEntry, - waitExchangeEntryUpdated, -} from "./exchanges.js"; +import { fetchFreshExchange } from "./exchanges.js"; import { constructTransactionIdentifier, notifyTransition, @@ -226,10 +222,7 @@ async function provideRefreshSession( const { refreshGroup, coin } = d; - const { exchange } = await fetchFreshExchange(ws, coin.exchangeBaseUrl); - if (!exchange) { - throw Error("db inconsistent: exchange of coin not found"); - } + const exch = await fetchFreshExchange(ws, coin.exchangeBaseUrl); // FIXME: use helper functions from withdraw.ts // to update and filter withdrawable denoms. @@ -240,7 +233,7 @@ async function provideRefreshSession( const oldDenom = await ws.getDenomInfo( ws, tx, - exchange.baseUrl, + exch.exchangeBaseUrl, coin.denomPubHash, ); @@ -251,7 +244,7 @@ async function provideRefreshSession( // FIXME: use an index here, based on the withdrawal expiration time. const availableDenoms: DenominationRecord[] = await tx.denominations.indexes.byExchangeBaseUrl - .iter(exchange.baseUrl) + .iter(exch.exchangeBaseUrl) .toArray(); const availableAmount = Amounts.sub( @@ -872,7 +865,7 @@ export async function processRefreshGroup( return TaskRunResult.finished(); } // Process refresh sessions of the group in parallel. - logger.trace("processing refresh sessions for old coins"); + logger.trace("processing refresh sessions for $ old coins"); let errors: TalerErrorDetail[] = []; let inShutdown = false; const ps = refreshGroup.oldCoinPubs.map((x, i) => diff --git a/packages/taler-wallet-core/src/operations/testing.ts b/packages/taler-wallet-core/src/operations/testing.ts index a03d54d3a..d75fb54a7 100644 --- a/packages/taler-wallet-core/src/operations/testing.ts +++ b/packages/taler-wallet-core/src/operations/testing.ts @@ -581,7 +581,7 @@ export async function runIntegrationTest2( const exchangeInfo = await fetchFreshExchange(ws, args.exchangeBaseUrl); - const currency = exchangeInfo.exchangeDetails.currency; + const currency = exchangeInfo.currency; const amountToWithdraw = Amounts.parseOrThrow(`${currency}:10`); const amountToSpend = Amounts.parseOrThrow(`${currency}:2`); diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 8a718dd49..3dffab7d6 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -136,6 +136,7 @@ import { getExchangeDetails, getExchangePaytoUri, fetchFreshExchange, + ReadyExchangeSummary, } from "./exchanges.js"; import { TransitionInfo, @@ -616,9 +617,10 @@ export async function getCandidateWithdrawalDenoms( return await ws.db .mktx((x) => [x.denominations]) .runReadOnly(async (tx) => { - const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl.getAll( - exchangeBaseUrl, - ); + const allDenoms = + await tx.denominations.indexes.byExchangeBaseUrl.getAll( + exchangeBaseUrl, + ); return allDenoms.filter((d) => isWithdrawableDenom(d, ws.config.testing.denomselAllowLate), ); @@ -870,10 +872,10 @@ async function handleKycRequired( amlStatus === AmlStatus.normal || amlStatus === undefined ? WithdrawalGroupStatus.PendingKyc : amlStatus === AmlStatus.pending - ? WithdrawalGroupStatus.PendingAml - : amlStatus === AmlStatus.fronzen - ? WithdrawalGroupStatus.SuspendedAml - : assertUnreachable(amlStatus); + ? WithdrawalGroupStatus.PendingAml + : amlStatus === AmlStatus.fronzen + ? WithdrawalGroupStatus.SuspendedAml + : assertUnreachable(amlStatus); notificationKycUrl = kycUrl; @@ -1508,10 +1510,7 @@ async function processWithdrawalGroupPendingReady( withdrawalGroupId, }); - await ws.exchangeOps.updateExchangeFromUrl( - ws, - withdrawalGroup.exchangeBaseUrl, - ); + await ws.exchangeOps.fetchFreshExchange(ws, withdrawalGroup.exchangeBaseUrl); if (withdrawalGroup.denomsSel.selectedDenoms.length === 0) { logger.warn("Finishing empty withdrawal group (no denoms)"); @@ -1549,9 +1548,8 @@ async function processWithdrawalGroupPendingReady( await ws.db .mktx((x) => [x.planchets]) .runReadOnly(async (tx) => { - const planchets = await tx.planchets.indexes.byGroup.getAll( - withdrawalGroupId, - ); + const planchets = + await tx.planchets.indexes.byGroup.getAll(withdrawalGroupId); for (const p of planchets) { if (p.planchetStatus === PlanchetStatus.WithdrawalDone) { wgContext.planchetsFinished.add(p.coinPub); @@ -1759,19 +1757,18 @@ export async function getExchangeWithdrawalInfo( ageRestricted: number | undefined, ): Promise<ExchangeWithdrawalDetails> { logger.trace("updating exchange"); - const { exchange, exchangeDetails } = - await ws.exchangeOps.updateExchangeFromUrl(ws, exchangeBaseUrl); + const exchange = await ws.exchangeOps.fetchFreshExchange(ws, exchangeBaseUrl); - if (exchangeDetails.currency != instructedAmount.currency) { + if (exchange.currency != instructedAmount.currency) { // Specifiying the amount in the conversion input currency is not yet supported. // We might add support for it later. throw new Error( - `withdrawal only supported when specifying target currency ${exchangeDetails.currency}`, + `withdrawal only supported when specifying target currency ${exchange.currency}`, ); } const withdrawalAccountsList = await fetchWithdrawalAccountInfo(ws, { - exchangeDetails, + exchange, instructedAmount, }); @@ -1799,7 +1796,7 @@ export async function getExchangeWithdrawalInfo( const exchangeWireAccounts: string[] = []; - for (const account of exchangeDetails.wireInfo.accounts) { + for (const account of exchange.wireInfo.accounts) { exchangeWireAccounts.push(account.payto_uri); } @@ -1839,17 +1836,18 @@ export async function getExchangeWithdrawalInfo( const possibleDenoms = await ws.db .mktx((x) => [x.denominations]) .runReadOnly(async (tx) => { - const ds = await tx.denominations.indexes.byExchangeBaseUrl.getAll( - exchangeBaseUrl, - ); + const ds = + await tx.denominations.indexes.byExchangeBaseUrl.getAll( + exchangeBaseUrl, + ); return ds.filter((x) => x.isOffered); }); let versionMatch; - if (exchangeDetails.protocolVersionRange) { + if (exchange.protocolVersionRange) { versionMatch = LibtoolVersion.compare( WALLET_EXCHANGE_PROTOCOL_VERSION, - exchangeDetails.protocolVersionRange, + exchange.protocolVersionRange, ); if ( @@ -1859,7 +1857,7 @@ export async function getExchangeWithdrawalInfo( ) { logger.warn( `wallet's support for exchange protocol version ${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` + - `(exchange has ${exchangeDetails.protocolVersionRange}), checking for updates`, + `(exchange has ${exchange.protocolVersionRange}), checking for updates`, ); } } @@ -1871,7 +1869,7 @@ export async function getExchangeWithdrawalInfo( } } - const paytoUris = exchangeDetails.wireInfo.accounts.map((x) => x.payto_uri); + const paytoUris = exchange.wireInfo.accounts.map((x) => x.payto_uri); if (!paytoUris) { throw Error("exchange is in invalid state"); } @@ -1881,7 +1879,7 @@ export async function getExchangeWithdrawalInfo( exchangePaytoUris: paytoUris, exchangeWireAccounts, exchangeCreditAccountDetails: withdrawalAccountsList, - exchangeVersion: exchangeDetails.protocolVersionRange || "unknown", + exchangeVersion: exchange.protocolVersionRange || "unknown", numOfferedDenoms: possibleDenoms.length, selectedDenoms, // FIXME: delete this field / replace by something we can display to the user @@ -1923,7 +1921,7 @@ export async function getWithdrawalDetailsForUri( // FIXME: right now the exchange gets permanently added, // we might want to only temporarily add it. try { - await ws.exchangeOps.updateExchangeFromUrl(ws, info.suggestedExchange); + await ws.exchangeOps.fetchFreshExchange(ws, info.suggestedExchange); } catch (e) { // We still continued if it failed, as other exchanges might be available. // We don't want to fail if the bank-suggested exchange is broken/offline. @@ -2289,7 +2287,6 @@ export interface PrepareCreateWithdrawalGroupResult { creationInfo?: { amount: AmountJson; canonExchange: string; - exchangeDetails: ExchangeDetailsRecord; }; } @@ -2376,7 +2373,6 @@ export async function internalPrepareCreateWithdrawalGroup( }; const exchangeInfo = await fetchFreshExchange(ws, canonExchange); - const exchangeDetails = exchangeInfo.exchangeDetails; const transactionId = constructTransactionIdentifier({ tag: TransactionType.Withdrawal, withdrawalGroupId: withdrawalGroup.withdrawalGroupId, @@ -2388,7 +2384,6 @@ export async function internalPrepareCreateWithdrawalGroup( creationInfo: { canonExchange, amount, - exchangeDetails, }, }; } @@ -2411,8 +2406,6 @@ export async function internalPerformCreateWithdrawalGroup( if (!prep.creationInfo) { return { withdrawalGroup, transitionInfo: undefined }; } - const { amount, canonExchange, exchangeDetails } = prep.creationInfo; - await tx.withdrawalGroups.add(withdrawalGroup); await tx.reserves.put({ reservePub: withdrawalGroup.reservePub, @@ -2529,13 +2522,13 @@ export async function acceptWithdrawalFromUri( withdrawInfo.wireTypes, ); - const { exchangeDetails } = await ws.exchangeOps.updateExchangeFromUrl( + const exchange = await ws.exchangeOps.fetchFreshExchange( ws, selectedExchange, ); const withdrawalAccountList = await fetchWithdrawalAccountInfo(ws, { - exchangeDetails, + exchange, instructedAmount: withdrawInfo.amount, }); @@ -2666,14 +2659,14 @@ async function fetchAccount( async function fetchWithdrawalAccountInfo( ws: InternalWalletState, req: { - exchangeDetails: ExchangeDetailsRecord; + exchange: ReadyExchangeSummary; instructedAmount: AmountJson; reservePub?: string; }, ): Promise<WithdrawalExchangeAccountDetails[]> { - const { exchangeDetails, instructedAmount } = req; + const { exchange, instructedAmount } = req; const withdrawalAccounts: WithdrawalExchangeAccountDetails[] = []; - for (let acct of exchangeDetails.wireInfo.accounts) { + for (let acct of exchange.wireInfo.accounts) { const acctInfo = await fetchAccount( ws, req.instructedAmount, @@ -2704,12 +2697,9 @@ export async function createManualWithdrawal( ): Promise<AcceptManualWithdrawalResult> { const { exchangeBaseUrl } = req; const amount = Amounts.parseOrThrow(req.amount); - const { exchangeDetails } = await ws.exchangeOps.updateExchangeFromUrl( - ws, - exchangeBaseUrl, - ); + const exchange = await ws.exchangeOps.fetchFreshExchange(ws, exchangeBaseUrl); - if (exchangeDetails.currency != amount.currency) { + if (exchange.currency != amount.currency) { throw Error( "manual withdrawal with conversion from foreign currency is not yet supported", ); @@ -2719,7 +2709,7 @@ export async function createManualWithdrawal( ); const withdrawalAccountsList = await fetchWithdrawalAccountInfo(ws, { - exchangeDetails, + exchange, instructedAmount: amount, reservePub: reserveKeyPair.pub, }); diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index da6ffa41f..2d422e59c 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -844,12 +844,12 @@ async function handlePrepareWithdrawExchange( } const exchangeBaseUrl = parsedUri.exchangeBaseUrl; const exchange = await fetchFreshExchange(ws, exchangeBaseUrl); - if (exchange.exchangeDetails.masterPublicKey != parsedUri.exchangePub) { + if (exchange.masterPub != parsedUri.exchangePub) { throw Error("mismatch of exchange master public key (URI vs actual)"); } if (parsedUri.amount) { const amt = Amounts.parseOrThrow(parsedUri.amount); - if (amt.currency !== exchange.exchangeDetails.currency) { + if (amt.currency !== exchange.currency) { throw Error("mismatch of currency (URI vs exchange)"); } } @@ -1689,7 +1689,7 @@ class InternalWalletStateImpl implements InternalWalletState { exchangeOps: ExchangeOperations = { getExchangeDetails, - updateExchangeFromUrl: fetchFreshExchange, + fetchFreshExchange, }; recoupOps: RecoupOperations = { |