summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/operations/pay.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-06-22 18:43:11 +0200
committerFlorian Dold <florian@dold.me>2021-06-22 18:43:11 +0200
commit09d1dd83ec1bf9ca16841d0afb18b9a7da705bcb (patch)
treeaf089d994c233888f3f7291fdc148a44142296ed /packages/taler-wallet-core/src/operations/pay.ts
parent39c4b42dafc5d8fc5f455e7ed936c45ec2340cfc (diff)
downloadwallet-core-09d1dd83ec1bf9ca16841d0afb18b9a7da705bcb.tar.gz
wallet-core-09d1dd83ec1bf9ca16841d0afb18b9a7da705bcb.tar.bz2
wallet-core-09d1dd83ec1bf9ca16841d0afb18b9a7da705bcb.zip
prevent conflicting coin allocation with concurrent payments
Diffstat (limited to 'packages/taler-wallet-core/src/operations/pay.ts')
-rw-r--r--packages/taler-wallet-core/src/operations/pay.ts33
1 files changed, 22 insertions, 11 deletions
diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts
index 71f11c960..280586c34 100644
--- a/packages/taler-wallet-core/src/operations/pay.ts
+++ b/packages/taler-wallet-core/src/operations/pay.ts
@@ -385,24 +385,34 @@ export async function applyCoinSpend(
denominations: typeof WalletStoresV1.denominations;
}>,
coinSelection: PayCoinSelection,
+ allocationId: string,
) {
for (let i = 0; i < coinSelection.coinPubs.length; i++) {
const coin = await tx.coins.get(coinSelection.coinPubs[i]);
if (!coin) {
throw Error("coin allocated for payment doesn't exist anymore");
}
+ const contrib = coinSelection.coinContributions[i];
if (coin.status !== CoinStatus.Fresh) {
- // applyCoinSpend was called again, probably
- // because of a coin re-selection to recover after
- // accidental double spending.
- // Ignore coins we already marked as spent.
- continue;
+ const alloc = coin.allocation;
+ if (!alloc) {
+ continue;
+ }
+ if (alloc.id !== allocationId) {
+ // FIXME: assign error code
+ throw Error("conflicting coin allocation (id)");
+ }
+ if (0 !== Amounts.cmp(alloc.amount, contrib)) {
+ // FIXME: assign error code
+ throw Error("conflicting coin allocation (contrib)");
+ }
}
coin.status = CoinStatus.Dormant;
- const remaining = Amounts.sub(
- coin.currentAmount,
- coinSelection.coinContributions[i],
- );
+ coin.allocation = {
+ id: allocationId,
+ amount: Amounts.stringify(contrib),
+ };
+ const remaining = Amounts.sub(coin.currentAmount, contrib);
if (remaining.saturated) {
throw Error("not enough remaining balance on coin for payment");
}
@@ -482,7 +492,7 @@ async function recordConfirmPay(
await tx.proposals.put(p);
}
await tx.purchases.put(t);
- await applyCoinSpend(ws, tx, coinSelection);
+ await applyCoinSpend(ws, tx, coinSelection, `proposal:${t.proposalId}`);
});
ws.notify({
@@ -1082,9 +1092,10 @@ async function handleInsufficientFunds(
return;
}
p.payCoinSelection = res;
+ p.payCoinSelectionUid = encodeCrock(getRandomBytes(32));
p.coinDepositPermissions = undefined;
await tx.purchases.put(p);
- await applyCoinSpend(ws, tx, res);
+ await applyCoinSpend(ws, tx, res, `proposal:${p.proposalId}`);
});
}