diff options
Diffstat (limited to 'packages')
-rw-r--r-- | packages/taler-util/src/wallet-types.ts | 6 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/common.ts | 32 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/db.ts | 58 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/deposits.ts | 13 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/pay-merchant.ts | 20 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/pay-peer-pull-debit.ts | 19 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/pay-peer-push-debit.ts | 10 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/recoup.ts | 30 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/refresh.ts | 11 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/wallet.ts | 6 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/withdraw.ts | 1 |
11 files changed, 73 insertions, 133 deletions
diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts index 8fa5b1e69..82ccb4fc4 100644 --- a/packages/taler-util/src/wallet-types.ts +++ b/packages/taler-util/src/wallet-types.ts @@ -664,12 +664,6 @@ export interface CoinDumpJson { */ withdrawal_reserve_pub: string | undefined; coin_status: CoinStatus; - spend_allocation: - | { - id: string; - amount: AmountString; - } - | undefined; /** * Information about the age restriction */ 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; |