From 7ba1d1f3351e58a331e99337afea0fbedb6eb828 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 6 Mar 2024 21:15:30 +0100 Subject: refactor coin selection, report maxEffectiveSpendAmount --- packages/taler-wallet-core/src/pay-merchant.ts | 63 ++++++++++++-------------- 1 file changed, 29 insertions(+), 34 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 ed58dc404..a155d6298 100644 --- a/packages/taler-wallet-core/src/pay-merchant.ts +++ b/packages/taler-wallet-core/src/pay-merchant.ts @@ -111,6 +111,7 @@ import { import { EddsaKeypair } from "./crypto/cryptoImplementation.js"; import { CoinRecord, + DbCoinSelection, DenominationRecord, PurchaseRecord, PurchaseStatus, @@ -445,11 +446,11 @@ export async function getTotalPaymentCost( wex: WalletExecutionContext, pcs: PayCoinSelection, ): Promise { - const currency = Amounts.currencyOf(pcs.paymentAmount); + const currency = Amounts.currencyOf(pcs.customerDepositFees); 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]); + for (let i = 0; i < pcs.coins.length; i++) { + const coin = await tx.coins.get(pcs.coins[i].coinPub); if (!coin) { throw Error("can't calculate payment cost, coin not found"); } @@ -470,7 +471,7 @@ export async function getTotalPaymentCost( ); const amountLeft = Amounts.sub( denom.value, - pcs.coinContributions[i], + pcs.coins[i].contribution, ).amount; const refreshCost = getTotalRefreshCost( allDenoms, @@ -478,10 +479,10 @@ export async function getTotalPaymentCost( amountLeft, wex.ws.config.testing.denomselAllowLate, ); - costs.push(Amounts.parseOrThrow(pcs.coinContributions[i])); + costs.push(Amounts.parseOrThrow(pcs.coins[i].contribution)); costs.push(refreshCost); } - const zero = Amounts.zeroOfAmount(pcs.paymentAmount); + const zero = Amounts.zeroOfAmount(pcs.customerDepositFees); return Amounts.sum([zero, ...costs]).amount; }); } @@ -617,7 +618,8 @@ async function processDownloadProposal( if (proposal.purchaseStatus != PurchaseStatus.PendingDownloadingProposal) { logger.error( - `unexpected state ${proposal.purchaseStatus}/${PurchaseStatus[proposal.purchaseStatus] + `unexpected state ${proposal.purchaseStatus}/${ + PurchaseStatus[proposal.purchaseStatus] } for ${ctx.transactionId} in processDownloadProposal`, ); return TaskRunResult.finished(); @@ -873,7 +875,8 @@ async function createOrReusePurchase( oldProposal.claimToken === claimToken ) { logger.info( - `Found old proposal (status=${PurchaseStatus[oldProposal.purchaseStatus] + `Found old proposal (status=${ + PurchaseStatus[oldProposal.purchaseStatus] }) for order ${orderId} at ${merchantBaseUrl}`, ); if (oldProposal.purchaseStatus === PurchaseStatus.DialogShared) { @@ -1137,26 +1140,10 @@ async function handleInsufficientFunds( 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) { - continue; - } const contrib = payCoinSelection.coinContributions[i]; - const coin = await tx.coins.get(coinPub); - if (!coin) { - continue; - } - const denom = await tx.denominations.get([ - coin.exchangeBaseUrl, - coin.denomPubHash, - ]); - if (!denom) { - continue; - } prevPayCoins.push({ coinPub, contribution: Amounts.parseOrThrow(contrib), - exchangeBaseUrl: coin.exchangeBaseUrl, - feeDeposit: Amounts.parseOrThrow(denom.fees.feeDeposit), }); } }); @@ -1199,7 +1186,11 @@ async function handleInsufficientFunds( if (!payInfo) { return; } - payInfo.payCoinSelection = res.coinSel; + // Convert to DB format + payInfo.payCoinSelection = { + coinContributions: res.coinSel.coins.map((x) => x.contribution), + coinPubs: res.coinSel.coins.map((x) => x.coinPub), + }; payInfo.payCoinSelectionUid = encodeCrock(getRandomBytes(32)); await tx.purchases.put(p); await spendCoins(wex, tx, { @@ -1286,13 +1277,14 @@ async function checkPaymentByProposalId( purchase.purchaseStatus === PurchaseStatus.DialogProposed || purchase.purchaseStatus === PurchaseStatus.DialogShared ) { + const instructedAmount = Amounts.parseOrThrow(contractData.amount); // If not already paid, check if we could pay for it. const res = await selectPayCoins(wex, { restrictExchanges: { auditors: [], exchanges: contractData.allowedExchanges, }, - contractTermsAmount: Amounts.parseOrThrow(contractData.amount), + contractTermsAmount: instructedAmount, depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee), wireFeeAmortization: contractData.wireFeeAmortization ?? 1, wireFeeLimit: Amounts.parseOrThrow(contractData.maxWireFee), @@ -1327,7 +1319,7 @@ async function checkPaymentByProposalId( transactionId, proposalId: proposal.proposalId, amountEffective: Amounts.stringify(totalCost), - amountRaw: Amounts.stringify(res.coinSel.paymentAmount), + amountRaw: Amounts.stringify(instructedAmount), contractTermsHash: d.contractData.contractTermsHash, talerUri, }; @@ -1599,7 +1591,7 @@ export async function preparePayForTemplate( */ export async function generateDepositPermissions( wex: WalletExecutionContext, - payCoinSel: PayCoinSelection, + payCoinSel: DbCoinSelection, contractData: WalletContractData, ): Promise { const depositPermissions: CoinDepositPermission[] = []; @@ -1608,7 +1600,7 @@ export async function generateDepositPermissions( denom: DenominationRecord; }> = []; await wex.db.runReadOnlyTx(["coins", "denominations"], async (tx) => { - for (let i = 0; i < payCoinSel.coinPubs.length; i++) { + for (let i = 0; i < payCoinSel.coinContributions.length; i++) { const coin = await tx.coins.get(payCoinSel.coinPubs[i]); if (!coin) { throw Error("can't pay, allocated coin not found anymore"); @@ -1626,7 +1618,7 @@ export async function generateDepositPermissions( } }); - for (let i = 0; i < payCoinSel.coinPubs.length; i++) { + for (let i = 0; i < payCoinSel.coinContributions.length; i++) { const { coin, denom } = coinWithDenom[i]; let wireInfoHash: string; wireInfoHash = contractData.wireInfoHash; @@ -1881,7 +1873,10 @@ export async function confirmPay( case PurchaseStatus.DialogShared: case PurchaseStatus.DialogProposed: p.payInfo = { - payCoinSelection: coinSelection, + payCoinSelection: { + coinContributions: coinSelection.coins.map((x) => x.contribution), + coinPubs: coinSelection.coins.map((x) => x.coinPub), + }, payCoinSelectionUid: encodeCrock(getRandomBytes(16)), totalPayCost: Amounts.stringify(payCostInfo), }; @@ -1895,9 +1890,9 @@ export async function confirmPay( tag: TransactionType.Payment, proposalId: proposalId, }), - coinPubs: coinSelection.coinPubs, - contributions: coinSelection.coinContributions.map((x) => - Amounts.parseOrThrow(x), + coinPubs: coinSelection.coins.map((x) => x.coinPub), + contributions: coinSelection.coins.map((x) => + Amounts.parseOrThrow(x.contribution), ), refreshReason: RefreshReason.PayMerchant, }); -- cgit v1.2.3