From 523280b3862b528512ff93c651bc0d9ed632fbf6 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 27 Feb 2024 17:39:58 +0100 Subject: wallet-core: thread through wallet execution context --- packages/taler-wallet-core/src/pay-merchant.ts | 428 ++++++++++++------------- 1 file changed, 200 insertions(+), 228 deletions(-) (limited to 'packages/taler-wallet-core/src/pay-merchant.ts') diff --git a/packages/taler-wallet-core/src/pay-merchant.ts b/packages/taler-wallet-core/src/pay-merchant.ts index 73dc59ba2..a576930ba 100644 --- a/packages/taler-wallet-core/src/pay-merchant.ts +++ b/packages/taler-wallet-core/src/pay-merchant.ts @@ -33,7 +33,6 @@ import { AmountString, assertUnreachable, AsyncFlag, - CancellationToken, checkDbInvariant, codecForAbortResponse, codecForMerchantContractTerms, @@ -142,6 +141,7 @@ import { EXCHANGE_COINS_LOCK, getDenomInfo, InternalWalletState, + WalletExecutionContext, } from "./wallet.js"; import { getCandidateWithdrawalDenomsTx } from "./withdraw.js"; @@ -155,7 +155,7 @@ export class PayMerchantTransactionContext implements TransactionContext { readonly taskId: TaskIdStr; constructor( - public ws: InternalWalletState, + public wex: WalletExecutionContext, public proposalId: string, ) { this.transactionId = constructTransactionIdentifier({ @@ -198,7 +198,7 @@ export class PayMerchantTransactionContext implements TransactionContext { >, ) => Promise, ): Promise { - const ws = this.ws; + const ws = this.wex; const extraStores = opts.extraStores ?? []; const transitionInfo = await ws.db.runReadWriteTx( ["purchases", ...extraStores], @@ -227,7 +227,7 @@ export class PayMerchantTransactionContext implements TransactionContext { } async deleteTransaction(): Promise { - const { ws, proposalId } = this; + const { wex: ws, proposalId } = this; await ws.db.runReadWriteTx(["purchases", "tombstones"], async (tx) => { let found = false; const purchase = await tx.purchases.get(proposalId); @@ -244,7 +244,7 @@ export class PayMerchantTransactionContext implements TransactionContext { } async suspendTransaction(): Promise { - const { ws, proposalId, transactionId } = this; + const { wex: ws, proposalId, transactionId } = this; ws.taskScheduler.stopShepherdTask(this.taskId); const transitionInfo = await ws.db.runReadWriteTx( ["purchases"], @@ -267,8 +267,8 @@ export class PayMerchantTransactionContext implements TransactionContext { } async abortTransaction(): Promise { - const { ws, proposalId, transactionId } = this; - const transitionInfo = await ws.db.runReadWriteTx( + const { wex, proposalId, transactionId } = this; + const transitionInfo = await wex.db.runReadWriteTx( [ "purchases", "refreshGroups", @@ -308,7 +308,7 @@ export class PayMerchantTransactionContext implements TransactionContext { }); } await createRefreshGroup( - ws, + wex, tx, currency, refreshCoins, @@ -328,13 +328,13 @@ export class PayMerchantTransactionContext implements TransactionContext { return { oldTxState, newTxState }; }, ); - ws.taskScheduler.stopShepherdTask(this.taskId); - notifyTransition(ws, transactionId, transitionInfo); - ws.taskScheduler.startShepherdTask(this.taskId); + wex.taskScheduler.stopShepherdTask(this.taskId); + notifyTransition(wex, transactionId, transitionInfo); + wex.taskScheduler.startShepherdTask(this.taskId); } async resumeTransaction(): Promise { - const { ws, proposalId, transactionId, taskId: retryTag } = this; + const { wex: ws, proposalId, transactionId, taskId: retryTag } = this; const transitionInfo = await ws.db.runReadWriteTx( ["purchases"], async (tx) => { @@ -357,7 +357,7 @@ export class PayMerchantTransactionContext implements TransactionContext { } async failTransaction(): Promise { - const { ws, proposalId, transactionId } = this; + const { wex: ws, proposalId, transactionId } = this; const transitionInfo = await ws.db.runReadWriteTx( [ "purchases", @@ -396,7 +396,7 @@ export class RefundTransactionContext implements TransactionContext { public transactionId: TransactionIdStr; public taskId: TaskIdStr | undefined = undefined; constructor( - public ws: InternalWalletState, + public wex: WalletExecutionContext, public refundGroupId: string, ) { this.transactionId = constructTransactionIdentifier({ @@ -406,8 +406,8 @@ export class RefundTransactionContext implements TransactionContext { } async deleteTransaction(): Promise { - const { ws, refundGroupId, transactionId } = this; - await ws.db.runReadWriteTx(["refundGroups", "tombstones"], async (tx) => { + const { wex, refundGroupId, transactionId } = this; + await wex.db.runReadWriteTx(["refundGroups", "tombstones"], async (tx) => { const refundRecord = await tx.refundGroups.get(refundGroupId); if (!refundRecord) { return; @@ -443,11 +443,11 @@ export class RefundTransactionContext implements TransactionContext { * of coins that are too small to spend. */ export async function getTotalPaymentCost( - ws: InternalWalletState, + wex: WalletExecutionContext, pcs: PayCoinSelection, ): Promise { const currency = Amounts.currencyOf(pcs.paymentAmount); - return ws.db.runReadOnlyTx(["coins", "denominations"], async (tx) => { + return wex.db.runReadOnlyTx(["coins", "denominations"], async (tx) => { const costs: AmountJson[] = []; for (let i = 0; i < pcs.coinPubs.length; i++) { const coin = await tx.coins.get(pcs.coinPubs[i]); @@ -464,7 +464,7 @@ export async function getTotalPaymentCost( ); } const allDenoms = await getCandidateWithdrawalDenomsTx( - ws, + wex, tx, coin.exchangeBaseUrl, currency, @@ -477,7 +477,7 @@ export async function getTotalPaymentCost( allDenoms, DenominationRecord.toDenomInfo(denom), amountLeft, - ws.config.testing.denomselAllowLate, + wex.ws.config.testing.denomselAllowLate, ); costs.push(Amounts.parseOrThrow(pcs.coinContributions[i])); costs.push(refreshCost); @@ -488,7 +488,7 @@ export async function getTotalPaymentCost( } async function failProposalPermanently( - ws: InternalWalletState, + wex: WalletExecutionContext, proposalId: string, err: TalerErrorDetail, ): Promise { @@ -496,7 +496,7 @@ async function failProposalPermanently( tag: TransactionType.Payment, proposalId, }); - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["purchases"], async (tx) => { const p = await tx.purchases.get(proposalId); @@ -511,7 +511,7 @@ async function failProposalPermanently( return { oldTxState, newTxState }; }, ); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); } function getPayRequestTimeout(purchase: PurchaseRecord): Duration { @@ -525,7 +525,7 @@ function getPayRequestTimeout(purchase: PurchaseRecord): Duration { * Return the proposal download data for a purchase, throw if not available. */ export async function expectProposalDownload( - ws: InternalWalletState, + wex: WalletExecutionContext, p: PurchaseRecord, parentTx?: WalletDbReadOnlyTransaction<["contractTerms"]>, ): Promise<{ @@ -559,7 +559,7 @@ export async function expectProposalDownload( if (parentTx) { return getFromTransaction(parentTx); } - return await ws.db.runReadOnlyTx(["contractTerms"], getFromTransaction); + return await wex.db.runReadOnlyTx(["contractTerms"], getFromTransaction); } export function extractContractData( @@ -603,11 +603,10 @@ export function extractContractData( } async function processDownloadProposal( - ws: InternalWalletState, + wex: WalletExecutionContext, proposalId: string, - cancellationToken: CancellationToken, ): Promise { - const proposal = await ws.db.runReadOnlyTx(["purchases"], async (tx) => { + const proposal = await wex.db.runReadOnlyTx(["purchases"], async (tx) => { return await tx.purchases.get(proposalId); }); @@ -615,7 +614,7 @@ async function processDownloadProposal( return TaskRunResult.finished(); } - const ctx = new PayMerchantTransactionContext(ws, proposalId); + const ctx = new PayMerchantTransactionContext(wex, proposalId); if (proposal.purchaseStatus != PurchaseStatus.PendingDownloadingProposal) { logger.error( @@ -644,10 +643,10 @@ async function processDownloadProposal( requestBody.token = proposal.claimToken; } - const httpResponse = await ws.http.fetch(orderClaimUrl, { + const httpResponse = await wex.http.fetch(orderClaimUrl, { method: "POST", body: requestBody, - cancellationToken, + cancellationToken: wex.cancellationToken, }); const r = await readSuccessResponseJsonOrErrorCode( httpResponse, @@ -692,7 +691,7 @@ async function processDownloadProposal( {}, "validation for well-formedness failed", ); - await failProposalPermanently(ws, proposalId, err); + await failProposalPermanently(wex, proposalId, err); throw makePendingOperationFailedError( err, TransactionType.Payment, @@ -718,7 +717,7 @@ async function processDownloadProposal( {}, `schema validation failed: ${e}`, ); - await failProposalPermanently(ws, proposalId, err); + await failProposalPermanently(wex, proposalId, err); throw makePendingOperationFailedError( err, TransactionType.Payment, @@ -726,7 +725,7 @@ async function processDownloadProposal( ); } - const sigValid = await ws.cryptoApi.isValidContractTermsSignature({ + const sigValid = await wex.cryptoApi.isValidContractTermsSignature({ contractTermsHash, merchantPub: parsedContractTerms.merchant_pub, sig: proposalResp.sig, @@ -741,7 +740,7 @@ async function processDownloadProposal( }, "merchant's signature on contract terms is invalid", ); - await failProposalPermanently(ws, proposalId, err); + await failProposalPermanently(wex, proposalId, err); throw makePendingOperationFailedError( err, TransactionType.Payment, @@ -763,7 +762,7 @@ async function processDownloadProposal( }, "merchant base URL mismatch", ); - await failProposalPermanently(ws, proposalId, err); + await failProposalPermanently(wex, proposalId, err); throw makePendingOperationFailedError( err, TransactionType.Payment, @@ -779,7 +778,7 @@ async function processDownloadProposal( logger.trace(`extracted contract data: ${j2s(contractData)}`); - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["purchases", "contractTerms"], async (tx) => { const p = await tx.purchases.get(proposalId); @@ -835,7 +834,7 @@ async function processDownloadProposal( }, ); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); return TaskRunResult.progress(); } @@ -846,15 +845,14 @@ async function processDownloadProposal( * return the old proposal ID. */ async function createOrReusePurchase( - ws: InternalWalletState, + wex: WalletExecutionContext, merchantBaseUrl: string, orderId: string, sessionId: string | undefined, claimToken: string | undefined, noncePriv: string | undefined, - cancellationToken: CancellationToken, ): Promise { - const oldProposals = await ws.db.runReadOnlyTx(["purchases"], async (tx) => { + const oldProposals = await wex.db.runReadOnlyTx(["purchases"], async (tx) => { return tx.purchases.indexes.byUrlAndOrderId.getAll([ merchantBaseUrl, orderId, @@ -882,17 +880,13 @@ async function createOrReusePurchase( }) for order ${orderId} at ${merchantBaseUrl}`, ); if (oldProposal.purchaseStatus === PurchaseStatus.DialogShared) { - const download = await expectProposalDownload(ws, oldProposal); - const paid = await checkIfOrderIsAlreadyPaid( - ws, - download.contractData, - cancellationToken, - ); + const download = await expectProposalDownload(wex, oldProposal); + const paid = await checkIfOrderIsAlreadyPaid(wex, download.contractData); logger.info(`old proposal paid: ${paid}`); if (paid) { // if this transaction was shared and the order is paid then it // means that another wallet already paid the proposal - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["purchases"], async (tx) => { const p = await tx.purchases.get(oldProposal.proposalId); @@ -912,7 +906,7 @@ async function createOrReusePurchase( tag: TransactionType.Payment, proposalId: oldProposal.proposalId, }); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); } } return oldProposal.proposalId; @@ -924,10 +918,10 @@ async function createOrReusePurchase( shared = true; noncePair = { priv: noncePriv, - pub: (await ws.cryptoApi.eddsaGetPublic({ priv: noncePriv })).pub, + pub: (await wex.cryptoApi.eddsaGetPublic({ priv: noncePriv })).pub, }; } else { - noncePair = await ws.cryptoApi.createEddsaKeypair({}); + noncePair = await wex.cryptoApi.createEddsaKeypair({}); } const { priv, pub } = noncePair; @@ -958,7 +952,7 @@ async function createOrReusePurchase( shared: shared, }; - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["purchases"], async (tx) => { await tx.purchases.put(proposalRecord); @@ -977,12 +971,12 @@ async function createOrReusePurchase( tag: TransactionType.Payment, proposalId, }); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); return proposalId; } async function storeFirstPaySuccess( - ws: InternalWalletState, + wex: WalletExecutionContext, proposalId: string, sessionId: string | undefined, payResponse: MerchantPayResponse, @@ -992,7 +986,7 @@ async function storeFirstPaySuccess( proposalId, }); const now = AbsoluteTime.toPreciseTimestamp(AbsoluteTime.now()); - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["contractTerms", "purchases"], async (tx) => { const purchase = await tx.purchases.get(proposalId); @@ -1044,11 +1038,11 @@ async function storeFirstPaySuccess( }; }, ); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); } async function storePayReplaySuccess( - ws: InternalWalletState, + wex: WalletExecutionContext, proposalId: string, sessionId: string | undefined, ): Promise { @@ -1056,7 +1050,7 @@ async function storePayReplaySuccess( tag: TransactionType.Payment, proposalId, }); - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["purchases"], async (tx) => { const purchase = await tx.purchases.get(proposalId); @@ -1082,7 +1076,7 @@ async function storePayReplaySuccess( return { oldTxState, newTxState }; }, ); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); } /** @@ -1094,13 +1088,13 @@ async function storePayReplaySuccess( * (3) re-do coin selection with the bad coin removed */ async function handleInsufficientFunds( - ws: InternalWalletState, + wex: WalletExecutionContext, proposalId: string, err: TalerErrorDetail, ): Promise { logger.trace("handling insufficient funds, trying to re-select coins"); - const proposal = await ws.db.runReadOnlyTx(["purchases"], async (tx) => { + const proposal = await wex.db.runReadOnlyTx(["purchases"], async (tx) => { return tx.purchases.get(proposalId); }); if (!proposal) { @@ -1128,7 +1122,7 @@ async function handleInsufficientFunds( throw new TalerProtocolViolationError(); } - const { contractData } = await expectProposalDownload(ws, proposal); + const { contractData } = await expectProposalDownload(wex, proposal); const prevPayCoins: PreviousPayCoins = []; @@ -1139,7 +1133,7 @@ async function handleInsufficientFunds( const payCoinSelection = payInfo.payCoinSelection; - await ws.db.runReadOnlyTx(["coins", "denominations"], async (tx) => { + await wex.db.runReadOnlyTx(["coins", "denominations"], async (tx) => { for (let i = 0; i < payCoinSelection.coinPubs.length; i++) { const coinPub = payCoinSelection.coinPubs[i]; if (coinPub === brokenCoinPub) { @@ -1166,7 +1160,7 @@ async function handleInsufficientFunds( } }); - const res = await selectPayCoinsNew(ws, { + const res = await selectPayCoinsNew(wex, { auditors: [], exchanges: contractData.allowedExchanges, wireMethod: contractData.wireMethod, @@ -1185,7 +1179,7 @@ async function handleInsufficientFunds( logger.trace("re-selected coins"); - await ws.db.runReadWriteTx( + await wex.db.runReadWriteTx( [ "purchases", "coins", @@ -1205,7 +1199,7 @@ async function handleInsufficientFunds( payInfo.payCoinSelection = res.coinSel; payInfo.payCoinSelectionUid = encodeCrock(getRandomBytes(32)); await tx.purchases.put(p); - await spendCoins(ws, tx, { + await spendCoins(wex, tx, { // allocationId: `txn:proposal:${p.proposalId}`, allocationId: constructTransactionIdentifier({ tag: TransactionType.Payment, @@ -1220,7 +1214,7 @@ async function handleInsufficientFunds( }, ); - ws.notify({ + wex.ws.notify({ type: NotificationType.BalanceChange, hintTransactionId: constructTransactionIdentifier({ tag: TransactionType.Payment, @@ -1233,11 +1227,11 @@ async function handleInsufficientFunds( // FIXME: Does way more than checking the payment // FIXME: Should return immediately. async function checkPaymentByProposalId( - ws: InternalWalletState, + wex: WalletExecutionContext, proposalId: string, sessionId?: string, ): Promise { - let proposal = await ws.db.runReadOnlyTx(["purchases"], async (tx) => { + let proposal = await wex.db.runReadOnlyTx(["purchases"], async (tx) => { return tx.purchases.get(proposalId); }); if (!proposal) { @@ -1247,7 +1241,7 @@ async function checkPaymentByProposalId( const existingProposalId = proposal.repurchaseProposalId; if (existingProposalId) { logger.trace("using existing purchase for same product"); - const oldProposal = await ws.db.runReadOnlyTx( + const oldProposal = await wex.db.runReadOnlyTx( ["purchases"], async (tx) => { return tx.purchases.get(existingProposalId); @@ -1258,7 +1252,7 @@ async function checkPaymentByProposalId( } } } - const d = await expectProposalDownload(ws, proposal); + const d = await expectProposalDownload(wex, proposal); const contractData = d.contractData; const merchantSig = d.contractData.merchantSig; if (!merchantSig) { @@ -1267,7 +1261,7 @@ async function checkPaymentByProposalId( proposalId = proposal.proposalId; - const ctx = new PayMerchantTransactionContext(ws, proposalId); + const ctx = new PayMerchantTransactionContext(wex, proposalId); const transactionId = ctx.transactionId; @@ -1280,7 +1274,7 @@ async function checkPaymentByProposalId( }); // First check if we already paid for it. - const purchase = await ws.db.runReadOnlyTx(["purchases"], async (tx) => { + const purchase = await wex.db.runReadOnlyTx(["purchases"], async (tx) => { return tx.purchases.get(proposalId); }); @@ -1290,7 +1284,7 @@ async function checkPaymentByProposalId( purchase.purchaseStatus === PurchaseStatus.DialogShared ) { // If not already paid, check if we could pay for it. - const res = await selectPayCoinsNew(ws, { + const res = await selectPayCoinsNew(wex, { auditors: [], exchanges: contractData.allowedExchanges, contractTermsAmount: Amounts.parseOrThrow(contractData.amount), @@ -1318,7 +1312,7 @@ async function checkPaymentByProposalId( }; } - const totalCost = await getTotalPaymentCost(ws, res.coinSel); + const totalCost = await getTotalPaymentCost(wex, res.coinSel); logger.trace("costInfo", totalCost); logger.trace("coinsForPayment", res); @@ -1342,7 +1336,7 @@ async function checkPaymentByProposalId( "automatically re-submitting payment with different session ID", ); logger.trace(`last: ${purchase.lastSessionId}, current: ${sessionId}`); - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["purchases"], async (tx) => { const p = await tx.purchases.get(proposalId); @@ -1357,14 +1351,14 @@ async function checkPaymentByProposalId( return { oldTxState, newTxState }; }, ); - notifyTransition(ws, transactionId, transitionInfo); - ws.taskScheduler.startShepherdTask(ctx.taskId); + notifyTransition(wex, transactionId, transitionInfo); + wex.taskScheduler.startShepherdTask(ctx.taskId); // FIXME: Consider changing the API here so that we don't have to // wait inline for the repurchase. - await waitPaymentResult(ws, proposalId, sessionId); - const download = await expectProposalDownload(ws, purchase); + await waitPaymentResult(wex, proposalId, sessionId); + const download = await expectProposalDownload(wex, purchase); return { status: PreparePayResultType.AlreadyConfirmed, contractTerms: download.contractTermsRaw, @@ -1379,7 +1373,7 @@ async function checkPaymentByProposalId( talerUri, }; } else if (!purchase.timestampFirstSuccessfulPay) { - const download = await expectProposalDownload(ws, purchase); + const download = await expectProposalDownload(wex, purchase); return { status: PreparePayResultType.AlreadyConfirmed, contractTerms: download.contractTermsRaw, @@ -1398,7 +1392,7 @@ async function checkPaymentByProposalId( purchase.purchaseStatus === PurchaseStatus.Done || purchase.purchaseStatus === PurchaseStatus.PendingQueryingRefund || purchase.purchaseStatus === PurchaseStatus.PendingQueryingAutoRefund; - const download = await expectProposalDownload(ws, purchase); + const download = await expectProposalDownload(wex, purchase); return { status: PreparePayResultType.AlreadyConfirmed, contractTerms: download.contractTermsRaw, @@ -1417,10 +1411,10 @@ async function checkPaymentByProposalId( } export async function getContractTermsDetails( - ws: InternalWalletState, + wex: WalletExecutionContext, proposalId: string, ): Promise { - const proposal = await ws.db.runReadOnlyTx(["purchases"], async (tx) => { + const proposal = await wex.db.runReadOnlyTx(["purchases"], async (tx) => { return tx.purchases.get(proposalId); }); @@ -1428,7 +1422,7 @@ export async function getContractTermsDetails( throw Error(`proposal with id ${proposalId} not found`); } - const d = await expectProposalDownload(ws, proposal); + const d = await expectProposalDownload(wex, proposal); return d.contractData; } @@ -1440,7 +1434,7 @@ export async function getContractTermsDetails( * yet send to the merchant. */ export async function preparePayForUri( - ws: InternalWalletState, + wex: WalletExecutionContext, talerPayUri: string, ): Promise { const uriResult = parsePayUri(talerPayUri); @@ -1456,39 +1450,38 @@ export async function preparePayForUri( } const proposalId = await createOrReusePurchase( - ws, + wex, uriResult.merchantBaseUrl, uriResult.orderId, uriResult.sessionId, uriResult.claimToken, uriResult.noncePriv, - CancellationToken.CONTINUE, ); - await waitProposalDownloaded(ws, proposalId); + await waitProposalDownloaded(wex, proposalId); - return checkPaymentByProposalId(ws, proposalId, uriResult.sessionId); + return checkPaymentByProposalId(wex, proposalId, uriResult.sessionId); } /** * Wait until a proposal is at least downloaded. */ async function waitProposalDownloaded( - ws: InternalWalletState, + wex: WalletExecutionContext, proposalId: string, ): Promise { - const ctx = new PayMerchantTransactionContext(ws, proposalId); + const ctx = new PayMerchantTransactionContext(wex, proposalId); logger.info(`waiting for ${ctx.transactionId} to be downloaded`); - ws.taskScheduler.startShepherdTask(ctx.taskId); + wex.taskScheduler.startShepherdTask(ctx.taskId); // FIXME: We should use Symbol.dispose magic here for cleanup! const payNotifFlag = new AsyncFlag(); // Raise exchangeNotifFlag whenever we get a notification // about our exchange. - const cancelNotif = ws.addNotificationListener((notif) => { + const cancelNotif = wex.ws.addNotificationListener((notif) => { if ( notif.type === NotificationType.TransactionStateTransition && notif.transactionId === ctx.transactionId @@ -1511,7 +1504,7 @@ async function internalWaitProposalDownloaded( payNotifFlag: AsyncFlag, ): Promise { while (true) { - const { purchase, retryInfo } = await ctx.ws.db.runReadOnlyTx( + const { purchase, retryInfo } = await ctx.wex.db.runReadOnlyTx( ["purchases", "operationRetries"], async (tx) => { return { @@ -1539,7 +1532,7 @@ async function internalWaitProposalDownloaded( } export async function preparePayForTemplate( - ws: InternalWalletState, + wex: WalletExecutionContext, req: PreparePayTemplateRequest, ): Promise { const parsedUri = parsePayTemplateUri(req.talerPayTemplateUri); @@ -1575,7 +1568,7 @@ export async function preparePayForTemplate( `templates/${parsedUri.templateId}`, parsedUri.merchantBaseUrl, ); - const httpReq = await ws.http.fetch(reqUrl.href, { + const httpReq = await wex.http.fetch(reqUrl.href, { method: "POST", body: templateDetails, }); @@ -1591,7 +1584,7 @@ export async function preparePayForTemplate( claimToken: resp.token, }); - return await preparePayForUri(ws, payUri); + return await preparePayForUri(wex, payUri); } /** @@ -1600,7 +1593,7 @@ export async function preparePayForTemplate( * Accesses the database and the crypto worker. */ export async function generateDepositPermissions( - ws: InternalWalletState, + wex: WalletExecutionContext, payCoinSel: PayCoinSelection, contractData: WalletContractData, ): Promise { @@ -1609,7 +1602,7 @@ export async function generateDepositPermissions( coin: CoinRecord; denom: DenominationRecord; }> = []; - await ws.db.runReadOnlyTx(["coins", "denominations"], async (tx) => { + await wex.db.runReadOnlyTx(["coins", "denominations"], async (tx) => { for (let i = 0; i < payCoinSel.coinPubs.length; i++) { const coin = await tx.coins.get(payCoinSel.coinPubs[i]); if (!coin) { @@ -1637,7 +1630,7 @@ export async function generateDepositPermissions( coin.ageCommitmentProof, )}`, ); - const dp = await ws.cryptoApi.signDepositPermission({ + const dp = await wex.cryptoApi.signDepositPermission({ coinPriv: coin.coinPriv, coinPub: coin.coinPub, contractTermsHash: contractData.contractTermsHash, @@ -1665,7 +1658,7 @@ async function internalWaitPaymentResult( waitSessionId?: string, ): Promise { while (true) { - const txRes = await ctx.ws.db.runReadOnlyTx( + const txRes = await ctx.wex.db.runReadOnlyTx( ["purchases", "operationRetries"], async (tx) => { const purchase = await tx.purchases.get(ctx.proposalId); @@ -1684,7 +1677,7 @@ async function internalWaitPaymentResult( `purchase is in state ${PurchaseStatus[purchase.purchaseStatus]}`, ); - const d = await expectProposalDownload(ctx.ws, purchase); + const d = await expectProposalDownload(ctx.wex, purchase); if (txRes.purchase.timestampFirstSuccessfulPay) { if ( @@ -1726,20 +1719,20 @@ async function internalWaitPaymentResult( * b) the attempt to pay failed (merchant unavailable, etc.) */ async function waitPaymentResult( - ws: InternalWalletState, + wex: WalletExecutionContext, proposalId: string, waitSessionId?: string, ): Promise { - const ctx = new PayMerchantTransactionContext(ws, proposalId); + const ctx = new PayMerchantTransactionContext(wex, proposalId); - ws.taskScheduler.ensureRunning(); - ws.taskScheduler.startShepherdTask(ctx.taskId); + wex.taskScheduler.ensureRunning(); + wex.taskScheduler.startShepherdTask(ctx.taskId); // FIXME: Clean up using the new JS "using" / Symbol.dispose syntax. const purchaseNotifFlag = new AsyncFlag(); // Raise purchaseNotifFlag whenever we get a notification // about our purchase. - const cancelNotif = ws.addNotificationListener((notif) => { + const cancelNotif = wex.ws.addNotificationListener((notif) => { if ( notif.type === NotificationType.TransactionStateTransition && notif.transactionId === ctx.transactionId @@ -1768,7 +1761,7 @@ async function waitPaymentResult( * Confirm payment for a proposal previously claimed by the wallet. */ export async function confirmPay( - ws: InternalWalletState, + wex: WalletExecutionContext, transactionId: string, sessionIdOverride?: string, forcedCoinSel?: ForcedCoinSel, @@ -1781,7 +1774,7 @@ export async function confirmPay( logger.trace( `executing confirmPay with proposalId ${proposalId} and sessionIdOverride ${sessionIdOverride}`, ); - const proposal = await ws.db.runReadOnlyTx(["purchases"], async (tx) => { + const proposal = await wex.db.runReadOnlyTx(["purchases"], async (tx) => { return tx.purchases.get(proposalId); }); @@ -1789,12 +1782,12 @@ export async function confirmPay( throw Error(`proposal with id ${proposalId} not found`); } - const d = await expectProposalDownload(ws, proposal); + const d = await expectProposalDownload(wex, proposal); if (!d) { throw Error("proposal is in invalid state"); } - const existingPurchase = await ws.db.runReadWriteTx( + const existingPurchase = await wex.db.runReadWriteTx( ["purchases"], async (tx) => { const purchase = await tx.purchases.get(proposalId); @@ -1817,18 +1810,18 @@ export async function confirmPay( if (existingPurchase && existingPurchase.payInfo) { logger.trace("confirmPay: submitting payment for existing purchase"); const ctx = new PayMerchantTransactionContext( - ws, + wex, existingPurchase.proposalId, ); - await ws.taskScheduler.resetTaskRetries(ctx.taskId); - return waitPaymentResult(ws, proposalId); + await wex.taskScheduler.resetTaskRetries(ctx.taskId); + return waitPaymentResult(wex, proposalId); } logger.trace("confirmPay: purchase record does not exist yet"); const contractData = d.contractData; - const selectCoinsResult = await selectPayCoinsNew(ws, { + const selectCoinsResult = await selectPayCoinsNew(wex, { auditors: [], exchanges: contractData.allowedExchanges, wireMethod: contractData.wireMethod, @@ -1852,7 +1845,7 @@ export async function confirmPay( } const coinSelection = selectCoinsResult.coinSel; - const payCostInfo = await getTotalPaymentCost(ws, coinSelection); + const payCostInfo = await getTotalPaymentCost(wex, coinSelection); let sessionId: string | undefined; if (sessionIdOverride) { @@ -1865,7 +1858,7 @@ export async function confirmPay( `recording payment on ${proposal.orderId} with session ID ${sessionId}`, ); - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( [ "purchases", "coins", @@ -1891,7 +1884,7 @@ export async function confirmPay( p.timestampAccept = timestampPreciseToDb(TalerPreciseTimestamp.now()); p.purchaseStatus = PurchaseStatus.PendingPaying; await tx.purchases.put(p); - await spendCoins(ws, tx, { + await spendCoins(wex, tx, { //`txn:proposal:${p.proposalId}` allocationId: constructTransactionIdentifier({ tag: TransactionType.Payment, @@ -1914,22 +1907,21 @@ export async function confirmPay( }, ); - notifyTransition(ws, transactionId, transitionInfo); - ws.notify({ + notifyTransition(wex, transactionId, transitionInfo); + wex.ws.notify({ type: NotificationType.BalanceChange, hintTransactionId: transactionId, }); // Wait until we have completed the first attempt to pay. - return waitPaymentResult(ws, proposalId); + return waitPaymentResult(wex, proposalId); } export async function processPurchase( - ws: InternalWalletState, + wex: WalletExecutionContext, proposalId: string, - cancellationToken: CancellationToken, ): Promise { - const purchase = await ws.db.runReadOnlyTx(["purchases"], async (tx) => { + const purchase = await wex.db.runReadOnlyTx(["purchases"], async (tx) => { return tx.purchases.get(proposalId); }); if (!purchase) { @@ -1947,20 +1939,20 @@ export async function processPurchase( switch (purchase.purchaseStatus) { case PurchaseStatus.PendingDownloadingProposal: - return processDownloadProposal(ws, proposalId, cancellationToken); + return processDownloadProposal(wex, proposalId); case PurchaseStatus.PendingPaying: case PurchaseStatus.PendingPayingReplay: - return processPurchasePay(ws, proposalId, cancellationToken); + return processPurchasePay(wex, proposalId); case PurchaseStatus.PendingQueryingRefund: - return processPurchaseQueryRefund(ws, purchase, cancellationToken); + return processPurchaseQueryRefund(wex, purchase); case PurchaseStatus.PendingQueryingAutoRefund: - return processPurchaseAutoRefund(ws, purchase, cancellationToken); + return processPurchaseAutoRefund(wex, purchase); case PurchaseStatus.AbortingWithRefund: - return processPurchaseAbortingRefund(ws, purchase, cancellationToken); + return processPurchaseAbortingRefund(wex, purchase); case PurchaseStatus.PendingAcceptRefund: - return processPurchaseAcceptRefund(ws, purchase, cancellationToken); + return processPurchaseAcceptRefund(wex, purchase); case PurchaseStatus.DialogShared: - return processPurchaseDialogShared(ws, purchase, cancellationToken); + return processPurchaseDialogShared(wex, purchase); case PurchaseStatus.FailedClaim: case PurchaseStatus.Done: case PurchaseStatus.DoneRepurchaseDetected: @@ -1984,11 +1976,10 @@ export async function processPurchase( } async function processPurchasePay( - ws: InternalWalletState, + wex: WalletExecutionContext, proposalId: string, - cancellationToken: CancellationToken, ): Promise { - const purchase = await ws.db.runReadOnlyTx(["purchases"], async (tx) => { + const purchase = await wex.db.runReadOnlyTx(["purchases"], async (tx) => { return tx.purchases.get(proposalId); }); if (!purchase) { @@ -2018,17 +2009,13 @@ async function processPurchasePay( const payInfo = purchase.payInfo; checkDbInvariant(!!payInfo, "payInfo"); - const download = await expectProposalDownload(ws, purchase); + const download = await expectProposalDownload(wex, purchase); if (purchase.shared) { - const paid = await checkIfOrderIsAlreadyPaid( - ws, - download.contractData, - cancellationToken, - ); + const paid = await checkIfOrderIsAlreadyPaid(wex, download.contractData); if (paid) { - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["purchases"], async (tx) => { const p = await tx.purchases.get(purchase.proposalId); @@ -2048,7 +2035,7 @@ async function processPurchasePay( proposalId, }); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); return { type: TaskRunResultType.Error, @@ -2069,7 +2056,7 @@ async function processPurchasePay( let depositPermissions: CoinDepositPermission[]; // FIXME: Cache! depositPermissions = await generateDepositPermissions( - ws, + wex, payInfo.payCoinSelection, download.contractData, ); @@ -2084,12 +2071,12 @@ async function processPurchasePay( JSON.stringify(reqBody, undefined, 2), ); - const resp = await ws.runSequentialized([EXCHANGE_COINS_LOCK], () => - ws.http.fetch(payUrl, { + const resp = await wex.ws.runSequentialized([EXCHANGE_COINS_LOCK], () => + wex.http.fetch(payUrl, { method: "POST", body: reqBody, timeout: getPayRequestTimeout(purchase), - cancellationToken, + cancellationToken: wex.cancellationToken, }), ); @@ -2115,7 +2102,7 @@ async function processPurchasePay( TalerErrorCode.MERCHANT_POST_ORDERS_ID_PAY_INSUFFICIENT_FUNDS ) { // Do this in the background, as it might take some time - handleInsufficientFunds(ws, proposalId, err).catch(async (e) => { + handleInsufficientFunds(wex, proposalId, err).catch(async (e) => { logger.error("handling insufficient funds failed"); logger.error(`${e.toString()}`); }); @@ -2140,7 +2127,7 @@ async function processPurchasePay( logger.trace("got success from pay URL", merchantResp); const merchantPub = download.contractData.merchantPub; - const { valid } = await ws.cryptoApi.isValidPaymentSignature({ + const { valid } = await wex.cryptoApi.isValidPaymentSignature({ contractHash: download.contractData.contractTermsHash, merchantPub, sig: merchantResp.sig, @@ -2152,7 +2139,7 @@ async function processPurchasePay( throw Error("merchant payment signature invalid"); } - await storeFirstPaySuccess(ws, proposalId, sessionId, merchantResp); + await storeFirstPaySuccess(wex, proposalId, sessionId, merchantResp); } else { const payAgainUrl = new URL( `orders/${download.contractData.orderId}/paid`, @@ -2164,11 +2151,11 @@ async function processPurchasePay( session_id: sessionId ?? "", }; logger.trace(`/paid request body: ${j2s(reqBody)}`); - const resp = await ws.runSequentialized([EXCHANGE_COINS_LOCK], () => - ws.http.fetch(payAgainUrl, { + const resp = await wex.ws.runSequentialized([EXCHANGE_COINS_LOCK], () => + wex.http.fetch(payAgainUrl, { method: "POST", body: reqBody, - cancellationToken, + cancellationToken: wex.cancellationToken, }), ); logger.trace(`/paid response status: ${resp.status}`); @@ -2182,21 +2169,21 @@ async function processPurchasePay( "/paid failed", ); } - await storePayReplaySuccess(ws, proposalId, sessionId); + await storePayReplaySuccess(wex, proposalId, sessionId); } return TaskRunResult.progress(); } export async function refuseProposal( - ws: InternalWalletState, + wex: WalletExecutionContext, proposalId: string, ): Promise { const transactionId = constructTransactionIdentifier({ tag: TransactionType.Payment, proposalId, }); - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["purchases"], async (tx) => { const proposal = await tx.purchases.get(proposalId); @@ -2218,7 +2205,7 @@ export async function refuseProposal( }, ); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); } const transitionSuspend: { @@ -2456,11 +2443,11 @@ export function computePayMerchantTransactionActions( } export async function sharePayment( - ws: InternalWalletState, + wex: WalletExecutionContext, merchantBaseUrl: string, orderId: string, ): Promise { - const result = await ws.db.runReadWriteTx(["purchases"], async (tx) => { + const result = await wex.db.runReadWriteTx(["purchases"], async (tx) => { const p = await tx.purchases.indexes.byUrlAndOrderId.get([ merchantBaseUrl, orderId, @@ -2503,9 +2490,8 @@ export async function sharePayment( } async function checkIfOrderIsAlreadyPaid( - ws: InternalWalletState, + wex: WalletExecutionContext, contract: WalletContractData, - cancellationToken: CancellationToken, ) { const requestUrl = new URL( `orders/${contract.orderId}`, @@ -2515,8 +2501,8 @@ async function checkIfOrderIsAlreadyPaid( requestUrl.searchParams.set("timeout_ms", "1000"); - const resp = await ws.http.fetch(requestUrl.href, { - cancellationToken, + const resp = await wex.http.fetch(requestUrl.href, { + cancellationToken: wex.cancellationToken, }); if ( resp.status === HttpStatusCode.Ok || @@ -2532,25 +2518,20 @@ async function checkIfOrderIsAlreadyPaid( } async function processPurchaseDialogShared( - ws: InternalWalletState, + wex: WalletExecutionContext, purchase: PurchaseRecord, - cancellationToken: CancellationToken, ): Promise { const proposalId = purchase.proposalId; logger.trace(`processing dialog-shared for proposal ${proposalId}`); - const download = await expectProposalDownload(ws, purchase); + const download = await expectProposalDownload(wex, purchase); if (purchase.purchaseStatus !== PurchaseStatus.DialogShared) { return TaskRunResult.finished(); } - const paid = await checkIfOrderIsAlreadyPaid( - ws, - download.contractData, - cancellationToken, - ); + const paid = await checkIfOrderIsAlreadyPaid(wex, download.contractData); if (paid) { - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["purchases"], async (tx) => { const p = await tx.purchases.get(purchase.proposalId); @@ -2570,31 +2551,25 @@ async function processPurchaseDialogShared( proposalId, }); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); } return TaskRunResult.backoff(); } async function processPurchaseAutoRefund( - ws: InternalWalletState, + wex: WalletExecutionContext, purchase: PurchaseRecord, - cancellationToken: CancellationToken, ): Promise { const proposalId = purchase.proposalId; logger.trace(`processing auto-refund for proposal ${proposalId}`); - const taskId = constructTaskIdentifier({ - tag: PendingTaskType.Purchase, - proposalId, - }); - const transactionId = constructTransactionIdentifier({ tag: TransactionType.Payment, proposalId, }); - const download = await expectProposalDownload(ws, purchase); + const download = await expectProposalDownload(wex, purchase); if ( !purchase.autoRefundDeadline || @@ -2604,7 +2579,7 @@ async function processPurchaseAutoRefund( ), ) ) { - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["purchases"], async (tx) => { const p = await tx.purchases.get(purchase.proposalId); @@ -2623,7 +2598,7 @@ async function processPurchaseAutoRefund( return { oldTxState, newTxState }; }, ); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); return TaskRunResult.finished(); } @@ -2639,8 +2614,8 @@ async function processPurchaseAutoRefund( requestUrl.searchParams.set("timeout_ms", "1000"); requestUrl.searchParams.set("await_refund_obtained", "yes"); - const resp = await ws.http.fetch(requestUrl.href, { - cancellationToken, + const resp = await wex.http.fetch(requestUrl.href, { + cancellationToken: wex.cancellationToken, }); // FIXME: Check other status codes! @@ -2651,7 +2626,7 @@ async function processPurchaseAutoRefund( ); if (orderStatus.refund_pending) { - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["purchases"], async (tx) => { const p = await tx.purchases.get(purchase.proposalId); @@ -2669,19 +2644,18 @@ async function processPurchaseAutoRefund( return { oldTxState, newTxState }; }, ); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); } return TaskRunResult.backoff(); } async function processPurchaseAbortingRefund( - ws: InternalWalletState, + wex: WalletExecutionContext, purchase: PurchaseRecord, - cancellationToken: CancellationToken, ): Promise { const proposalId = purchase.proposalId; - const download = await expectProposalDownload(ws, purchase); + const download = await expectProposalDownload(wex, purchase); logger.trace(`processing aborting-refund for proposal ${proposalId}`); const requestUrl = new URL( @@ -2696,7 +2670,7 @@ async function processPurchaseAbortingRefund( throw Error("can't abort, no coins selected"); } - await ws.db.runReadOnlyTx(["coins"], async (tx) => { + await wex.db.runReadOnlyTx(["coins"], async (tx) => { for (let i = 0; i < payCoinSelection.coinPubs.length; i++) { const coinPub = payCoinSelection.coinPubs[i]; const coin = await tx.coins.get(coinPub); @@ -2716,10 +2690,10 @@ async function processPurchaseAbortingRefund( logger.trace(`making order abort request to ${requestUrl.href}`); - const abortHttpResp = await ws.http.fetch(requestUrl.href, { + const abortHttpResp = await wex.http.fetch(requestUrl.href, { method: "POST", body: abortReq, - cancellationToken, + cancellationToken: wex.cancellationToken, }); if (abortHttpResp.status === HttpStatusCode.NotFound) { @@ -2728,7 +2702,7 @@ async function processPurchaseAbortingRefund( err.code === TalerErrorCode.MERCHANT_POST_ORDERS_ID_ABORT_CONTRACT_NOT_FOUND ) { - const ctx = new PayMerchantTransactionContext(ws, proposalId); + const ctx = new PayMerchantTransactionContext(wex, proposalId); await ctx.transition(async (rec) => { if (rec.purchaseStatus === PurchaseStatus.AbortingWithRefund) { rec.purchaseStatus = PurchaseStatus.AbortedOrderDeleted; @@ -2766,18 +2740,17 @@ async function processPurchaseAbortingRefund( ), }); } - return await storeRefunds(ws, purchase, refunds, RefundReason.AbortRefund); + return await storeRefunds(wex, purchase, refunds, RefundReason.AbortRefund); } async function processPurchaseQueryRefund( - ws: InternalWalletState, + wex: WalletExecutionContext, purchase: PurchaseRecord, - cancellationToken: CancellationToken, ): Promise { const proposalId = purchase.proposalId; logger.trace(`processing query-refund for proposal ${proposalId}`); - const download = await expectProposalDownload(ws, purchase); + const download = await expectProposalDownload(wex, purchase); const requestUrl = new URL( `orders/${download.contractData.orderId}`, @@ -2788,8 +2761,8 @@ async function processPurchaseQueryRefund( download.contractData.contractTermsHash, ); - const resp = await ws.http.fetch(requestUrl.href, { - cancellationToken, + const resp = await wex.http.fetch(requestUrl.href, { + cancellationToken: wex.cancellationToken, }); const orderStatus = await readSuccessResponseJsonOrThrow( resp, @@ -2802,7 +2775,7 @@ async function processPurchaseQueryRefund( }); if (!orderStatus.refund_pending) { - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["purchases"], async (tx) => { const p = await tx.purchases.get(purchase.proposalId); @@ -2821,7 +2794,7 @@ async function processPurchaseQueryRefund( return { oldTxState, newTxState }; }, ); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); return TaskRunResult.progress(); } else { const refundAwaiting = Amounts.sub( @@ -2829,7 +2802,7 @@ async function processPurchaseQueryRefund( Amounts.parseOrThrow(orderStatus.refund_taken), ).amount; - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["purchases"], async (tx) => { const p = await tx.purchases.get(purchase.proposalId); @@ -2848,17 +2821,16 @@ async function processPurchaseQueryRefund( return { oldTxState, newTxState }; }, ); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); return TaskRunResult.progress(); } } async function processPurchaseAcceptRefund( - ws: InternalWalletState, + wex: WalletExecutionContext, purchase: PurchaseRecord, - cancellationToken: CancellationToken, ): Promise { - const download = await expectProposalDownload(ws, purchase); + const download = await expectProposalDownload(wex, purchase); const requestUrl = new URL( `orders/${download.contractData.orderId}/refund`, @@ -2867,12 +2839,12 @@ async function processPurchaseAcceptRefund( logger.trace(`making refund request to ${requestUrl.href}`); - const request = await ws.http.fetch(requestUrl.href, { + const request = await wex.http.fetch(requestUrl.href, { method: "POST", body: { h_contract: download.contractData.contractTermsHash, }, - cancellationToken, + cancellationToken: wex.cancellationToken, }); const refundResponse = await readSuccessResponseJsonOrThrow( @@ -2880,7 +2852,7 @@ async function processPurchaseAcceptRefund( codecForMerchantOrderRefundPickupResponse(), ); return await storeRefunds( - ws, + wex, purchase, refundResponse.refunds, RefundReason.AbortRefund, @@ -2888,7 +2860,7 @@ async function processPurchaseAcceptRefund( } export async function startRefundQueryForUri( - ws: InternalWalletState, + wex: WalletExecutionContext, talerUri: string, ): Promise { const parsedUri = parseTalerUri(talerUri); @@ -2898,7 +2870,7 @@ export async function startRefundQueryForUri( if (parsedUri.type !== TalerUriAction.Refund) { throw Error("expected taler://refund URI"); } - const purchaseRecord = await ws.db.runReadOnlyTx( + const purchaseRecord = await wex.db.runReadOnlyTx( ["purchases"], async (tx) => { return tx.purchases.indexes.byUrlAndOrderId.get([ @@ -2918,18 +2890,18 @@ export async function startRefundQueryForUri( tag: TransactionType.Payment, proposalId, }); - await startQueryRefund(ws, proposalId); + await startQueryRefund(wex, proposalId); return { transactionId, }; } export async function startQueryRefund( - ws: InternalWalletState, + wex: WalletExecutionContext, proposalId: string, ): Promise { - const ctx = new PayMerchantTransactionContext(ws, proposalId); - const transitionInfo = await ws.db.runReadWriteTx( + const ctx = new PayMerchantTransactionContext(wex, proposalId); + const transitionInfo = await wex.db.runReadWriteTx( ["purchases"], async (tx) => { const p = await tx.purchases.get(proposalId); @@ -2947,12 +2919,12 @@ export async function startQueryRefund( return { oldTxState, newTxState }; }, ); - notifyTransition(ws, ctx.transactionId, transitionInfo); - ws.taskScheduler.startShepherdTask(ctx.taskId); + notifyTransition(wex, ctx.transactionId, transitionInfo); + wex.taskScheduler.startShepherdTask(ctx.taskId); } async function computeRefreshRequest( - ws: InternalWalletState, + wex: WalletExecutionContext, tx: WalletDbReadWriteTransaction<["coins", "denominations"]>, items: RefundItemRecord[], ): Promise { @@ -2963,7 +2935,7 @@ async function computeRefreshRequest( throw Error("coin not found"); } const denomInfo = await getDenomInfo( - ws, + wex, tx, coin.exchangeBaseUrl, coin.denomPubHash, @@ -3004,7 +2976,7 @@ function getItemStatus(rf: MerchantCoinRefundStatus): RefundItemStatus { * Store refunds, possibly creating a new refund group. */ async function storeRefunds( - ws: InternalWalletState, + wex: WalletExecutionContext, purchase: PurchaseRecord, refunds: MerchantCoinRefundStatus[], reason: RefundReason, @@ -3019,10 +2991,10 @@ async function storeRefunds( const newRefundGroupId = encodeCrock(randomBytes(32)); const now = TalerPreciseTimestamp.now(); - const download = await expectProposalDownload(ws, purchase); + const download = await expectProposalDownload(wex, purchase); const currency = Amounts.currencyOf(download.contractData.amount); - const result = await ws.db.runReadWriteTx( + const result = await wex.db.runReadWriteTx( [ "coins", "denominations", @@ -3116,12 +3088,12 @@ async function storeRefunds( if (newGroup) { const amountsRaw = newGroupRefunds.map((x) => x.refundAmount); const refreshCoins = await computeRefreshRequest( - ws, + wex, tx, newGroupRefunds, ); const outInfo = await calculateRefreshOutput( - ws, + wex, tx, currency, refreshCoins, @@ -3172,9 +3144,9 @@ async function storeRefunds( refundGroup.status = RefundGroupStatus.Failed; } await tx.refundGroups.put(refundGroup); - const refreshCoins = await computeRefreshRequest(ws, tx, items); + const refreshCoins = await computeRefreshRequest(wex, tx, items); await createRefreshGroup( - ws, + wex, tx, Amounts.currencyOf(download.contractData.amount), refreshCoins, @@ -3215,7 +3187,7 @@ async function storeRefunds( return TaskRunResult.finished(); } - notifyTransition(ws, transactionId, result.transitionInfo); + notifyTransition(wex, transactionId, result.transitionInfo); if (result.numPendingItemsTotal > 0) { return TaskRunResult.backoff(); -- cgit v1.2.3