aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2024-06-13 19:55:48 +0200
committerFlorian Dold <florian@dold.me>2024-06-13 19:55:48 +0200
commit331ff2a22ddbe7512e3a54235a986e7275b88920 (patch)
treebdd255d988059ecde9c147367cabab70b8c7a315 /packages/taler-wallet-core
parent10aa5e767a35d39d6612f5e4addf4e04f3241a42 (diff)
downloadwallet-core-331ff2a22ddbe7512e3a54235a986e7275b88920.tar.xz
wallet-core: remove coinAllocationId, simplify coin history store
Diffstat (limited to 'packages/taler-wallet-core')
-rw-r--r--packages/taler-wallet-core/src/common.ts32
-rw-r--r--packages/taler-wallet-core/src/db.ts58
-rw-r--r--packages/taler-wallet-core/src/deposits.ts13
-rw-r--r--packages/taler-wallet-core/src/pay-merchant.ts20
-rw-r--r--packages/taler-wallet-core/src/pay-peer-pull-debit.ts19
-rw-r--r--packages/taler-wallet-core/src/pay-peer-push-debit.ts10
-rw-r--r--packages/taler-wallet-core/src/recoup.ts30
-rw-r--r--packages/taler-wallet-core/src/refresh.ts11
-rw-r--r--packages/taler-wallet-core/src/wallet.ts6
-rw-r--r--packages/taler-wallet-core/src/withdraw.ts1
10 files changed, 73 insertions, 127 deletions
diff --git a/packages/taler-wallet-core/src/common.ts b/packages/taler-wallet-core/src/common.ts
index 13c875575..9f63c74d5 100644
--- a/packages/taler-wallet-core/src/common.ts
+++ b/packages/taler-wallet-core/src/common.ts
@@ -74,9 +74,9 @@ export interface CoinsSpendInfo {
contributions: AmountJson[];
refreshReason: RefreshReason;
/**
- * Identifier for what the coin has been spent for.
+ * Transaction for which the coin is spent.
*/
- allocationId: TransactionIdStr;
+ transactionId: TransactionIdStr;
}
export async function makeCoinsVisible(
@@ -149,11 +149,16 @@ export async function makeCoinAvailable(
await tx.coinAvailability.put(car);
}
+/**
+ * Spend coins. Marks the coins as used, adds a coin history items
+ * and creates refresh group.
+ */
export async function spendCoins(
wex: WalletExecutionContext,
tx: WalletDbReadWriteTransaction<
[
"coins",
+ "coinHistory",
"coinAvailability",
"refreshGroups",
"refreshSessions",
@@ -194,28 +199,7 @@ export async function spendCoins(
`age denom info is missing for ${coin.maxAge}`,
);
const contrib = csi.contributions[i];
- if (coin.status !== CoinStatus.Fresh) {
- const alloc = coin.spendAllocation;
- if (!alloc) {
- continue;
- }
- if (alloc.id !== csi.allocationId) {
- // FIXME: assign error code
- logger.info("conflicting coin allocation ID");
- logger.info(`old ID: ${alloc.id}, new ID: ${csi.allocationId}`);
- throw Error("conflicting coin allocation (id)");
- }
- if (0 !== Amounts.cmp(alloc.amount, contrib)) {
- // FIXME: assign error code
- throw Error("conflicting coin allocation (contrib)");
- }
- continue;
- }
coin.status = CoinStatus.Dormant;
- coin.spendAllocation = {
- id: csi.allocationId,
- amount: Amounts.stringify(contrib),
- };
const remaining = Amounts.sub(denom.value, contrib);
if (remaining.saturated) {
throw Error("not enough remaining balance on coin for payment");
@@ -247,7 +231,7 @@ export async function spendCoins(
Amounts.currencyOf(csi.contributions[0]),
refreshCoinPubs,
csi.refreshReason,
- csi.allocationId,
+ csi.transactionId,
);
}
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index 0ce838fd2..bf5d29603 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -151,7 +151,7 @@ export const CURRENT_DB_CONFIG_KEY = "currentMainDbName";
* backwards-compatible way or object stores and indices
* are added.
*/
-export const WALLET_DB_MINOR_VERSION = 10;
+export const WALLET_DB_MINOR_VERSION = 11;
declare const symDbProtocolTimestamp: unique symbol;
@@ -894,15 +894,6 @@ export interface CoinRecord {
visible?: number;
/**
- * Information about what the coin has been allocated for.
- *
- * Used for:
- * - Diagnostics
- * - Idempotency of applying a coin selection (e.g. after re-selection)
- */
- spendAllocation: CoinAllocation | undefined;
-
- /**
* Maximum age of purchases that can be made with this coin.
*
* (Used for indexing, redundant with {@link ageCommitmentProof}).
@@ -912,36 +903,44 @@ export interface CoinRecord {
ageCommitmentProof: AgeCommitmentProof | undefined;
}
-export type HistoryItem =
+export type WalletCoinHistoryItem =
| {
type: "withdraw";
transactionId: TransactionIdStr;
}
- | { type: "spend"; transactionId: TransactionIdStr; amount: AmountString }
- | { type: "refresh"; transactionId: TransactionIdStr; amount: AmountString }
- | { type: "recoup"; transactionId: TransactionIdStr; amount: AmountString }
- | { type: "refund"; transactionId: TransactionIdStr; amount: AmountString };
+ | {
+ type: "spend";
+ transactionId: TransactionIdStr;
+ amount: AmountString;
+ }
+ | {
+ type: "refresh";
+ transactionId: TransactionIdStr;
+ amount: AmountString;
+ }
+ | {
+ type: "recoup";
+ transactionId: TransactionIdStr;
+ amount: AmountString;
+ }
+ | {
+ type: "refund";
+ transactionId: TransactionIdStr;
+ amount: AmountString;
+ };
/**
* History event for a coin from the wallet's perspective.
*/
export interface CoinHistoryRecord {
coinPub: string;
-
- timestamp: DbPreciseTimestamp;
-
- item: HistoryItem;
-}
-
-/**
- * Coin allocation, i.e. what a coin has been used for.
- */
-export interface CoinAllocation {
/**
- * ID of the allocation, should be the ID of the transaction that
+ * History items for the coin.
+ *
+ * We store this as an array in the object store, as the coin history
+ * is pretty much always very small.
*/
- id: TransactionIdStr;
- amount: AmountString;
+ history: WalletCoinHistoryItem[];
}
export enum RefreshCoinStatus {
@@ -2447,7 +2446,8 @@ export const WalletStoresV1 = {
coinHistory: describeStoreV2({
storeName: "coinHistory",
recordCodec: passthroughCodec<CoinHistoryRecord>(),
- keyPath: ["coinPub", "timestamp"],
+ keyPath: "coinPub",
+ versionAdded: 11,
}),
coins: describeStore(
"coins",
diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts
index 23b52ac5c..06b57fc79 100644
--- a/packages/taler-wallet-core/src/deposits.ts
+++ b/packages/taler-wallet-core/src/deposits.ts
@@ -983,6 +983,7 @@ async function processDepositGroupPendingDeposit(
const transitionDone = await wex.db.runReadWriteTx(
{
storeNames: [
+ "contractTerms",
"exchanges",
"exchangeDetails",
"depositGroups",
@@ -1001,6 +1002,14 @@ async function processDepositGroupPendingDeposit(
if (dg.statusPerCoin) {
return false;
}
+
+ const contractTermsRec = tx.contractTerms.get(
+ depositGroup.contractTermsHash,
+ );
+ if (!contractTermsRec) {
+ throw Error("contract terms for deposit not found in database");
+ }
+
const payCoinSel = await selectPayCoinsInTx(wex, tx, {
restrictExchanges: {
auditors: [],
@@ -1044,7 +1053,7 @@ async function processDepositGroupPendingDeposit(
);
await tx.depositGroups.put(dg);
await spendCoins(wex, tx, {
- allocationId: transactionId,
+ transactionId,
coinPubs: dg.payCoinSelection.coinPubs,
contributions: dg.payCoinSelection.coinContributions.map((x) =>
Amounts.parseOrThrow(x),
@@ -1622,7 +1631,7 @@ export async function createDepositGroup(
async (tx) => {
if (depositGroup.payCoinSelection) {
await spendCoins(wex, tx, {
- allocationId: transactionId,
+ transactionId,
coinPubs: depositGroup.payCoinSelection.coinPubs,
contributions: depositGroup.payCoinSelection.coinContributions.map(
(x) => Amounts.parseOrThrow(x),
diff --git a/packages/taler-wallet-core/src/pay-merchant.ts b/packages/taler-wallet-core/src/pay-merchant.ts
index 993b12dd1..37faae686 100644
--- a/packages/taler-wallet-core/src/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/pay-merchant.ts
@@ -1131,6 +1131,8 @@ async function handleInsufficientFunds(
): Promise<void> {
logger.trace("handling insufficient funds, trying to re-select coins");
+ const ctx = new PayMerchantTransactionContext(wex, proposalId);
+
const proposal = await wex.db.runReadOnlyTx(
{ storeNames: ["purchases"] },
async (tx) => {
@@ -1247,11 +1249,7 @@ async function handleInsufficientFunds(
payInfo.payCoinSelectionUid = encodeCrock(getRandomBytes(32));
await tx.purchases.put(p);
await spendCoins(wex, tx, {
- // allocationId: `txn:proposal:${p.proposalId}`,
- allocationId: constructTransactionIdentifier({
- tag: TransactionType.Payment,
- proposalId: proposalId,
- }),
+ transactionId: ctx.transactionId,
coinPubs: payInfo.payCoinSelection.coinPubs,
contributions: payInfo.payCoinSelection.coinContributions.map((x) =>
Amounts.parseOrThrow(x),
@@ -2049,11 +2047,7 @@ export async function confirmPay(
if (p.payInfo.payCoinSelection) {
const sel = p.payInfo.payCoinSelection;
await spendCoins(wex, tx, {
- //`txn:proposal:${p.proposalId}`
- allocationId: constructTransactionIdentifier({
- tag: TransactionType.Payment,
- proposalId: proposalId,
- }),
+ transactionId: transactionId as TransactionIdStr,
coinPubs: sel.coinPubs,
contributions: sel.coinContributions.map((x) =>
Amounts.parseOrThrow(x),
@@ -2308,11 +2302,7 @@ async function processPurchasePay(
await tx.purchases.put(p);
await spendCoins(wex, tx, {
- //`txn:proposal:${p.proposalId}`
- allocationId: constructTransactionIdentifier({
- tag: TransactionType.Payment,
- proposalId: proposalId,
- }),
+ transactionId: ctx.transactionId,
coinPubs: selectCoinsResult.coinSel.coins.map((x) => x.coinPub),
contributions: selectCoinsResult.coinSel.coins.map((x) =>
Amounts.parseOrThrow(x.contribution),
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 e9be15026..42b777e38 100644
--- a/packages/taler-wallet-core/src/pay-peer-pull-debit.ts
+++ b/packages/taler-wallet-core/src/pay-peer-pull-debit.ts
@@ -496,11 +496,7 @@ async function processPeerPullDebitPendingDeposit(
return false;
}
await spendCoins(wex, tx, {
- // allocationId: `txn:peer-pull-debit:${req.peerPullDebitId}`,
- allocationId: constructTransactionIdentifier({
- tag: TransactionType.PeerPullDebit,
- peerPullDebitId,
- }),
+ transactionId: ctx.transactionId,
coinPubs: coinSelRes.result.coins.map((x) => x.coinPub),
contributions: coinSelRes.result.coins.map((x) =>
Amounts.parseOrThrow(x.contribution),
@@ -697,6 +693,9 @@ export async function confirmPeerPullDebit(
);
}
+ const ctx = new PeerPullDebitTransactionContext(wex, peerPullDebitId);
+ const transactionId = ctx.transactionId;
+
const instructedAmount = Amounts.parseOrThrow(peerPullInc.amount);
const coinSelRes = await selectPeerCoins(wex, {
@@ -752,11 +751,7 @@ export async function confirmPeerPullDebit(
}
if (coinSelRes.type == "success") {
await spendCoins(wex, tx, {
- // allocationId: `txn:peer-pull-debit:${req.peerPullDebitId}`,
- allocationId: constructTransactionIdentifier({
- tag: TransactionType.PeerPullDebit,
- peerPullDebitId,
- }),
+ transactionId,
coinPubs: coinSelRes.result.coins.map((x) => x.coinPub),
contributions: coinSelRes.result.coins.map((x) =>
Amounts.parseOrThrow(x.contribution),
@@ -774,10 +769,6 @@ export async function confirmPeerPullDebit(
},
);
- const ctx = new PeerPullDebitTransactionContext(wex, peerPullDebitId);
-
- const transactionId = ctx.transactionId;
-
wex.ws.notify({
type: NotificationType.BalanceChange,
hintTransactionId: transactionId,
diff --git a/packages/taler-wallet-core/src/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/pay-peer-push-debit.ts
index 9c31de06a..6ce426bb0 100644
--- a/packages/taler-wallet-core/src/pay-peer-push-debit.ts
+++ b/packages/taler-wallet-core/src/pay-peer-push-debit.ts
@@ -543,10 +543,7 @@ async function processPeerPushDebitCreateReserve(
// we might want to mark the coins as used and spend them
// after we've been able to create the purse.
await spendCoins(wex, tx, {
- allocationId: constructTransactionIdentifier({
- tag: TransactionType.PeerPushDebit,
- pursePub,
- }),
+ transactionId: ctx.transactionId,
coinPubs: coinSelRes.result.coins.map((x) => x.coinPub),
contributions: coinSelRes.result.coins.map((x) =>
Amounts.parseOrThrow(x.contribution),
@@ -1170,10 +1167,7 @@ export async function initiatePeerPushDebit(
// we might want to mark the coins as used and spend them
// after we've been able to create the purse.
await spendCoins(wex, tx, {
- allocationId: constructTransactionIdentifier({
- tag: TransactionType.PeerPushDebit,
- pursePub: pursePair.pub,
- }),
+ transactionId: ctx.transactionId,
coinPubs: coinSelRes.result.coins.map((x) => x.coinPub),
contributions: coinSelRes.result.coins.map((x) =>
Amounts.parseOrThrow(x.contribution),
diff --git a/packages/taler-wallet-core/src/recoup.ts b/packages/taler-wallet-core/src/recoup.ts
index be5731b0b..9adc5e2ad 100644
--- a/packages/taler-wallet-core/src/recoup.ts
+++ b/packages/taler-wallet-core/src/recoup.ts
@@ -199,24 +199,20 @@ async function recoupRefreshCoin(
revokedCoin.exchangeBaseUrl,
revokedCoin.denomPubHash,
);
- checkDbInvariant(!!oldCoinDenom, `no denom for coin, hash ${oldCoin.denomPubHash}`);
- checkDbInvariant(!!revokedCoinDenom, `no revoked denom for coin, hash ${revokedCoin.denomPubHash}`);
+ checkDbInvariant(
+ !!oldCoinDenom,
+ `no denom for coin, hash ${oldCoin.denomPubHash}`,
+ );
+ checkDbInvariant(
+ !!revokedCoinDenom,
+ `no revoked denom for coin, hash ${revokedCoin.denomPubHash}`,
+ );
revokedCoin.status = CoinStatus.Dormant;
- if (!revokedCoin.spendAllocation) {
- // We don't know what happened to this coin
- logger.error(
- `can't refresh-recoup coin ${revokedCoin.coinPub}, no spendAllocation known`,
- );
- } else {
- let residualAmount = Amounts.sub(
- revokedCoinDenom.value,
- revokedCoin.spendAllocation.amount,
- ).amount;
- recoupGroup.scheduleRefreshCoins.push({
- coinPub: oldCoin.coinPub,
- amount: Amounts.stringify(residualAmount),
- });
- }
+ // FIXME: Schedule recoup for the sum of refreshes, based on the coin event history.
+ // recoupGroup.scheduleRefreshCoins.push({
+ // coinPub: oldCoin.coinPub,
+ // amount: Amounts.stringify(refreshAmount),
+ // });
await tx.coins.put(revokedCoin);
await tx.coins.put(oldCoin);
await putGroupAsFinished(wex, tx, recoupGroup, coinIdx);
diff --git a/packages/taler-wallet-core/src/refresh.ts b/packages/taler-wallet-core/src/refresh.ts
index 05c65f6b6..cb39e6588 100644
--- a/packages/taler-wallet-core/src/refresh.ts
+++ b/packages/taler-wallet-core/src/refresh.ts
@@ -1211,7 +1211,6 @@ async function refreshReveal(
coinEvHash: pc.coinEvHash,
maxAge: pc.maxAge,
ageCommitmentProof: pc.ageCommitmentProof,
- spendAllocation: undefined,
};
coins.push(coin);
@@ -1605,16 +1604,6 @@ async function applyRefreshToOldCoins(
default:
assertUnreachable(coin.status);
}
- if (!coin.spendAllocation) {
- coin.spendAllocation = {
- amount: Amounts.stringify(ocp.amount),
- // id: `txn:refresh:${refreshGroupId}`,
- id: constructTransactionIdentifier({
- tag: TransactionType.Refresh,
- refreshGroupId,
- }),
- };
- }
await tx.coins.put(coin);
}
}
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index 2a84d912d..bc2011883 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -557,12 +557,6 @@ async function dumpCoins(wex: WalletExecutionContext): Promise<CoinDumpJson> {
withdrawal_reserve_pub: withdrawalReservePub,
coin_status: c.status,
ageCommitmentProof: c.ageCommitmentProof,
- spend_allocation: c.spendAllocation
- ? {
- amount: c.spendAllocation.amount,
- id: c.spendAllocation.id,
- }
- : undefined,
});
}
},
diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts
index 8bc4aafd1..2af8807cc 100644
--- a/packages/taler-wallet-core/src/withdraw.ts
+++ b/packages/taler-wallet-core/src/withdraw.ts
@@ -1462,7 +1462,6 @@ async function processPlanchetVerifyAndStoreCoin(
sourceTransactionId: transactionId,
maxAge: withdrawalGroup.restrictAge ?? AgeRestriction.AGE_UNRESTRICTED,
ageCommitmentProof: planchet.ageCommitmentProof,
- spendAllocation: undefined,
};
const planchetCoinPub = planchet.coinPub;