diff options
author | Florian Dold <florian@dold.me> | 2024-04-04 20:48:19 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2024-04-04 20:48:26 +0200 |
commit | ab724bdbd2059484335211662b63a9ae415a270c (patch) | |
tree | dd96150dae760dd4cfc2068c7ba990277137200e /packages/taler-wallet-core | |
parent | 58323fc496d0fe2a34884b289860264ffd1310e8 (diff) | |
download | wallet-core-ab724bdbd2059484335211662b63a9ae415a270c.tar.xz |
wallet-core: allow peer-pull with coins locked behind refresh
Diffstat (limited to 'packages/taler-wallet-core')
-rw-r--r-- | packages/taler-wallet-core/src/pay-peer-pull-debit.ts | 145 |
1 files changed, 114 insertions, 31 deletions
diff --git a/packages/taler-wallet-core/src/pay-peer-pull-debit.ts b/packages/taler-wallet-core/src/pay-peer-pull-debit.ts index 9bfa14ca2..705317eb6 100644 --- a/packages/taler-wallet-core/src/pay-peer-pull-debit.ts +++ b/packages/taler-wallet-core/src/pay-peer-pull-debit.ts @@ -37,6 +37,7 @@ import { PreparePeerPullDebitRequest, PreparePeerPullDebitResponse, RefreshReason, + SelectedProspectiveCoin, TalerError, TalerErrorCode, TalerPreciseTimestamp, @@ -427,8 +428,88 @@ async function processPeerPullDebitPendingDeposit( const pursePub = peerPullInc.pursePub; const coinSel = peerPullInc.coinSel; + if (!coinSel) { - throw Error("invalid state, no coins selected"); + const instructedAmount = Amounts.parseOrThrow(peerPullInc.amount); + + const coinSelRes = await selectPeerCoins(wex, { + instructedAmount, + }); + if (logger.shouldLogTrace()) { + logger.trace(`selected p2p coins (pull): ${j2s(coinSelRes)}`); + } + + let coins: SelectedProspectiveCoin[] | undefined = undefined; + + switch (coinSelRes.type) { + case "failure": + throw TalerError.fromDetail( + TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE, + { + insufficientBalanceDetails: coinSelRes.insufficientBalanceDetails, + }, + ); + case "prospective": + throw Error("insufficient balance (locked behind refresh)"); + case "success": + coins = coinSelRes.result.coins; + break; + default: + assertUnreachable(coinSelRes); + } + + const peerPullDebitId = peerPullInc.peerPullDebitId; + const totalAmount = await getTotalPeerPaymentCost(wex, coins); + + // FIXME: Missing notification here! + + const transitionDone = await wex.db.runReadWriteTx( + [ + "exchanges", + "coins", + "denominations", + "refreshGroups", + "refreshSessions", + "peerPullDebit", + "coinAvailability", + ], + async (tx) => { + const pi = await tx.peerPullDebit.get(peerPullDebitId); + if (!pi) { + return false; + } + if (pi.status !== PeerPullDebitRecordStatus.PendingDeposit) { + return false; + } + if (pi.coinSel) { + return false; + } + await spendCoins(wex, tx, { + // allocationId: `txn:peer-pull-debit:${req.peerPullDebitId}`, + allocationId: constructTransactionIdentifier({ + tag: TransactionType.PeerPullDebit, + peerPullDebitId, + }), + coinPubs: coinSelRes.result.coins.map((x) => x.coinPub), + contributions: coinSelRes.result.coins.map((x) => + Amounts.parseOrThrow(x.contribution), + ), + refreshReason: RefreshReason.PayPeerPull, + }); + pi.coinSel = { + coinPubs: coinSelRes.result.coins.map((x) => x.coinPub), + contributions: coinSelRes.result.coins.map((x) => x.contribution), + totalCost: Amounts.stringify(totalAmount), + }; + await tx.peerPullDebit.put(pi); + return true; + }, + ); + if (transitionDone) { + return TaskRunResult.progress(); + } else { + return TaskRunResult.backoff(); + } } const coins = await queryCoinInfosForSelection(wex, coinSel); @@ -595,8 +676,6 @@ export async function confirmPeerPullDebit( const instructedAmount = Amounts.parseOrThrow(peerPullInc.amount); - // FIXME: Select coins once with pending coins, once without. - const coinSelRes = await selectPeerCoins(wex, { instructedAmount, }); @@ -604,6 +683,8 @@ export async function confirmPeerPullDebit( logger.trace(`selected p2p coins (pull): ${j2s(coinSelRes)}`); } + let coins: SelectedProspectiveCoin[] | undefined = undefined; + switch (coinSelRes.type) { case "failure": throw TalerError.fromDetail( @@ -613,19 +694,18 @@ export async function confirmPeerPullDebit( }, ); case "prospective": - throw Error("insufficient balance (blocked on refresh)"); + coins = coinSelRes.result.prospectiveCoins; + break; case "success": + coins = coinSelRes.result.coins; break; default: assertUnreachable(coinSelRes); } - const sel = coinSelRes.result; + const totalAmount = await getTotalPeerPaymentCost(wex, coins); - const totalAmount = await getTotalPeerPaymentCost( - wex, - coinSelRes.result.coins, - ); + // FIXME: Missing notification here! await wex.db.runReadWriteTx( [ @@ -638,31 +718,33 @@ export async function confirmPeerPullDebit( "coinAvailability", ], async (tx) => { - await spendCoins(wex, tx, { - // allocationId: `txn:peer-pull-debit:${req.peerPullDebitId}`, - allocationId: constructTransactionIdentifier({ - tag: TransactionType.PeerPullDebit, - peerPullDebitId, - }), - coinPubs: sel.coins.map((x) => x.coinPub), - contributions: sel.coins.map((x) => - Amounts.parseOrThrow(x.contribution), - ), - refreshReason: RefreshReason.PayPeerPull, - }); - const pi = await tx.peerPullDebit.get(peerPullDebitId); if (!pi) { throw Error(); } - if (pi.status === PeerPullDebitRecordStatus.DialogProposed) { - pi.status = PeerPullDebitRecordStatus.PendingDeposit; + if (pi.status !== PeerPullDebitRecordStatus.DialogProposed) { + return; + } + if (coinSelRes.type == "success") { + await spendCoins(wex, tx, { + // allocationId: `txn:peer-pull-debit:${req.peerPullDebitId}`, + allocationId: constructTransactionIdentifier({ + tag: TransactionType.PeerPullDebit, + peerPullDebitId, + }), + coinPubs: coinSelRes.result.coins.map((x) => x.coinPub), + contributions: coinSelRes.result.coins.map((x) => + Amounts.parseOrThrow(x.contribution), + ), + refreshReason: RefreshReason.PayPeerPull, + }); pi.coinSel = { - coinPubs: sel.coins.map((x) => x.coinPub), - contributions: sel.coins.map((x) => x.contribution), + coinPubs: coinSelRes.result.coins.map((x) => x.coinPub), + contributions: coinSelRes.result.coins.map((x) => x.contribution), totalCost: Amounts.stringify(totalAmount), }; } + pi.status = PeerPullDebitRecordStatus.PendingDeposit; await tx.peerPullDebit.put(pi); }, ); @@ -788,6 +870,8 @@ export async function preparePeerPullDebit( logger.trace(`selected p2p coins (pull): ${j2s(coinSelRes)}`); } + let coins: SelectedProspectiveCoin[] | undefined = undefined; + switch (coinSelRes.type) { case "failure": throw TalerError.fromDetail( @@ -797,17 +881,16 @@ export async function preparePeerPullDebit( }, ); case "prospective": - throw Error("insufficient balance (waiting on refresh)"); + coins = coinSelRes.result.prospectiveCoins; + break; case "success": + coins = coinSelRes.result.coins; break; default: assertUnreachable(coinSelRes); } - const totalAmount = await getTotalPeerPaymentCost( - wex, - coinSelRes.result.coins, - ); + const totalAmount = await getTotalPeerPaymentCost(wex, coins); await wex.db.runReadWriteTx( ["peerPullDebit", "contractTerms"], |