summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/pay-peer-pull-debit.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2024-04-04 20:48:19 +0200
committerFlorian Dold <florian@dold.me>2024-04-04 20:48:26 +0200
commitab724bdbd2059484335211662b63a9ae415a270c (patch)
treedd96150dae760dd4cfc2068c7ba990277137200e /packages/taler-wallet-core/src/pay-peer-pull-debit.ts
parent58323fc496d0fe2a34884b289860264ffd1310e8 (diff)
downloadwallet-core-ab724bdbd2059484335211662b63a9ae415a270c.tar.gz
wallet-core-ab724bdbd2059484335211662b63a9ae415a270c.tar.bz2
wallet-core-ab724bdbd2059484335211662b63a9ae415a270c.zip
wallet-core: allow peer-pull with coins locked behind refresh
Diffstat (limited to 'packages/taler-wallet-core/src/pay-peer-pull-debit.ts')
-rw-r--r--packages/taler-wallet-core/src/pay-peer-pull-debit.ts145
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"],