From c660db82c12e08020661828f1d8383baa7ef0e02 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 7 Sep 2023 20:35:46 +0200 Subject: wallet-core: address DB FIXMEs, systematic state numbering --- .../src/operations/transactions.ts | 122 ++++++++++----------- 1 file changed, 61 insertions(+), 61 deletions(-) (limited to 'packages/taler-wallet-core/src/operations/transactions.ts') diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 7f5302b25..5c57195c1 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -50,10 +50,10 @@ import { OperationRetryRecord, PeerPullPaymentIncomingRecord, PeerPullDebitRecordStatus, - PeerPullPaymentInitiationRecord, + PeerPullCreditRecord, PeerPushPaymentIncomingRecord, - PeerPushPaymentIncomingStatus, - PeerPushPaymentInitiationRecord, + PeerPushCreditStatus, + PeerPushDebitRecord, PurchaseRecord, PurchaseStatus, RefreshGroupRecord, @@ -335,10 +335,10 @@ export async function getTransactionById( } case TransactionType.PeerPullDebit: { return await ws.db - .mktx((x) => [x.peerPullPaymentIncoming]) + .mktx((x) => [x.peerPullDebit]) .runReadWrite(async (tx) => { - const debit = await tx.peerPullPaymentIncoming.get( - parsedTx.peerPullPaymentIncomingId, + const debit = await tx.peerPullDebit.get( + parsedTx.peerPullDebitId, ); if (!debit) throw Error("not found"); return buildTransactionForPullPaymentDebit(debit); @@ -347,9 +347,9 @@ export async function getTransactionById( case TransactionType.PeerPushDebit: { return await ws.db - .mktx((x) => [x.peerPushPaymentInitiations, x.contractTerms]) + .mktx((x) => [x.peerPushDebit, x.contractTerms]) .runReadWrite(async (tx) => { - const debit = await tx.peerPushPaymentInitiations.get( + const debit = await tx.peerPushDebit.get( parsedTx.pursePub, ); if (!debit) throw Error("not found"); @@ -363,17 +363,17 @@ export async function getTransactionById( } case TransactionType.PeerPushCredit: { - const peerPushPaymentIncomingId = parsedTx.peerPushPaymentIncomingId; + const peerPushCreditId = parsedTx.peerPushCreditId; return await ws.db .mktx((x) => [ - x.peerPushPaymentIncoming, + x.peerPushCredit, x.contractTerms, x.withdrawalGroups, x.operationRetries, ]) .runReadWrite(async (tx) => { - const pushInc = await tx.peerPushPaymentIncoming.get( - peerPushPaymentIncomingId, + const pushInc = await tx.peerPushCredit.get( + peerPushCreditId, ); if (!pushInc) throw Error("not found"); const ct = await tx.contractTerms.get(pushInc.contractTermsHash); @@ -405,13 +405,13 @@ export async function getTransactionById( const pursePub = parsedTx.pursePub; return await ws.db .mktx((x) => [ - x.peerPullPaymentInitiations, + x.peerPullCredit, x.contractTerms, x.withdrawalGroups, x.operationRetries, ]) .runReadWrite(async (tx) => { - const pushInc = await tx.peerPullPaymentInitiations.get(pursePub); + const pushInc = await tx.peerPullCredit.get(pursePub); if (!pushInc) throw Error("not found"); const ct = await tx.contractTerms.get(pushInc.contractTermsHash); checkDbInvariant(!!ct); @@ -442,7 +442,7 @@ export async function getTransactionById( } function buildTransactionForPushPaymentDebit( - pi: PeerPushPaymentInitiationRecord, + pi: PeerPushDebitRecord, contractTerms: PeerContractTerms, ort?: OperationRetryRecord, ): Transaction { @@ -490,14 +490,14 @@ function buildTransactionForPullPaymentDebit( timestamp: pi.timestampCreated, transactionId: constructTransactionIdentifier({ tag: TransactionType.PeerPullDebit, - peerPullPaymentIncomingId: pi.peerPullPaymentIncomingId, + peerPullDebitId: pi.peerPullDebitId, }), ...(ort?.lastError ? { error: ort.lastError } : {}), }; } function buildTransactionForPeerPullCredit( - pullCredit: PeerPullPaymentInitiationRecord, + pullCredit: PeerPullCreditRecord, pullCreditOrt: OperationRetryRecord | undefined, peerContractTerms: PeerContractTerms, wsr: WithdrawalGroupRecord | undefined, @@ -606,7 +606,7 @@ function buildTransactionForPeerPushCredit( timestamp: wsr.timestampStart, transactionId: constructTransactionIdentifier({ tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId: pushInc.peerPushPaymentIncomingId, + peerPushCreditId: pushInc.peerPushCreditId, }), kycUrl: pushInc.kycUrl, ...(wsrOrt?.lastError ? { error: wsrOrt.lastError } : {}), @@ -629,7 +629,7 @@ function buildTransactionForPeerPushCredit( timestamp: pushInc.timestamp, transactionId: constructTransactionIdentifier({ tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId: pushInc.peerPushPaymentIncomingId, + peerPushCreditId: pushInc.peerPushCreditId, }), ...(pushOrt?.lastError ? { error: pushOrt.lastError } : {}), }; @@ -654,7 +654,7 @@ function buildTransactionForBankIntegratedWithdraw( reservePub: wgRecord.reservePub, bankConfirmationUrl: wgRecord.wgInfo.bankInfo.confirmUrl, reserveIsReady: - wgRecord.status === WithdrawalGroupStatus.Finished || + wgRecord.status === WithdrawalGroupStatus.Done || wgRecord.status === WithdrawalGroupStatus.PendingReady, }, kycUrl: wgRecord.kycUrl, @@ -698,7 +698,7 @@ function buildTransactionForManualWithdraw( reservePub: withdrawalGroup.reservePub, exchangePaytoUris, reserveIsReady: - withdrawalGroup.status === WithdrawalGroupStatus.Finished || + withdrawalGroup.status === WithdrawalGroupStatus.Done || withdrawalGroup.status === WithdrawalGroupStatus.PendingReady, }, kycUrl: withdrawalGroup.kycUrl, @@ -944,10 +944,10 @@ export async function getTransactions( x.exchangeDetails, x.exchanges, x.operationRetries, - x.peerPullPaymentIncoming, - x.peerPushPaymentInitiations, - x.peerPushPaymentIncoming, - x.peerPullPaymentInitiations, + x.peerPullDebit, + x.peerPushDebit, + x.peerPushCredit, + x.peerPullCredit, x.planchets, x.purchases, x.contractTerms, @@ -985,7 +985,7 @@ export async function getTransactions( } if ( pi.status !== PeerPullDebitRecordStatus.PendingDeposit && - pi.status !== PeerPullDebitRecordStatus.DonePaid + pi.status !== PeerPullDebitRecordStatus.Done ) { return; } @@ -1004,7 +1004,7 @@ export async function getTransactions( if (shouldSkipSearch(transactionsRequest, [])) { return; } - if (pi.status === PeerPushPaymentIncomingStatus.DialogProposed) { + if (pi.status === PeerPushCreditStatus.DialogProposed) { // We don't report proposed push credit transactions, user needs // to scan URI again and confirm to see it. return; @@ -1268,9 +1268,9 @@ export async function getTransactions( export type ParsedTransactionIdentifier = | { tag: TransactionType.Deposit; depositGroupId: string } | { tag: TransactionType.Payment; proposalId: string } - | { tag: TransactionType.PeerPullDebit; peerPullPaymentIncomingId: string } + | { tag: TransactionType.PeerPullDebit; peerPullDebitId: string } | { tag: TransactionType.PeerPullCredit; pursePub: string } - | { tag: TransactionType.PeerPushCredit; peerPushPaymentIncomingId: string } + | { tag: TransactionType.PeerPushCredit; peerPushCreditId: string } | { tag: TransactionType.PeerPushDebit; pursePub: string } | { tag: TransactionType.Refresh; refreshGroupId: string } | { tag: TransactionType.Refund; refundGroupId: string } @@ -1289,9 +1289,9 @@ export function constructTransactionIdentifier( case TransactionType.PeerPullCredit: return `txn:${pTxId.tag}:${pTxId.pursePub}` as TransactionIdStr; case TransactionType.PeerPullDebit: - return `txn:${pTxId.tag}:${pTxId.peerPullPaymentIncomingId}` as TransactionIdStr; + return `txn:${pTxId.tag}:${pTxId.peerPullDebitId}` as TransactionIdStr; case TransactionType.PeerPushCredit: - return `txn:${pTxId.tag}:${pTxId.peerPushPaymentIncomingId}` as TransactionIdStr; + return `txn:${pTxId.tag}:${pTxId.peerPushCreditId}` as TransactionIdStr; case TransactionType.PeerPushDebit: return `txn:${pTxId.tag}:${pTxId.pursePub}` as TransactionIdStr; case TransactionType.Refresh: @@ -1337,12 +1337,12 @@ export function parseTransactionIdentifier( case TransactionType.PeerPullDebit: return { tag: TransactionType.PeerPullDebit, - peerPullPaymentIncomingId: rest[0], + peerPullDebitId: rest[0], }; case TransactionType.PeerPushCredit: return { tag: TransactionType.PeerPushCredit, - peerPushPaymentIncomingId: rest[0], + peerPushCreditId: rest[0], }; case TransactionType.PeerPushDebit: return { tag: TransactionType.PeerPushDebit, pursePub: rest[0] }; @@ -1455,7 +1455,7 @@ export async function retryTransaction( case TransactionType.PeerPullDebit: { const taskId = constructTaskIdentifier({ tag: PendingTaskType.PeerPullDebit, - peerPullPaymentIncomingId: parsedTx.peerPullPaymentIncomingId, + peerPullDebitId: parsedTx.peerPullDebitId, }); await resetPendingTaskTimeout(ws, taskId); stopLongpolling(ws, taskId); @@ -1464,7 +1464,7 @@ export async function retryTransaction( case TransactionType.PeerPushCredit: { const taskId = constructTaskIdentifier({ tag: PendingTaskType.PeerPushCredit, - peerPushPaymentIncomingId: parsedTx.peerPushPaymentIncomingId, + peerPushCreditId: parsedTx.peerPushCreditId, }); await resetPendingTaskTimeout(ws, taskId); stopLongpolling(ws, taskId); @@ -1522,10 +1522,10 @@ export async function suspendTransaction( await suspendPeerPushDebitTransaction(ws, tx.pursePub); break; case TransactionType.PeerPullDebit: - await suspendPeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId); + await suspendPeerPullDebitTransaction(ws, tx.peerPullDebitId); break; case TransactionType.PeerPushCredit: - await suspendPeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId); + await suspendPeerPushCreditTransaction(ws, tx.peerPushCreditId); break; case TransactionType.Refund: throw Error("refund transactions can't be suspended or resumed"); @@ -1568,10 +1568,10 @@ export async function failTransaction( await failPeerPullCreditTransaction(ws, tx.pursePub); return; case TransactionType.PeerPullDebit: - await failPeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId); + await failPeerPullDebitTransaction(ws, tx.peerPullDebitId); return; case TransactionType.PeerPushCredit: - await failPeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId); + await failPeerPushCreditTransaction(ws, tx.peerPushCreditId); return; case TransactionType.PeerPushDebit: await failPeerPushDebitTransaction(ws, tx.pursePub); @@ -1613,10 +1613,10 @@ export async function resumeTransaction( await resumePeerPushDebitTransaction(ws, tx.pursePub); break; case TransactionType.PeerPullDebit: - await resumePeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId); + await resumePeerPullDebitTransaction(ws, tx.peerPullDebitId); break; case TransactionType.PeerPushCredit: - await resumePeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId); + await resumePeerPushCreditTransaction(ws, tx.peerPushCreditId); break; case TransactionType.Refund: throw Error("refund transactions can't be suspended or resumed"); @@ -1641,16 +1641,16 @@ export async function deleteTransaction( switch (parsedTx.tag) { case TransactionType.PeerPushCredit: { - const peerPushPaymentIncomingId = parsedTx.peerPushPaymentIncomingId; + const peerPushCreditId = parsedTx.peerPushCreditId; await ws.db .mktx((x) => [ x.withdrawalGroups, - x.peerPushPaymentIncoming, + x.peerPushCredit, x.tombstones, ]) .runReadWrite(async (tx) => { - const pushInc = await tx.peerPushPaymentIncoming.get( - peerPushPaymentIncomingId, + const pushInc = await tx.peerPushCredit.get( + peerPushCreditId, ); if (!pushInc) { return; @@ -1668,12 +1668,12 @@ export async function deleteTransaction( }); } } - await tx.peerPushPaymentIncoming.delete(peerPushPaymentIncomingId); + await tx.peerPushCredit.delete(peerPushCreditId); await tx.tombstones.put({ id: TombstoneTag.DeletePeerPushCredit + ":" + - peerPushPaymentIncomingId, + peerPushCreditId, }); }); return; @@ -1684,11 +1684,11 @@ export async function deleteTransaction( await ws.db .mktx((x) => [ x.withdrawalGroups, - x.peerPullPaymentInitiations, + x.peerPullCredit, x.tombstones, ]) .runReadWrite(async (tx) => { - const pullIni = await tx.peerPullPaymentInitiations.get(pursePub); + const pullIni = await tx.peerPullCredit.get(pursePub); if (!pullIni) { return; } @@ -1705,7 +1705,7 @@ export async function deleteTransaction( }); } } - await tx.peerPullPaymentInitiations.delete(pursePub); + await tx.peerPullCredit.delete(pursePub); await tx.tombstones.put({ id: TombstoneTag.DeletePeerPullCredit + ":" + pursePub, }); @@ -1795,7 +1795,7 @@ export async function deleteTransaction( case TransactionType.Refund: { const refundGroupId = parsedTx.refundGroupId; await ws.db - .mktx((x) => [x.refundGroups, x.tombstones, x.refundItems]) + .mktx((x) => [x.refundGroups, x.tombstones]) .runReadWrite(async (tx) => { const refundRecord = await tx.refundGroups.get(refundGroupId); if (!refundRecord) { @@ -1809,15 +1809,15 @@ export async function deleteTransaction( } case TransactionType.PeerPullDebit: { - const peerPullPaymentIncomingId = parsedTx.peerPullPaymentIncomingId; + const peerPullDebitId = parsedTx.peerPullDebitId; await ws.db - .mktx((x) => [x.peerPullPaymentIncoming, x.tombstones]) + .mktx((x) => [x.peerPullDebit, x.tombstones]) .runReadWrite(async (tx) => { - const debit = await tx.peerPullPaymentIncoming.get( - peerPullPaymentIncomingId, + const debit = await tx.peerPullDebit.get( + peerPullDebitId, ); if (debit) { - await tx.peerPullPaymentIncoming.delete(peerPullPaymentIncomingId); + await tx.peerPullDebit.delete(peerPullDebitId); await tx.tombstones.put({ id: transactionId }); } }); @@ -1828,11 +1828,11 @@ export async function deleteTransaction( case TransactionType.PeerPushDebit: { const pursePub = parsedTx.pursePub; await ws.db - .mktx((x) => [x.peerPushPaymentInitiations, x.tombstones]) + .mktx((x) => [x.peerPushDebit, x.tombstones]) .runReadWrite(async (tx) => { - const debit = await tx.peerPushPaymentInitiations.get(pursePub); + const debit = await tx.peerPushDebit.get(pursePub); if (debit) { - await tx.peerPushPaymentInitiations.delete(pursePub); + await tx.peerPushDebit.delete(pursePub); await tx.tombstones.put({ id: transactionId }); } }); @@ -1875,10 +1875,10 @@ export async function abortTransaction( await abortPeerPullCreditTransaction(ws, txId.pursePub); break; case TransactionType.PeerPullDebit: - await abortPeerPullDebitTransaction(ws, txId.peerPullPaymentIncomingId); + await abortPeerPullDebitTransaction(ws, txId.peerPullDebitId); break; case TransactionType.PeerPushCredit: - await abortPeerPushCreditTransaction(ws, txId.peerPushPaymentIncomingId); + await abortPeerPushCreditTransaction(ws, txId.peerPushCreditId); break; case TransactionType.PeerPushDebit: await abortPeerPushDebitTransaction(ws, txId.pursePub); -- cgit v1.2.3 From 132ece8e53d2c7d69c943a2898ed07411c63f12f Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Fri, 8 Sep 2023 11:45:31 +0200 Subject: wallet-core: store contract terms in separate object store only --- packages/taler-wallet-core/src/db.ts | 75 +++++++++++----------- .../src/operations/pay-peer-pull-credit.ts | 33 ++++++---- .../src/operations/pay-peer-push-credit.ts | 1 - .../src/operations/pay-peer-push-debit.ts | 33 ++++++---- .../src/operations/transactions.ts | 64 ++++++++---------- 5 files changed, 106 insertions(+), 100 deletions(-) (limited to 'packages/taler-wallet-core/src/operations/transactions.ts') diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 9aedb888b..679ca2842 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -101,6 +101,14 @@ import { RetryInfo, TaskIdentifiers } from "./operations/common.js"; * @author Florian Dold */ +/** + FIXMEs: + - Contract terms can be quite large. We currently tend to read the + full contract terms from the DB quite often. + Instead, we should probably extract what we need into a separate object + store. + */ + /** * Name of the Taler database. This is effectively the major * version of the DB schema. Whenever it changes, custom import logic @@ -166,47 +174,47 @@ export enum WithdrawalGroupStatus { /** * Reserve must be registered with the bank. */ - PendingRegisteringBank = 0x0100_0000, - SuspendedRegisteringBank = 0x0110_0000, + PendingRegisteringBank = 0x0100_0001, + SuspendedRegisteringBank = 0x0110_0001, /** * We've registered reserve's information with the bank * and are now waiting for the user to confirm the withdraw * with the bank (typically 2nd factor auth). */ - PendingWaitConfirmBank = 0x0100_0001, - SuspendedWaitConfirmBank = 0x0110_0001, + PendingWaitConfirmBank = 0x0100_0002, + SuspendedWaitConfirmBank = 0x0110_0002, /** * Querying reserve status with the exchange. */ - PendingQueryingStatus = 0x0100_0002, - SuspendedQueryingStatus = 0x0110_0002, + PendingQueryingStatus = 0x0100_0003, + SuspendedQueryingStatus = 0x0110_0003, /** * Ready for withdrawal. */ - PendingReady = 0x0100_0003, - SuspendedReady = 0x0110_0003, + PendingReady = 0x0100_0004, + SuspendedReady = 0x0110_0004, /** * We are telling the bank that we don't want to complete * the withdrawal! */ - AbortingBank = 0x0103_0000, - SuspendedAbortingBank = 0x0113_0000, + AbortingBank = 0x0103_0001, + SuspendedAbortingBank = 0x0113_0001, /** * Exchange wants KYC info from the user. */ - PendingKyc = 0x0100_0004, - SuspendedKyc = 0x0110_004, + PendingKyc = 0x0100_0005, + SuspendedKyc = 0x0110_005, /** * Exchange is doing AML checks. */ - PendingAml = 0x0100_0005, - SuspendedAml = 0x0100_0005, + PendingAml = 0x0100_0006, + SuspendedAml = 0x0100_0006, /** * The corresponding withdraw record has been created. @@ -218,9 +226,9 @@ export enum WithdrawalGroupStatus { /** * The bank aborted the withdrawal. */ - FailedBankAborted = 0x0501_0000, + FailedBankAborted = 0x0501_0001, - FailedAbortingBank = 0x0501_0001, + FailedAbortingBank = 0x0501_0002, /** * Aborted in a state where we were supposed to @@ -272,9 +280,9 @@ export interface ReserveBankInfo { */ export enum DenominationVerificationStatus { /** - * Verification was delayed. + * Verification was delayed (pending). */ - Unverified = 0x0500_0000, + Unverified = 0x0100_0000, /** * Verified as valid. @@ -532,6 +540,7 @@ export enum ExchangeEntryDbRecordStatus { Used = 3, } +// FIXME: Use status ranges for this as well? export enum ExchangeEntryDbUpdateStatus { Initial = 1, InitialUpdate = 2, @@ -612,9 +621,9 @@ export interface ExchangeEntryRecord { } export enum PlanchetStatus { - Pending = 10 /* ACTIVE_START */, - KycRequired = 11 /* ACTIVE_START + 1 */, - WithdrawalDone = 50 /* DORMANT_START */, + Pending = 0x0100_0000, + KycRequired = 0x0100_0001, + WithdrawalDone = 0x0500_000, } /** @@ -1358,7 +1367,8 @@ export interface WgInfoBankManual { export interface WgInfoBankPeerPull { withdrawalType: WithdrawalRecordType.PeerPullCredit; - contractTerms: any; + // FIXME: include a transaction ID here? + /** * Needed to quickly construct the taler:// URI for the counterparty * without a join. @@ -1369,7 +1379,7 @@ export interface WgInfoBankPeerPull { export interface WgInfoBankPeerPush { withdrawalType: WithdrawalRecordType.PeerPushCredit; - contractTerms: any; + // FIXME: include a transaction ID here? } export interface WgInfoBankRecoup { @@ -1829,11 +1839,6 @@ export interface PeerPushDebitRecord { */ contractEncNonce: string; - /** - * FIXME: Put those in a different object store! - */ - contractTerms: PeerContractTerms; - purseExpiration: TalerProtocolTimestamp; timestampCreated: TalerPreciseTimestamp; @@ -1861,7 +1866,7 @@ export enum PeerPullPaymentCreditStatus { SuspendedCreatePurse = 0x0110_0000, SuspendedReady = 0x0110_0001, SuspendedMergeKycRequired = 0x0110_0002, - SuspendedWithdrawing = 0x0113_0000, + SuspendedWithdrawing = 0x0110_0000, SuspendedAbortingDeletePurse = 0x0113_0000, @@ -1878,6 +1883,7 @@ export interface PeerPullCreditRecord { /** * Amount requested. + * FIXME: What type of instructed amount is i? */ amount: AmountString; @@ -1908,11 +1914,6 @@ export interface PeerPullCreditRecord { contractEncNonce: string; - /** - * FIXME: Put in separate object store! - */ - contractTerms: PeerContractTerms; - mergeTimestamp: TalerPreciseTimestamp; mergeReserveRowId: number; @@ -2529,8 +2530,7 @@ export const WalletStoresV1 = { byWithdrawalGroupId: describeIndex( "byWithdrawalGroupId", "withdrawalGroupId", - { - }, + {}, ), }, ), @@ -2571,8 +2571,7 @@ export const WalletStoresV1 = { }), { byProposalId: describeIndex("byProposalId", "proposalId"), - byStatus: describeIndex("byStatus", "status", { - }), + byStatus: describeIndex("byStatus", "status", {}), }, ), refundItems: describeStore( diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts index edadad1fc..0355eb152 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts @@ -27,6 +27,7 @@ import { InitiatePeerPullCreditResponse, Logger, NotificationType, + PeerContractTerms, TalerErrorCode, TalerPreciseTimestamp, TalerProtocolTimestamp, @@ -143,7 +144,6 @@ async function queryPurseForPeerPullCredit( amount: Amounts.parseOrThrow(pullIni.amount), wgInfo: { withdrawalType: WithdrawalRecordType.PeerPullCredit, - contractTerms: pullIni.contractTerms, contractPriv: pullIni.contractPriv, }, forcedWithdrawalGroupId: pullIni.withdrawalGroupId, @@ -283,9 +283,7 @@ async function processPeerPullCreditAbortingDeletePurse( if (!ppiRec) { return undefined; } - if ( - ppiRec.status !== PeerPullPaymentCreditStatus.AbortingDeletePurse - ) { + if (ppiRec.status !== PeerPullPaymentCreditStatus.AbortingDeletePurse) { return undefined; } const oldTxState = computePeerPullCreditTransactionState(ppiRec); @@ -371,6 +369,18 @@ async function handlePeerPullCreditCreatePurse( throw Error("merge reserve for peer pull payment not found in database"); } + const contractTermsRecord = await ws.db + .mktx((x) => [x.contractTerms]) + .runReadOnly(async (tx) => { + return tx.contractTerms.get(pullIni.contractTermsHash); + }); + + if (!contractTermsRecord) { + throw Error("contract terms for peer pull payment not found in database"); + } + + const contractTerms: PeerContractTerms = contractTermsRecord.contractTermsRaw; + const reservePayto = talerPaytoFromExchangeReserve( pullIni.exchangeBaseUrl, mergeReserve.reservePub, @@ -379,19 +389,19 @@ async function handlePeerPullCreditCreatePurse( const econtractResp = await ws.cryptoApi.encryptContractForDeposit({ contractPriv: pullIni.contractPriv, contractPub: pullIni.contractPub, - contractTerms: pullIni.contractTerms, + contractTerms: contractTermsRecord, pursePriv: pullIni.pursePriv, pursePub: pullIni.pursePub, nonce: pullIni.contractEncNonce, }); - const purseExpiration = pullIni.contractTerms.purse_expiration; + const purseExpiration = contractTerms.purse_expiration; const sigRes = await ws.cryptoApi.signReservePurseCreate({ contractTermsHash: pullIni.contractTermsHash, flags: WalletAccountMergeFlags.CreateWithPurseFee, mergePriv: pullIni.mergePriv, mergeTimestamp: TalerPreciseTimestamp.round(pullIni.mergeTimestamp), - purseAmount: pullIni.contractTerms.amount, + purseAmount: pullIni.amount, purseExpiration: purseExpiration, purseFee: purseFee, pursePriv: pullIni.pursePriv, @@ -410,7 +420,7 @@ async function handlePeerPullCreditCreatePurse( purse_fee: purseFee, purse_pub: pullIni.pursePub, purse_sig: sigRes.purseSig, - purse_value: pullIni.contractTerms.amount, + purse_value: pullIni.amount, reserve_sig: sigRes.accountSig, econtract: econtractResp.econtract, }; @@ -585,8 +595,7 @@ async function processPeerPullCreditKycRequired( requirementRow: kycPending.requirement_row, }; peerInc.kycUrl = kycStatus.kyc_url; - peerInc.status = - PeerPullPaymentCreditStatus.PendingMergeKycRequired; + peerInc.status = PeerPullPaymentCreditStatus.PendingMergeKycRequired; const newTxState = computePeerPullCreditTransactionState(peerInc); await tx.peerPullCredit.put(peerInc); // We'll remove this eventually! New clients should rely on the @@ -769,7 +778,6 @@ export async function initiatePeerPullPayment( mergePriv: mergePair.priv, mergePub: mergePair.pub, status: PeerPullPaymentCreditStatus.PendingCreatePurse, - contractTerms: contractTerms, mergeTimestamp, contractEncNonce, mergeReserveRowId: mergeReserveRowId, @@ -848,8 +856,7 @@ export async function suspendPeerPullCreditTransaction( newStatus = PeerPullPaymentCreditStatus.SuspendedReady; break; case PeerPullPaymentCreditStatus.AbortingDeletePurse: - newStatus = - PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse; + newStatus = PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse; break; case PeerPullPaymentCreditStatus.Done: case PeerPullPaymentCreditStatus.SuspendedCreatePurse: diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts b/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts index f0f659aa3..89d9e3b49 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts @@ -446,7 +446,6 @@ async function handlePendingMerge( amount, wgInfo: { withdrawalType: WithdrawalRecordType.PeerPushCredit, - contractTerms, }, forcedWithdrawalGroupId: peerInc.withdrawalGroupId, exchangeBaseUrl: peerInc.exchangeBaseUrl, diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts index a2e7a6891..e80ffc059 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts @@ -154,9 +154,7 @@ async function handlePurseCreationConflict( await ws.db .mktx((x) => [x.peerPushDebit]) .runReadWrite(async (tx) => { - const myPpi = await tx.peerPushDebit.get( - peerPushInitiation.pursePub, - ); + const myPpi = await tx.peerPushDebit.get(peerPushInitiation.pursePub); if (!myPpi) { return; } @@ -182,10 +180,27 @@ async function processPeerPushDebitCreateReserve( ws: InternalWalletState, peerPushInitiation: PeerPushDebitRecord, ): Promise { - logger.info("processing peer-push-debit pending(create-reserve)"); const pursePub = peerPushInitiation.pursePub; const purseExpiration = peerPushInitiation.purseExpiration; const hContractTerms = peerPushInitiation.contractTermsHash; + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.PeerPushDebit, + pursePub: pursePub, + }); + + logger.trace(`processing ${transactionId} pending(create-reserve)`); + + const contractTermsRecord = await ws.db + .mktx((x) => [x.contractTerms]) + .runReadOnly(async (tx) => { + return tx.contractTerms.get(hContractTerms); + }); + + if (!contractTermsRecord) { + throw Error( + `db invariant failed, contract terms for ${transactionId} missing`, + ); + } const purseSigResp = await ws.cryptoApi.signPurseCreation({ hContractTerms, @@ -208,7 +223,7 @@ async function processPeerPushDebitCreateReserve( }); const encryptContractRequest: EncryptContractRequest = { - contractTerms: peerPushInitiation.contractTerms, + contractTerms: contractTermsRecord.contractTermsRaw, mergePriv: peerPushInitiation.mergePriv, pursePriv: peerPushInitiation.pursePriv, pursePub: peerPushInitiation.pursePub, @@ -329,9 +344,7 @@ async function processPeerPushDebitAbortingDeletePurse( if (!ppiRec) { return undefined; } - if ( - ppiRec.status !== PeerPushDebitStatus.AbortingDeletePurse - ) { + if (ppiRec.status !== PeerPushDebitStatus.AbortingDeletePurse) { return undefined; } const currency = Amounts.currencyOf(ppiRec.amount); @@ -658,7 +671,6 @@ export async function initiatePeerPushDebit( pursePub: pursePair.pub, timestampCreated: TalerPreciseTimestamp.now(), status: PeerPushDebitStatus.PendingCreatePurse, - contractTerms: contractTerms, contractEncNonce, coinSel: { coinPubs: sel.coins.map((x) => x.coinPub), @@ -881,8 +893,7 @@ export async function suspendPeerPushDebitTransaction( newStatus = PeerPushDebitStatus.SuspendedAbortingRefresh; break; case PeerPushDebitStatus.AbortingDeletePurse: - newStatus = - PeerPushDebitStatus.SuspendedAbortingDeletePurse; + newStatus = PeerPushDebitStatus.SuspendedAbortingDeletePurse; break; case PeerPushDebitStatus.PendingReady: newStatus = PeerPushDebitStatus.SuspendedReady; diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 5c57195c1..ff9fbf57a 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -154,7 +154,18 @@ import { resumePeerPushDebitTransaction, abortPeerPushDebitTransaction, } from "./pay-peer-push-debit.js"; -import { iterRecordsForDeposit, iterRecordsForPeerPullDebit, iterRecordsForPeerPullInitiation, iterRecordsForPeerPushCredit, iterRecordsForPeerPushInitiation, iterRecordsForPurchase, iterRecordsForRefresh, iterRecordsForRefund, iterRecordsForReward, iterRecordsForWithdrawal } from "./pending.js"; +import { + iterRecordsForDeposit, + iterRecordsForPeerPullDebit, + iterRecordsForPeerPullInitiation, + iterRecordsForPeerPushCredit, + iterRecordsForPeerPushInitiation, + iterRecordsForPurchase, + iterRecordsForRefresh, + iterRecordsForRefund, + iterRecordsForReward, + iterRecordsForWithdrawal, +} from "./pending.js"; const logger = new Logger("taler-wallet-core:transactions.ts"); @@ -337,9 +348,7 @@ export async function getTransactionById( return await ws.db .mktx((x) => [x.peerPullDebit]) .runReadWrite(async (tx) => { - const debit = await tx.peerPullDebit.get( - parsedTx.peerPullDebitId, - ); + const debit = await tx.peerPullDebit.get(parsedTx.peerPullDebitId); if (!debit) throw Error("not found"); return buildTransactionForPullPaymentDebit(debit); }); @@ -349,9 +358,7 @@ export async function getTransactionById( return await ws.db .mktx((x) => [x.peerPushDebit, x.contractTerms]) .runReadWrite(async (tx) => { - const debit = await tx.peerPushDebit.get( - parsedTx.pursePub, - ); + const debit = await tx.peerPushDebit.get(parsedTx.pursePub); if (!debit) throw Error("not found"); const ct = await tx.contractTerms.get(debit.contractTermsHash); checkDbInvariant(!!ct); @@ -372,9 +379,7 @@ export async function getTransactionById( x.operationRetries, ]) .runReadWrite(async (tx) => { - const pushInc = await tx.peerPushCredit.get( - peerPushCreditId, - ); + const pushInc = await tx.peerPushCredit.get(peerPushCreditId); if (!pushInc) throw Error("not found"); const ct = await tx.contractTerms.get(pushInc.contractTermsHash); checkDbInvariant(!!ct); @@ -532,8 +537,8 @@ function buildTransactionForPeerPullCredit( // Old transactions don't have it! timestamp: pullCredit.mergeTimestamp ?? TalerPreciseTimestamp.now(), info: { - expiration: wsr.wgInfo.contractTerms.purse_expiration, - summary: wsr.wgInfo.contractTerms.summary, + expiration: peerContractTerms.purse_expiration, + summary: peerContractTerms.summary, }, talerUri: stringifyPayPullUri({ exchangeBaseUrl: wsr.exchangeBaseUrl, @@ -600,8 +605,8 @@ function buildTransactionForPeerPushCredit( amountRaw: Amounts.stringify(wsr.instructedAmount), exchangeBaseUrl: wsr.exchangeBaseUrl, info: { - expiration: wsr.wgInfo.contractTerms.purse_expiration, - summary: wsr.wgInfo.contractTerms.summary, + expiration: peerContractTerms.purse_expiration, + summary: peerContractTerms.summary, }, timestamp: wsr.timestampStart, transactionId: constructTransactionIdentifier({ @@ -1033,7 +1038,7 @@ export async function getTransactions( ), ); }); - + await iterRecordsForPeerPullInitiation(tx, filter, async (pi) => { const currency = Amounts.currencyOf(pi.amount); if (shouldSkipCurrency(transactionsRequest, currency)) { @@ -1078,7 +1083,7 @@ export async function getTransactions( ); transactions.push(buildTransactionForRefund(refundGroup, contractData)); }); - + await iterRecordsForRefresh(tx, filter, async (rg) => { if (shouldSkipCurrency(transactionsRequest, rg.currency)) { return; @@ -1099,7 +1104,7 @@ export async function getTransactions( } }); - await iterRecordsForWithdrawal(tx, filter ,async (wsr) => { + await iterRecordsForWithdrawal(tx, filter, async (wsr) => { if ( shouldSkipCurrency( transactionsRequest, @@ -1643,15 +1648,9 @@ export async function deleteTransaction( case TransactionType.PeerPushCredit: { const peerPushCreditId = parsedTx.peerPushCreditId; await ws.db - .mktx((x) => [ - x.withdrawalGroups, - x.peerPushCredit, - x.tombstones, - ]) + .mktx((x) => [x.withdrawalGroups, x.peerPushCredit, x.tombstones]) .runReadWrite(async (tx) => { - const pushInc = await tx.peerPushCredit.get( - peerPushCreditId, - ); + const pushInc = await tx.peerPushCredit.get(peerPushCreditId); if (!pushInc) { return; } @@ -1670,10 +1669,7 @@ export async function deleteTransaction( } await tx.peerPushCredit.delete(peerPushCreditId); await tx.tombstones.put({ - id: - TombstoneTag.DeletePeerPushCredit + - ":" + - peerPushCreditId, + id: TombstoneTag.DeletePeerPushCredit + ":" + peerPushCreditId, }); }); return; @@ -1682,11 +1678,7 @@ export async function deleteTransaction( case TransactionType.PeerPullCredit: { const pursePub = parsedTx.pursePub; await ws.db - .mktx((x) => [ - x.withdrawalGroups, - x.peerPullCredit, - x.tombstones, - ]) + .mktx((x) => [x.withdrawalGroups, x.peerPullCredit, x.tombstones]) .runReadWrite(async (tx) => { const pullIni = await tx.peerPullCredit.get(pursePub); if (!pullIni) { @@ -1813,9 +1805,7 @@ export async function deleteTransaction( await ws.db .mktx((x) => [x.peerPullDebit, x.tombstones]) .runReadWrite(async (tx) => { - const debit = await tx.peerPullDebit.get( - peerPullDebitId, - ); + const debit = await tx.peerPullDebit.get(peerPullDebitId); if (debit) { await tx.peerPullDebit.delete(peerPullDebitId); await tx.tombstones.put({ id: transactionId }); -- cgit v1.2.3 From 50b0b324ae67bea01079d6e9a1d684795f5b430f Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Fri, 8 Sep 2023 12:26:58 +0200 Subject: wallet-core: put refresh sessions into own store --- packages/taler-wallet-core/src/db.ts | 27 +++-- .../taler-wallet-core/src/operations/balance.ts | 9 +- .../taler-wallet-core/src/operations/refresh.ts | 122 +++++++++++++-------- .../src/operations/transactions.ts | 2 +- 4 files changed, 100 insertions(+), 60 deletions(-) (limited to 'packages/taler-wallet-core/src/operations/transactions.ts') diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 679ca2842..359569055 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -107,6 +107,8 @@ import { RetryInfo, TaskIdentifiers } from "./operations/common.js"; full contract terms from the DB quite often. Instead, we should probably extract what we need into a separate object store. + - More object stores should have an "id" primary key, + as this makes referencing less expensive. */ /** @@ -943,9 +945,6 @@ export interface RefreshReasonDetails { export interface RefreshGroupRecord { operationStatus: RefreshOperationStatus; - // FIXME: Put this into a different object store? - lastErrorPerCoin: { [coinIndex: number]: TalerErrorDetail }; - /** * Unique, randomly generated identifier for this group of * refresh operations. @@ -969,13 +968,9 @@ export interface RefreshGroupRecord { oldCoinPubs: string[]; - // FIXME: Should this go into a separate - // object store for faster updates? - refreshSessionPerCoin: (RefreshSessionRecord | undefined)[]; - inputPerCoin: AmountString[]; - estimatedOutputPerCoin: AmountString[]; + expectedOutputPerCoin: AmountString[]; /** * Flag for each coin whether refreshing finished. @@ -997,6 +992,13 @@ export interface RefreshGroupRecord { * Ongoing refresh */ export interface RefreshSessionRecord { + refreshGroupId: string; + + /** + * Index of the coin in the refresh group. + */ + coinIndex: number; + /** * 512-bit secret that can be used to derive * the other cryptographic material for the refresh session. @@ -1021,6 +1023,8 @@ export interface RefreshSessionRecord { * The no-reveal-index after we've done the melting. */ norevealIndex?: number; + + lastError?: TalerErrorDetail; } export enum RefundReason { @@ -2372,6 +2376,13 @@ export const WalletStoresV1 = { byStatus: describeIndex("byStatus", "operationStatus"), }, ), + refreshSessions: describeStore( + "refreshSessions", + describeContents({ + keyPath: ["refreshGroupId", "coinIndex"], + }), + {}, + ), recoupGroups: describeStore( "recoupGroups", describeContents({ diff --git a/packages/taler-wallet-core/src/operations/balance.ts b/packages/taler-wallet-core/src/operations/balance.ts index 287ac94fb..28aa5ac70 100644 --- a/packages/taler-wallet-core/src/operations/balance.ts +++ b/packages/taler-wallet-core/src/operations/balance.ts @@ -95,14 +95,7 @@ function computeRefreshGroupAvailableAmount(r: RefreshGroupRecord): AmountJson { return available; } for (let i = 0; i < r.oldCoinPubs.length; i++) { - const session = r.refreshSessionPerCoin[i]; - if (session) { - // We are always assuming the refresh will succeed, thus we - // report the output as available balance. - available = Amounts.add(available, session.amountRefreshOutput).amount; - } else { - available = Amounts.add(available, r.estimatedOutputPerCoin[i]).amount; - } + available = Amounts.add(available, r.expectedOutputPerCoin[i]).amount; } return available; } diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index fb356f0fc..3c4ef207a 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -76,7 +76,11 @@ import { RefreshReasonDetails, WalletStoresV1, } from "../db.js"; -import { isWithdrawableDenom, PendingTaskType } from "../index.js"; +import { + isWithdrawableDenom, + PendingTaskType, + RefreshSessionRecord, +} from "../index.js"; import { EXCHANGE_COINS_LOCK, InternalWalletState, @@ -170,18 +174,23 @@ function updateGroupStatus(rg: RefreshGroupRecord): { final: boolean } { /** * Create a refresh session for one particular coin inside a refresh group. + * + * If the session already exists, return the existing one. + * + * If the session doesn't need to be created (refresh group gone or session already + * finished), return undefined. */ -async function refreshCreateSession( +async function provideRefreshSession( ws: InternalWalletState, refreshGroupId: string, coinIndex: number, -): Promise { +): Promise { logger.trace( `creating refresh session for coin ${coinIndex} in refresh group ${refreshGroupId}`, ); const d = await ws.db - .mktx((x) => [x.refreshGroups, x.coins]) + .mktx((x) => [x.refreshGroups, x.coins, x.refreshSessions]) .runReadWrite(async (tx) => { const refreshGroup = await tx.refreshGroups.get(refreshGroupId); if (!refreshGroup) { @@ -192,21 +201,24 @@ async function refreshCreateSession( ) { return; } - const existingRefreshSession = - refreshGroup.refreshSessionPerCoin[coinIndex]; - if (existingRefreshSession) { - return; - } + const existingRefreshSession = await tx.refreshSessions.get([ + refreshGroupId, + coinIndex, + ]); const oldCoinPub = refreshGroup.oldCoinPubs[coinIndex]; const coin = await tx.coins.get(oldCoinPub); if (!coin) { throw Error("Can't refresh, coin not found"); } - return { refreshGroup, coin }; + return { refreshGroup, coin, existingRefreshSession }; }); if (!d) { - return; + return undefined; + } + + if (d.existingRefreshSession) { + return d.existingRefreshSession; } const { refreshGroup, coin } = d; @@ -288,17 +300,23 @@ async function refreshCreateSession( const sessionSecretSeed = encodeCrock(getRandomBytes(64)); // Store refresh session for this coin in the database. - await ws.db - .mktx((x) => [x.refreshGroups, x.coins]) + const newSession = await ws.db + .mktx((x) => [x.refreshGroups, x.coins, x.refreshSessions]) .runReadWrite(async (tx) => { const rg = await tx.refreshGroups.get(refreshGroupId); if (!rg) { return; } - if (rg.refreshSessionPerCoin[coinIndex]) { + const existingSession = await tx.refreshSessions.get([ + refreshGroupId, + coinIndex, + ]); + if (existingSession) { return; } - rg.refreshSessionPerCoin[coinIndex] = { + const newSession: RefreshSessionRecord = { + coinIndex, + refreshGroupId, norevealIndex: undefined, sessionSecretSeed: sessionSecretSeed, newDenoms: newCoinDenoms.selectedDenoms.map((x) => ({ @@ -307,11 +325,13 @@ async function refreshCreateSession( })), amountRefreshOutput: Amounts.stringify(newCoinDenoms.totalCoinValue), }; - await tx.refreshGroups.put(rg); + await tx.refreshSessions.put(newSession); + return newSession; }); logger.trace( `created refresh session for coin #${coinIndex} in ${refreshGroupId}`, ); + return newSession; } function getRefreshRequestTimeout(rg: RefreshGroupRecord): Duration { @@ -326,13 +346,16 @@ async function refreshMelt( coinIndex: number, ): Promise { const d = await ws.db - .mktx((x) => [x.refreshGroups, x.coins, x.denominations]) + .mktx((x) => [x.refreshGroups, x.refreshSessions, x.coins, x.denominations]) .runReadWrite(async (tx) => { const refreshGroup = await tx.refreshGroups.get(refreshGroupId); if (!refreshGroup) { return; } - const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex]; + const refreshSession = await tx.refreshSessions.get([ + refreshGroupId, + coinIndex, + ]); if (!refreshSession) { return; } @@ -442,7 +465,12 @@ async function refreshMelt( if (resp.status === HttpStatusCode.NotFound) { const errDetails = await readUnexpectedResponseDetails(resp); const transitionInfo = await ws.db - .mktx((x) => [x.refreshGroups, x.coins, x.coinAvailability]) + .mktx((x) => [ + x.refreshGroups, + x.refreshSessions, + x.coins, + x.coinAvailability, + ]) .runReadWrite(async (tx) => { const rg = await tx.refreshGroups.get(refreshGroupId); if (!rg) { @@ -456,12 +484,22 @@ async function refreshMelt( } const oldTxState = computeRefreshTransactionState(rg); rg.statusPerCoin[coinIndex] = RefreshCoinStatus.Failed; - rg.lastErrorPerCoin[coinIndex] = errDetails; + const refreshSession = await tx.refreshSessions.get([ + refreshGroupId, + coinIndex, + ]); + if (!refreshSession) { + throw Error( + "db invariant failed: missing refresh session in database", + ); + } + refreshSession.lastError = errDetails; const updateRes = updateGroupStatus(rg); if (updateRes.final) { await makeCoinsVisible(ws, tx, transactionId); } await tx.refreshGroups.put(rg); + await tx.refreshSessions.put(refreshSession); const newTxState = computeRefreshTransactionState(rg); return { oldTxState, @@ -493,7 +531,7 @@ async function refreshMelt( refreshSession.norevealIndex = norevealIndex; await ws.db - .mktx((x) => [x.refreshGroups]) + .mktx((x) => [x.refreshGroups, x.refreshSessions]) .runReadWrite(async (tx) => { const rg = await tx.refreshGroups.get(refreshGroupId); if (!rg) { @@ -502,7 +540,7 @@ async function refreshMelt( if (rg.timestampFinished) { return; } - const rs = rg.refreshSessionPerCoin[coinIndex]; + const rs = await tx.refreshSessions.get([refreshGroupId, coinIndex]); if (!rs) { return; } @@ -510,7 +548,7 @@ async function refreshMelt( return; } rs.norevealIndex = norevealIndex; - await tx.refreshGroups.put(rg); + await tx.refreshSessions.put(rs); }); } @@ -581,13 +619,16 @@ async function refreshReveal( `doing refresh reveal for ${refreshGroupId} (old coin ${coinIndex})`, ); const d = await ws.db - .mktx((x) => [x.refreshGroups, x.coins, x.denominations]) + .mktx((x) => [x.refreshGroups, x.refreshSessions, x.coins, x.denominations]) .runReadOnly(async (tx) => { const refreshGroup = await tx.refreshGroups.get(refreshGroupId); if (!refreshGroup) { return; } - const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex]; + const refreshSession = await tx.refreshSessions.get([ + refreshGroupId, + coinIndex, + ]); if (!refreshSession) { return; } @@ -755,6 +796,7 @@ async function refreshReveal( x.denominations, x.coinAvailability, x.refreshGroups, + x.refreshSessions, ]) .runReadWrite(async (tx) => { const rg = await tx.refreshGroups.get(refreshGroupId); @@ -762,7 +804,7 @@ async function refreshReveal( logger.warn("no refresh session found"); return; } - const rs = rg.refreshSessionPerCoin[coinIndex]; + const rs = await tx.refreshSessions.get([refreshGroupId, coinIndex]); if (!rs) { return; } @@ -858,10 +900,15 @@ async function processRefreshSession( logger.trace( `processing refresh session for coin ${coinIndex} of group ${refreshGroupId}`, ); - let refreshGroup = await ws.db - .mktx((x) => [x.refreshGroups]) + let { refreshGroup, refreshSession } = await ws.db + .mktx((x) => [x.refreshGroups, x.refreshSessions]) .runReadOnly(async (tx) => { - return tx.refreshGroups.get(refreshGroupId); + const rg = await tx.refreshGroups.get(refreshGroupId); + const rs = await tx.refreshSessions.get([refreshGroupId, coinIndex]); + return { + refreshGroup: rg, + refreshSession: rs, + }; }); if (!refreshGroup) { return; @@ -869,18 +916,9 @@ async function processRefreshSession( if (refreshGroup.statusPerCoin[coinIndex] === RefreshCoinStatus.Finished) { return; } - if (!refreshGroup.refreshSessionPerCoin[coinIndex]) { - await refreshCreateSession(ws, refreshGroupId, coinIndex); - refreshGroup = await ws.db - .mktx((x) => [x.refreshGroups]) - .runReadOnly(async (tx) => { - return tx.refreshGroups.get(refreshGroupId); - }); - if (!refreshGroup) { - return; - } + if (!refreshSession) { + refreshSession = await provideRefreshSession(ws, refreshGroupId, coinIndex); } - const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex]; if (!refreshSession) { if (refreshGroup.statusPerCoin[coinIndex] !== RefreshCoinStatus.Finished) { throw Error( @@ -1058,13 +1096,11 @@ export async function createRefreshGroup( timestampFinished: undefined, statusPerCoin: oldCoinPubs.map(() => RefreshCoinStatus.Pending), oldCoinPubs: oldCoinPubs.map((x) => x.coinPub), - lastErrorPerCoin: {}, reasonDetails, reason, refreshGroupId, - refreshSessionPerCoin: oldCoinPubs.map(() => undefined), inputPerCoin: oldCoinPubs.map((x) => x.amount), - estimatedOutputPerCoin: estimatedOutputPerCoin.map((x) => + expectedOutputPerCoin: estimatedOutputPerCoin.map((x) => Amounts.stringify(x), ), timestampCreated: TalerPreciseTimestamp.now(), diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index ff9fbf57a..8db68e0f1 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -760,7 +760,7 @@ function buildTransactionForRefresh( ).amount; const outputAmount = Amounts.sumOrZero( refreshGroupRecord.currency, - refreshGroupRecord.estimatedOutputPerCoin, + refreshGroupRecord.expectedOutputPerCoin, ).amount; return { type: TransactionType.Refresh, -- cgit v1.2.3 From 2ae952cdfa8f38a650be8e4438c21bace2f24c19 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Fri, 8 Sep 2023 12:54:31 +0200 Subject: wallet-core: remove redundant deposit status field in DB --- packages/taler-wallet-core/src/db.ts | 17 ++++----- .../taler-wallet-core/src/operations/deposits.ts | 41 ++++++++++++---------- .../taler-wallet-core/src/operations/pending.ts | 21 ++++------- .../src/operations/transactions.ts | 10 +++--- 4 files changed, 41 insertions(+), 48 deletions(-) (limited to 'packages/taler-wallet-core/src/operations/transactions.ts') diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 359569055..3db7ae9b5 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -921,8 +921,11 @@ export enum RefreshOperationStatus { * Status of a single element of a deposit group. */ export enum DepositElementStatus { - Unknown = 0x0100_0000, - Accepted = 0x0100_0001, + DepositPending = 0x0100_0000, + /** + * Accepted, but tracking. + */ + Tracking = 0x0100_0001, KycRequired = 0x0100_0002, Wired = 0x0500_0000, RefundSuccess = 0x0503_0000, @@ -1723,10 +1726,8 @@ export interface DepositGroupRecord { /** * The counterparty effective deposit amount. - * - * FIXME: If possible, rename to counterpartyEffectiveDepositAmount. */ - effectiveDepositAmount: AmountString; + counterpartyEffectiveDepositAmount: AmountString; timestampCreated: TalerPreciseTimestamp; @@ -1734,11 +1735,7 @@ export interface DepositGroupRecord { operationStatus: DepositOperationStatus; - // FIXME: Duplication between this and transactionPerCoin! - depositedPerCoin: boolean[]; - - // FIXME: Improve name! - transactionPerCoin: DepositElementStatus[]; + statusPerCoin: DepositElementStatus[]; /** * When the deposit transaction was aborted and diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts index a8ec859cf..3d78e938b 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -510,10 +510,10 @@ async function refundDepositGroup( ws: InternalWalletState, depositGroup: DepositGroupRecord, ): Promise { - const newTxPerCoin = [...depositGroup.transactionPerCoin]; - logger.info(`status per coin: ${j2s(depositGroup.transactionPerCoin)}`); - for (let i = 0; i < depositGroup.transactionPerCoin.length; i++) { - const st = depositGroup.transactionPerCoin[i]; + const newTxPerCoin = [...depositGroup.statusPerCoin]; + logger.info(`status per coin: ${j2s(depositGroup.statusPerCoin)}`); + for (let i = 0; i < depositGroup.statusPerCoin.length; i++) { + const st = depositGroup.statusPerCoin[i]; switch (st) { case DepositElementStatus.RefundFailed: case DepositElementStatus.RefundSuccess: @@ -593,7 +593,7 @@ async function refundDepositGroup( if (!newDg) { return; } - newDg.transactionPerCoin = newTxPerCoin; + newDg.statusPerCoin = newTxPerCoin; const refreshCoins: CoinRefreshRequest[] = []; for (let i = 0; i < newTxPerCoin.length; i++) { refreshCoins.push({ @@ -766,7 +766,7 @@ async function processDepositGroupPendingTrack( cancellationToken?: CancellationToken, ): Promise { const { depositGroupId } = depositGroup; - for (let i = 0; i < depositGroup.depositedPerCoin.length; i++) { + for (let i = 0; i < depositGroup.statusPerCoin.length; i++) { const coinPub = depositGroup.payCoinSelection.coinPubs[i]; // FIXME: Make the URL part of the coin selection? const exchangeBaseUrl = await ws.db @@ -785,7 +785,7 @@ async function processDepositGroupPendingTrack( } | undefined; - if (depositGroup.transactionPerCoin[i] !== DepositElementStatus.Wired) { + if (depositGroup.statusPerCoin[i] !== DepositElementStatus.Wired) { const track = await trackDeposit( ws, depositGroup, @@ -810,7 +810,7 @@ async function processDepositGroupPendingTrack( exchangeBaseUrl, ); } else { - updatedTxStatus = DepositElementStatus.Accepted; + updatedTxStatus = DepositElementStatus.Tracking; } } else if (track.type === "wired") { updatedTxStatus = DepositElementStatus.Wired; @@ -840,7 +840,7 @@ async function processDepositGroupPendingTrack( id: track.exchange_sig, }; } else { - updatedTxStatus = DepositElementStatus.Unknown; + updatedTxStatus = DepositElementStatus.DepositPending; } } @@ -853,7 +853,7 @@ async function processDepositGroupPendingTrack( return; } if (updatedTxStatus !== undefined) { - dg.transactionPerCoin[i] = updatedTxStatus; + dg.statusPerCoin[i] = updatedTxStatus; } if (newWiredCoin) { /** @@ -885,8 +885,8 @@ async function processDepositGroupPendingTrack( return undefined; } const oldTxState = computeDepositTransactionStatus(dg); - for (let i = 0; i < depositGroup.depositedPerCoin.length; i++) { - if (depositGroup.transactionPerCoin[i] !== DepositElementStatus.Wired) { + for (let i = 0; i < depositGroup.statusPerCoin.length; i++) { + if (depositGroup.statusPerCoin[i] !== DepositElementStatus.Wired) { allWired = false; break; } @@ -943,7 +943,7 @@ async function processDepositGroupPendingDeposit( for (let i = 0; i < depositPermissions.length; i++) { const perm = depositPermissions[i]; - if (depositGroup.depositedPerCoin[i]) { + if (depositGroup.statusPerCoin[i] !== DepositElementStatus.DepositPending) { continue; } @@ -980,8 +980,12 @@ async function processDepositGroupPendingDeposit( if (!dg) { return; } - dg.depositedPerCoin[i] = true; - await tx.depositGroups.put(dg); + const coinStatus = dg.statusPerCoin[i]; + switch (coinStatus) { + case DepositElementStatus.DepositPending: + dg.statusPerCoin[i] = DepositElementStatus.Tracking; + await tx.depositGroups.put(dg); + } }); } @@ -1373,16 +1377,15 @@ export async function createDepositGroup( noncePub: noncePair.pub, timestampCreated: AbsoluteTime.toPreciseTimestamp(now), timestampFinished: undefined, - transactionPerCoin: payCoinSel.coinSel.coinPubs.map( - () => DepositElementStatus.Unknown, + statusPerCoin: payCoinSel.coinSel.coinPubs.map( + () => DepositElementStatus.DepositPending, ), payCoinSelection: payCoinSel.coinSel, payCoinSelectionUid: encodeCrock(getRandomBytes(32)), - depositedPerCoin: payCoinSel.coinSel.coinPubs.map(() => false), merchantPriv: merchantPair.priv, merchantPub: merchantPair.pub, totalPayCost: Amounts.stringify(totalDepositCost), - effectiveDepositAmount: Amounts.stringify( + counterpartyEffectiveDepositAmount: Amounts.stringify( counterpartyEffectiveDepositAmount, ), wire: { diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts index 207e6ffda..6115f848b 100644 --- a/packages/taler-wallet-core/src/operations/pending.ts +++ b/packages/taler-wallet-core/src/operations/pending.ts @@ -46,6 +46,7 @@ import { RefundGroupStatus, ExchangeEntryDbUpdateStatus, RefreshOperationStatus, + DepositElementStatus, } from "../db.js"; import { PendingOperationsResponse, @@ -277,8 +278,8 @@ async function gatherDepositPending( ): Promise { await iterRecordsForDeposit(tx, { onlyState: "nonfinal" }, async (dg) => { let deposited = true; - for (const d of dg.depositedPerCoin) { - if (!d) { + for (const d of dg.statusPerCoin) { + if (d === DepositElementStatus.DepositPending) { deposited = false; } } @@ -480,9 +481,7 @@ export async function iterRecordsForPeerPullInitiation( PeerPullPaymentCreditStatus.PendingCreatePurse, PeerPullPaymentCreditStatus.AbortingDeletePurse, ); - await tx.peerPullCredit.indexes.byStatus - .iter(keyRange) - .forEachAsync(f); + await tx.peerPullCredit.indexes.byStatus.iter(keyRange).forEachAsync(f); } else { await tx.peerPullCredit.indexes.byStatus.iter().forEachAsync(f); } @@ -528,9 +527,7 @@ export async function iterRecordsForPeerPullDebit( PeerPullDebitRecordStatus.PendingDeposit, PeerPullDebitRecordStatus.AbortingRefresh, ); - await tx.peerPullDebit.indexes.byStatus - .iter(keyRange) - .forEachAsync(f); + await tx.peerPullDebit.indexes.byStatus.iter(keyRange).forEachAsync(f); } else { await tx.peerPullDebit.indexes.byStatus.iter().forEachAsync(f); } @@ -576,9 +573,7 @@ export async function iterRecordsForPeerPushInitiation( PeerPushDebitStatus.PendingCreatePurse, PeerPushDebitStatus.AbortingRefresh, ); - await tx.peerPushDebit.indexes.byStatus - .iter(keyRange) - .forEachAsync(f); + await tx.peerPushDebit.indexes.byStatus.iter(keyRange).forEachAsync(f); } else { await tx.peerPushDebit.indexes.byStatus.iter().forEachAsync(f); } @@ -624,9 +619,7 @@ export async function iterRecordsForPeerPushCredit( PeerPushCreditStatus.PendingMerge, PeerPushCreditStatus.PendingWithdrawing, ); - await tx.peerPushCredit.indexes.byStatus - .iter(keyRange) - .forEachAsync(f); + await tx.peerPushCredit.indexes.byStatus.iter(keyRange).forEachAsync(f); } else { await tx.peerPushCredit.indexes.byStatus.iter().forEachAsync(f); } diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 8db68e0f1..9d5ca9f1a 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -791,8 +791,8 @@ function buildTransactionForDeposit( ort?: OperationRetryRecord, ): Transaction { let deposited = true; - for (const d of dg.depositedPerCoin) { - if (!d) { + for (const d of dg.statusPerCoin) { + if (d == DepositElementStatus.DepositPending) { deposited = false; } } @@ -801,7 +801,7 @@ function buildTransactionForDeposit( type: TransactionType.Deposit, txState: computeDepositTransactionStatus(dg), txActions: computeDepositTransactionActions(dg), - amountRaw: Amounts.stringify(dg.effectiveDepositAmount), + amountRaw: Amounts.stringify(dg.counterpartyEffectiveDepositAmount), amountEffective: Amounts.stringify(dg.totalPayCost), timestamp: dg.timestampCreated, targetPaytoUri: dg.wire.payto_uri, @@ -812,11 +812,11 @@ function buildTransactionForDeposit( }), wireTransferProgress: (100 * - dg.transactionPerCoin.reduce( + dg.statusPerCoin.reduce( (prev, cur) => prev + (cur === DepositElementStatus.Wired ? 1 : 0), 0, )) / - dg.transactionPerCoin.length, + dg.statusPerCoin.length, depositGroupId: dg.depositGroupId, trackingState: Object.values(dg.trackingState ?? {}), deposited, -- cgit v1.2.3 From 4898f50db777a68cd2ddd0c1e323ef55033f4af7 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Fri, 8 Sep 2023 13:33:21 +0200 Subject: wallet-core: more DB cleanup --- packages/taler-util/src/transactions-types.ts | 17 ------ .../src/crypto/cryptoImplementation.ts | 6 +-- packages/taler-wallet-core/src/db.ts | 63 +++++++++------------- packages/taler-wallet-core/src/dbless.ts | 18 ++----- .../taler-wallet-core/src/operations/balance.ts | 18 ++----- .../taler-wallet-core/src/operations/common.ts | 27 +++++----- .../taler-wallet-core/src/operations/deposits.ts | 2 +- .../taler-wallet-core/src/operations/exchanges.ts | 3 +- .../src/operations/pay-merchant.ts | 4 +- .../src/operations/pay-peer-common.ts | 12 +---- .../taler-wallet-core/src/operations/refresh.ts | 12 +---- .../src/operations/transactions.ts | 1 - .../src/operations/withdraw.test.ts | 22 ++++---- .../taler-wallet-core/src/util/coinSelection.ts | 30 +++-------- .../src/util/instructedAmountConversion.ts | 2 +- packages/taler-wallet-core/src/wallet.ts | 22 ++------ 16 files changed, 77 insertions(+), 182 deletions(-) (limited to 'packages/taler-wallet-core/src/operations/transactions.ts') diff --git a/packages/taler-util/src/transactions-types.ts b/packages/taler-util/src/transactions-types.ts index 6331bc731..304183ceb 100644 --- a/packages/taler-util/src/transactions-types.ts +++ b/packages/taler-util/src/transactions-types.ts @@ -527,22 +527,6 @@ export interface OrderShortInfo { */ summary_i18n?: InternationalizedString; - /** - * List of products that are part of the order - */ - products: Product[] | undefined; - - /** - * Time indicating when the order should be delivered. - * May be overwritten by individual products. - */ - delivery_date?: TalerProtocolTimestamp; - - /** - * Delivery location for (all!) products. - */ - delivery_location?: Location; - /** * URL of the fulfillment, given by the merchant */ @@ -724,7 +708,6 @@ export const codecForOrderShortInfo = (): Codec => .property("fulfillmentUrl", codecOptional(codecForString())) .property("merchant", codecForMerchantInfo()) .property("orderId", codecForString()) - .property("products", codecOptional(codecForList(codecForProduct()))) .property("summary", codecForString()) .property("summary_i18n", codecOptional(codecForInternationalizedString())) .build("OrderShortInfo"); diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts index c1a761fb6..35777e714 100644 --- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts +++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts @@ -959,11 +959,7 @@ export const nativeCryptoR: TalerCryptoInterfaceR = { req: DenominationValidationRequest, ): Promise { const { masterPub, denom } = req; - const value: AmountJson = { - currency: denom.currency, - fraction: denom.amountFrac, - value: denom.amountVal, - }; + const value: AmountJson = Amounts.parseOrThrow(denom.value); const p = buildSigPS(TalerSignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY) .put(decodeCrock(masterPub)) .put(timestampRoundedToBuffer(denom.stampStart)) diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 3db7ae9b5..3a8109320 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -28,56 +28,53 @@ import { } from "@gnu-taler/idb-bridge"; import { AgeCommitmentProof, - AmountJson, AmountString, + Amounts, + AttentionInfo, + Codec, CoinEnvelope, + CoinPublicKeyString, CoinRefreshRequest, CoinStatus, - MerchantContractTerms, + DenomSelectionState, DenominationInfo, DenominationPubKey, - DenomSelectionState, EddsaPublicKeyString, EddsaSignatureString, ExchangeAuditor, ExchangeGlobalFees, + HashCodeString, InternationalizedString, - Location, + Logger, + MerchantContractTerms, MerchantInfo, PayCoinSelection, PeerContractTerms, - Product, RefreshReason, TalerErrorDetail, + TalerPreciseTimestamp, TalerProtocolDuration, TalerProtocolTimestamp, TransactionIdStr, UnblindedSignature, WireInfo, - HashCodeString, - Amounts, - AttentionInfo, - Logger, - CoinPublicKeyString, - TalerPreciseTimestamp, codecForAny, - Codec, } from "@gnu-taler/taler-util"; +import { RetryInfo, TaskIdentifiers } from "./operations/common.js"; import { DbAccess, DbReadOnlyTransaction, DbReadWriteTransaction, - describeContents, - describeIndex, - describeStore, GetReadWriteAccess, IndexDescriptor, - openDatabase, StoreDescriptor, StoreNames, StoreWithIndexes, + describeContents, + describeIndex, + describeStore, + openDatabase, } from "./util/query.js"; -import { RetryInfo, TaskIdentifiers } from "./operations/common.js"; /** * This file contains the database schema of the Taler wallet together @@ -109,6 +106,9 @@ import { RetryInfo, TaskIdentifiers } from "./operations/common.js"; store. - More object stores should have an "id" primary key, as this makes referencing less expensive. + - Coin selections should probably go into a separate object store. + - Some records should be split up into an extra "details" record + that we don't always need to iterate over. */ /** @@ -323,12 +323,14 @@ export interface DenomFees { * Denomination record as stored in the wallet's database. */ export interface DenominationRecord { + /** + * Currency of the denomination. + * + * Stored separately as we have an index on it. + */ currency: string; - // FIXME: Use binary encoding of amount instead? - amountVal: number; - - amountFrac: number; + value: AmountString; /** * The denomination public key. @@ -407,14 +409,6 @@ export interface DenominationRecord { } export namespace DenominationRecord { - export function getValue(d: DenominationRecord): AmountJson { - return { - currency: d.currency, - fraction: d.amountFrac, - value: d.amountVal, - }; - } - export function toDenomInfo(d: DenominationRecord): DenominationInfo { return { denomPub: d.denomPub, @@ -427,7 +421,7 @@ export namespace DenominationRecord { stampExpireLegal: d.stampExpireLegal, stampExpireWithdraw: d.stampExpireWithdraw, stampStart: d.stampStart, - value: Amounts.stringify(DenominationRecord.getValue(d)), + value: Amounts.stringify(d.value), exchangeBaseUrl: d.exchangeBaseUrl, }; } @@ -1056,9 +1050,6 @@ export interface AllowedExchangeInfo { * processing in the wallet. */ export interface WalletContractData { - products?: Product[]; - summaryI18n: { [lang_tag: string]: string } | undefined; - /** * Fulfillment URL, or the empty string if the order has no fulfillment URL. * @@ -1076,6 +1067,7 @@ export interface WalletContractData { orderId: string; merchantBaseUrl: string; summary: string; + summaryI18n: { [lang_tag: string]: string } | undefined; autoRefund: TalerProtocolDuration | undefined; maxWireFee: AmountString; wireFeeAmortization: number; @@ -1087,8 +1079,6 @@ export interface WalletContractData { wireInfoHash: string; maxDepositFee: AmountString; minimumAge?: number; - deliveryDate: TalerProtocolTimestamp | undefined; - deliveryLocation: Location | undefined; } export enum PurchaseStatus { @@ -2095,8 +2085,7 @@ export interface OperationRetryRecord { */ export interface CoinAvailabilityRecord { currency: string; - amountVal: number; - amountFrac: number; + value: AmountString; denomPubHash: string; exchangeBaseUrl: string; diff --git a/packages/taler-wallet-core/src/dbless.ts b/packages/taler-wallet-core/src/dbless.ts index 0aad477e4..11c6c0f74 100644 --- a/packages/taler-wallet-core/src/dbless.ts +++ b/packages/taler-wallet-core/src/dbless.ts @@ -159,11 +159,7 @@ export async function withdrawCoin(args: { reservePriv: reserveKeyPair.reservePriv, reservePub: reserveKeyPair.reservePub, secretSeed: encodeCrock(getRandomBytes(32)), - value: { - currency: denom.currency, - fraction: denom.amountFrac, - value: denom.amountVal, - }, + value: Amounts.parseOrThrow(denom.value), }); const reqBody: ExchangeWithdrawRequest = { @@ -211,11 +207,7 @@ export function findDenomOrThrow( ): DenominationRecord { const denomselAllowLate = options.denomselAllowLate ?? false; for (const d of exchangeInfo.keys.currentDenominations) { - const value: AmountJson = { - currency: d.currency, - fraction: d.amountFrac, - value: d.amountVal, - }; + const value: AmountJson = Amounts.parseOrThrow(d.value); if ( Amounts.cmp(value, amount) === 0 && isWithdrawableDenom(d, denomselAllowLate) @@ -303,11 +295,7 @@ export async function refreshCoin(req: { denomPub: x.denomPub, denomPubHash: x.denomPubHash, feeWithdraw: x.fees.feeWithdraw, - value: Amounts.stringify({ - currency: x.currency, - fraction: x.amountFrac, - value: x.amountVal, - }), + value: x.value, })), meltCoinMaxAge: oldCoin.maxAge, }); diff --git a/packages/taler-wallet-core/src/operations/balance.ts b/packages/taler-wallet-core/src/operations/balance.ts index 28aa5ac70..a20ded2af 100644 --- a/packages/taler-wallet-core/src/operations/balance.ts +++ b/packages/taler-wallet-core/src/operations/balance.ts @@ -133,11 +133,7 @@ export async function getBalancesInsideTransaction( const b = initBalance(ca.currency); const count = ca.visibleCoinCount ?? 0; for (let i = 0; i < count; i++) { - b.available = Amounts.add(b.available, { - currency: ca.currency, - fraction: ca.amountFrac, - value: ca.amountVal, - }).amount; + b.available = Amounts.add(b.available, ca.value).amount; } }); @@ -408,11 +404,7 @@ export async function getMerchantPaymentBalanceDetails( if (ca.currency != req.currency) { return; } - const singleCoinAmount: AmountJson = { - currency: ca.currency, - fraction: ca.amountFrac, - value: ca.amountVal, - }; + const singleCoinAmount: AmountJson = Amounts.parseOrThrow(ca.value); const coinAmount: AmountJson = Amounts.mult( singleCoinAmount, ca.freshCoinCount, @@ -530,11 +522,7 @@ export async function getPeerPaymentBalanceDetailsInTx( ) { return; } - const singleCoinAmount: AmountJson = { - currency: ca.currency, - fraction: ca.amountFrac, - value: ca.amountVal, - }; + const singleCoinAmount: AmountJson = Amounts.parseOrThrow(ca.value); const coinAmount: AmountJson = Amounts.mult( singleCoinAmount, ca.freshCoinCount, diff --git a/packages/taler-wallet-core/src/operations/common.ts b/packages/taler-wallet-core/src/operations/common.ts index 680874dfa..50dd3dc5c 100644 --- a/packages/taler-wallet-core/src/operations/common.ts +++ b/packages/taler-wallet-core/src/operations/common.ts @@ -26,7 +26,6 @@ import { CoinRefreshRequest, CoinStatus, Duration, - ErrorInfoSummary, ExchangeEntryStatus, ExchangeListItem, ExchangeTosStatus, @@ -34,9 +33,11 @@ import { getErrorDetailFromException, j2s, Logger, + makeErrorDetail, NotificationType, OperationErrorInfo, RefreshReason, + TalerError, TalerErrorCode, TalerErrorDetail, TombstoneIdStr, @@ -44,32 +45,31 @@ import { TransactionType, WalletNotification, } from "@gnu-taler/taler-util"; +import { CryptoApiStoppedError } from "../crypto/workers/crypto-dispatcher.js"; import { - WalletStoresV1, + BackupProviderRecord, CoinRecord, + DepositGroupRecord, ExchangeDetailsRecord, + ExchangeEntryDbRecordStatus, + ExchangeEntryDbUpdateStatus, ExchangeEntryRecord, - BackupProviderRecord, - DepositGroupRecord, - PeerPullPaymentIncomingRecord, PeerPullCreditRecord, - PeerPushPaymentIncomingRecord, + PeerPullPaymentIncomingRecord, PeerPushDebitRecord, + PeerPushPaymentIncomingRecord, PurchaseRecord, RecoupGroupRecord, RefreshGroupRecord, RewardRecord, + WalletStoresV1, WithdrawalGroupRecord, - ExchangeEntryDbUpdateStatus, - ExchangeEntryDbRecordStatus, } from "../db.js"; -import { makeErrorDetail, TalerError } from "@gnu-taler/taler-util"; import { InternalWalletState } from "../internal-wallet-state.js"; -import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js"; -import { GetReadOnlyAccess, GetReadWriteAccess } from "../util/query.js"; -import { CryptoApiStoppedError } from "../crypto/workers/crypto-dispatcher.js"; import { PendingTaskType, TaskId } from "../pending-types.js"; import { assertUnreachable } from "../util/assertUnreachable.js"; +import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js"; +import { GetReadOnlyAccess, GetReadWriteAccess } from "../util/query.js"; import { constructTransactionIdentifier } from "./transactions.js"; const logger = new Logger("operations/common.ts"); @@ -144,8 +144,7 @@ export async function makeCoinAvailable( if (!car) { car = { maxAge: ageRestriction, - amountFrac: denom.amountFrac, - amountVal: denom.amountVal, + value: denom.value, currency: denom.currency, denomPubHash: denom.denomPubHash, exchangeBaseUrl: denom.exchangeBaseUrl, diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts index 3d78e938b..8ea792d91 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -1539,7 +1539,7 @@ async function getTotalFeesForDepositAmount( .iter(coin.exchangeBaseUrl) .filter((x) => Amounts.isSameCurrency( - DenominationRecord.getValue(x), + x.value, pcs.coinContributions[i], ), ); diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index 13e89b16c..43a08ed3b 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -443,8 +443,7 @@ async function downloadExchangeKeysInfo( exchangeMasterPub: exchangeKeysJsonUnchecked.master_public_key, isOffered: true, isRevoked: false, - amountFrac: value.fraction, - amountVal: value.value, + value: Amounts.stringify(value), currency: value.currency, stampExpireDeposit: denomIn.stamp_expire_deposit, stampExpireLegal: denomIn.stamp_expire_legal, diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts b/packages/taler-wallet-core/src/operations/pay-merchant.ts index 1b3248f49..3a7208ab0 100644 --- a/packages/taler-wallet-core/src/operations/pay-merchant.ts +++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts @@ -174,12 +174,12 @@ export async function getTotalPaymentCost( .iter(coin.exchangeBaseUrl) .filter((x) => Amounts.isSameCurrency( - DenominationRecord.getValue(x), + x.value, pcs.coinContributions[i], ), ); const amountLeft = Amounts.sub( - DenominationRecord.getValue(denom), + denom.value, pcs.coinContributions[i], ).amount; const refreshCost = getTotalRefreshCost( diff --git a/packages/taler-wallet-core/src/operations/pay-peer-common.ts b/packages/taler-wallet-core/src/operations/pay-peer-common.ts index 9e05e43d8..6d425289d 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer-common.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer-common.ts @@ -108,16 +108,8 @@ export async function getTotalPeerPaymentCost( } const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl .iter(coin.exchangeBaseUrl) - .filter((x) => - Amounts.isSameCurrency( - DenominationRecord.getValue(x), - pcs[i].contribution, - ), - ); - const amountLeft = Amounts.sub( - DenominationRecord.getValue(denom), - pcs[i].contribution, - ).amount; + .filter((x) => Amounts.isSameCurrency(x.value, pcs[i].contribution)); + const amountLeft = Amounts.sub(denom.value, pcs[i].contribution).amount; const refreshCost = getTotalRefreshCost( allDenoms, DenominationRecord.toDenomInfo(denom), diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index 3c4ef207a..75adbc860 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -134,11 +134,7 @@ export function getTotalRefreshCost( const resultingAmount = Amounts.add( Amounts.zeroOfCurrency(withdrawAmount.currency), ...withdrawDenoms.selectedDenoms.map( - (d) => - Amounts.mult( - DenominationRecord.getValue(denomMap[d.denomPubHash]), - d.count, - ).amount, + (d) => Amounts.mult(denomMap[d.denomPubHash].value, d.count).amount, ), ).amount; const totalCost = Amounts.sub(amountLeft, resultingAmount).amount; @@ -1200,11 +1196,7 @@ export async function autoRefresh( if (AbsoluteTime.isExpired(executeThreshold)) { refreshCoins.push({ coinPub: coin.coinPub, - amount: Amounts.stringify({ - value: denom.amountVal, - fraction: denom.amountFrac, - currency: denom.currency, - }), + amount: denom.value, }); } else { const checkThreshold = getAutoRefreshCheckThreshold(denom); diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 9d5ca9f1a..7bdb9af5b 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -884,7 +884,6 @@ async function buildTransactionForPurchase( const info: OrderShortInfo = { merchant: contractData.merchant, orderId: contractData.orderId, - products: contractData.products, summary: contractData.summary, summary_i18n: contractData.summaryI18n, contractTermsHash: contractData.contractTermsHash, diff --git a/packages/taler-wallet-core/src/operations/withdraw.test.ts b/packages/taler-wallet-core/src/operations/withdraw.test.ts index 5a557b5de..2d9286610 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.test.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.test.ts @@ -78,8 +78,7 @@ test("withdrawal selection bug repro", (t) => { }, verificationStatus: DenominationVerificationStatus.Unverified, currency: "KUDOS", - amountFrac: 0, - amountVal: 1000, + value: "KUDOS:1000", listIssueDate: { t_s: 0 }, }, { @@ -133,8 +132,7 @@ test("withdrawal selection bug repro", (t) => { t_s: 1585229388, }, verificationStatus: DenominationVerificationStatus.Unverified, - amountFrac: 0, - amountVal: 10, + value: "KUDOS:10", currency: "KUDOS", listIssueDate: { t_s: 0 }, }, @@ -188,8 +186,7 @@ test("withdrawal selection bug repro", (t) => { t_s: 1585229388, }, verificationStatus: DenominationVerificationStatus.Unverified, - amountFrac: 0, - amountVal: 5, + value: "KUDOS:5", currency: "KUDOS", listIssueDate: { t_s: 0 }, }, @@ -244,8 +241,7 @@ test("withdrawal selection bug repro", (t) => { t_s: 1585229388, }, verificationStatus: DenominationVerificationStatus.Unverified, - amountFrac: 0, - amountVal: 1, + value: "KUDOS:1", currency: "KUDOS", listIssueDate: { t_s: 0 }, }, @@ -299,8 +295,11 @@ test("withdrawal selection bug repro", (t) => { t_s: 1585229388, }, verificationStatus: DenominationVerificationStatus.Unverified, - amountFrac: 10000000, - amountVal: 0, + value: Amounts.stringify({ + currency: "KUDOS", + fraction: 10000000, + value: 0, + }), currency: "KUDOS", listIssueDate: { t_s: 0 }, }, @@ -354,8 +353,7 @@ test("withdrawal selection bug repro", (t) => { t_s: 1585229388, }, verificationStatus: DenominationVerificationStatus.Unverified, - amountFrac: 0, - amountVal: 2, + value: "KUDOS:2", currency: "KUDOS", listIssueDate: { t_s: 0 }, }, diff --git a/packages/taler-wallet-core/src/util/coinSelection.ts b/packages/taler-wallet-core/src/util/coinSelection.ts index ce9fec186..ef2f85789 100644 --- a/packages/taler-wallet-core/src/util/coinSelection.ts +++ b/packages/taler-wallet-core/src/util/coinSelection.ts @@ -699,25 +699,17 @@ export function selectWithdrawalDenominations( let totalWithdrawCost = Amounts.zeroOfCurrency(amountAvailable.currency); denoms = denoms.filter((d) => isWithdrawableDenom(d, denomselAllowLate)); - denoms.sort((d1, d2) => - Amounts.cmp( - DenominationRecord.getValue(d2), - DenominationRecord.getValue(d1), - ), - ); + denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value)); for (const d of denoms) { - const cost = Amounts.add( - DenominationRecord.getValue(d), - d.fees.feeWithdraw, - ).amount; + const cost = Amounts.add(d.value, d.fees.feeWithdraw).amount; const res = Amounts.divmod(remaining, cost); const count = res.quotient; remaining = Amounts.sub(remaining, Amounts.mult(cost, count).amount).amount; if (count > 0) { totalCoinValue = Amounts.add( totalCoinValue, - Amounts.mult(DenominationRecord.getValue(d), count).amount, + Amounts.mult(d.value, count).amount, ).amount; totalWithdrawCost = Amounts.add( totalWithdrawCost, @@ -766,30 +758,22 @@ export function selectForcedWithdrawalDenominations( let totalWithdrawCost = Amounts.zeroOfCurrency(amountAvailable.currency); denoms = denoms.filter((d) => isWithdrawableDenom(d, denomselAllowLate)); - denoms.sort((d1, d2) => - Amounts.cmp( - DenominationRecord.getValue(d2), - DenominationRecord.getValue(d1), - ), - ); + denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value)); for (const fds of forcedDenomSel.denoms) { const count = fds.count; const denom = denoms.find((x) => { - return Amounts.cmp(DenominationRecord.getValue(x), fds.value) == 0; + return Amounts.cmp(x.value, fds.value) == 0; }); if (!denom) { throw Error( `unable to find denom for forced selection (value ${fds.value})`, ); } - const cost = Amounts.add( - DenominationRecord.getValue(denom), - denom.fees.feeWithdraw, - ).amount; + const cost = Amounts.add(denom.value, denom.fees.feeWithdraw).amount; totalCoinValue = Amounts.add( totalCoinValue, - Amounts.mult(DenominationRecord.getValue(denom), count).amount, + Amounts.mult(denom.value, count).amount, ).amount; totalWithdrawCost = Amounts.add( totalWithdrawCost, diff --git a/packages/taler-wallet-core/src/util/instructedAmountConversion.ts b/packages/taler-wallet-core/src/util/instructedAmountConversion.ts index bd02e7b22..54c08eee4 100644 --- a/packages/taler-wallet-core/src/util/instructedAmountConversion.ts +++ b/packages/taler-wallet-core/src/util/instructedAmountConversion.ts @@ -321,7 +321,7 @@ function buildCoinInfoFromDenom( AbsoluteTime.fromProtocolTimestamp(denom.stampExpireDeposit), ), totalAvailable: total, - value: DenominationRecord.getValue(denom), + value: Amounts.parseOrThrow(denom.value), maxAge, }; } diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 11fb3f739..1a60b148c 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -915,11 +915,7 @@ async function dumpCoins(ws: InternalWalletState): Promise { coin_pub: c.coinPub, denom_pub: denomInfo.denomPub, denom_pub_hash: c.denomPubHash, - denom_value: Amounts.stringify({ - value: denom.amountVal, - currency: denom.currency, - fraction: denom.amountFrac, - }), + denom_value: denom.value, exchange_base_url: c.exchangeBaseUrl, refresh_parent_coin_pub: refreshParentCoinPub, withdrawal_reserve_pub: withdrawalReservePub, @@ -1876,35 +1872,27 @@ class InternalWalletStateImpl implements InternalWalletState { return computeRefundTransactionState(rec); } case TransactionType.PeerPullCredit: - const rec = await tx.peerPullCredit.get( - parsedTxId.pursePub, - ); + const rec = await tx.peerPullCredit.get(parsedTxId.pursePub); if (!rec) { return undefined; } return computePeerPullCreditTransactionState(rec); case TransactionType.PeerPullDebit: { - const rec = await tx.peerPullDebit.get( - parsedTxId.peerPullDebitId, - ); + const rec = await tx.peerPullDebit.get(parsedTxId.peerPullDebitId); if (!rec) { return undefined; } return computePeerPullDebitTransactionState(rec); } case TransactionType.PeerPushCredit: { - const rec = await tx.peerPushCredit.get( - parsedTxId.peerPushCreditId, - ); + const rec = await tx.peerPushCredit.get(parsedTxId.peerPushCreditId); if (!rec) { return undefined; } return computePeerPushCreditTransactionState(rec); } case TransactionType.PeerPushDebit: { - const rec = await tx.peerPushDebit.get( - parsedTxId.pursePub, - ); + const rec = await tx.peerPushDebit.get(parsedTxId.pursePub); if (!rec) { return undefined; } -- cgit v1.2.3