taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit a2b1a5cdd17dc58b136494162b3f0179008b1893
parent e7beff1a329b8976c1c38b93054e1336d4e624fd
Author: Florian Dold <florian@dold.me>
Date:   Wed, 11 Dec 2024 16:24:56 +0100

wallet-core: move refresh outside of peer-push-debit transaction

We expect this refresh to always succees, unless the exchange is
buggy/evil or the network is down.  In that case, the problem is still
shown as a failing separate refresh transaction.  Doesn't make sense to
include the refresh as part of the transaction, as then the
(non-material) available amount would include the refresh output,
but the peer-push-debit transaction would still be considered pending
and included in pendingOutgoing.

Diffstat:
Mpackages/taler-wallet-core/src/balance.ts | 4----
Mpackages/taler-wallet-core/src/db.ts | 17+++++++----------
Mpackages/taler-wallet-core/src/pay-peer-push-debit.ts | 177+------------------------------------------------------------------------------
3 files changed, 9 insertions(+), 189 deletions(-)

diff --git a/packages/taler-wallet-core/src/balance.ts b/packages/taler-wallet-core/src/balance.ts @@ -438,10 +438,6 @@ export async function getBalancesInsideTransaction( case PeerPushDebitStatus.PendingReady: case PeerPushDebitStatus.SuspendedReady: case PeerPushDebitStatus.PendingCreatePurse: - case PeerPushDebitStatus.AbortingRefreshDeleted: - case PeerPushDebitStatus.AbortingRefreshExpired: - case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted: - case PeerPushDebitStatus.SuspendedAbortingRefreshExpired: case PeerPushDebitStatus.SuspendedCreatePurse: { const currency = Amounts.currencyOf(ppdRecord.amount); await balanceStore.addPendingOutgoing( diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts @@ -1884,25 +1884,22 @@ export enum PeerPushDebitStatus { PendingCreatePurse = 0x0100_0000 /* ACTIVE_START */, PendingReady = 0x0100_0001, AbortingDeletePurse = 0x0103_0000, - /** - * Refresh after the purse got deleted by the wallet. - */ - AbortingRefreshDeleted = 0x0103_0001, - /** - * Refresh after the purse expired. - */ - AbortingRefreshExpired = 0x0103_0002, + SuspendedCreatePurse = 0x0110_0000, SuspendedReady = 0x0110_0001, SuspendedAbortingDeletePurse = 0x0113_0000, - SuspendedAbortingRefreshDeleted = 0x0113_0001, - SuspendedAbortingRefreshExpired = 0x0113_0002, Done = 0x0500_0000, Aborted = 0x0503_0000, Failed = 0x0501_0000, Expired = 0x0502_0000, + + // Legacy / reserved: + // SuspendedAbortingRefreshDeleted = 0x0113_0001, + // SuspendedAbortingRefreshExpired = 0x0113_0002, + // AbortingRefreshDeleted = 0x0103_0001, + // AbortingRefreshExpired = 0x0103_0002, } export interface DbPeerPushPaymentCoinSelection { diff --git a/packages/taler-wallet-core/src/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/pay-peer-push-debit.ts @@ -44,7 +44,6 @@ import { TransactionType, assertUnreachable, checkDbInvariant, - checkLogicInvariant, encodeCrock, getRandomBytes, j2s, @@ -74,7 +73,6 @@ import { EncryptContractRequest } from "./crypto/cryptoTypes.js"; import { PeerPushDebitRecord, PeerPushDebitStatus, - RefreshOperationStatus, WalletDbAllStoresReadOnlyTransaction, WalletDbReadWriteTransaction, timestampPreciseFromDb, @@ -92,7 +90,7 @@ import { getTotalPeerPaymentCostInTx, queryCoinInfosForSelection, } from "./pay-peer-common.js"; -import { createRefreshGroup, waitRefreshFinal } from "./refresh.js"; +import { createRefreshGroup } from "./refresh.js"; import { constructTransactionIdentifier, isUnsuccessfulTransaction, @@ -226,12 +224,6 @@ export class PeerPushDebitTransactionContext implements TransactionContext { case PeerPushDebitStatus.PendingCreatePurse: newStatus = PeerPushDebitStatus.SuspendedCreatePurse; break; - case PeerPushDebitStatus.AbortingRefreshDeleted: - newStatus = PeerPushDebitStatus.SuspendedAbortingRefreshDeleted; - break; - case PeerPushDebitStatus.AbortingRefreshExpired: - newStatus = PeerPushDebitStatus.SuspendedAbortingRefreshExpired; - break; case PeerPushDebitStatus.AbortingDeletePurse: newStatus = PeerPushDebitStatus.SuspendedAbortingDeletePurse; break; @@ -239,8 +231,6 @@ export class PeerPushDebitTransactionContext implements TransactionContext { newStatus = PeerPushDebitStatus.SuspendedReady; break; case PeerPushDebitStatus.SuspendedAbortingDeletePurse: - case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted: - case PeerPushDebitStatus.SuspendedAbortingRefreshExpired: case PeerPushDebitStatus.SuspendedReady: case PeerPushDebitStatus.SuspendedCreatePurse: case PeerPushDebitStatus.Done: @@ -293,11 +283,7 @@ export class PeerPushDebitTransactionContext implements TransactionContext { pushDebitRec.abortReason = reason; newStatus = PeerPushDebitStatus.AbortingDeletePurse; break; - case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted: case PeerPushDebitStatus.SuspendedAbortingDeletePurse: - case PeerPushDebitStatus.SuspendedAbortingRefreshExpired: - case PeerPushDebitStatus.AbortingRefreshDeleted: - case PeerPushDebitStatus.AbortingRefreshExpired: case PeerPushDebitStatus.Done: case PeerPushDebitStatus.AbortingDeletePurse: case PeerPushDebitStatus.Aborted: @@ -342,12 +328,6 @@ export class PeerPushDebitTransactionContext implements TransactionContext { case PeerPushDebitStatus.SuspendedAbortingDeletePurse: newStatus = PeerPushDebitStatus.AbortingDeletePurse; break; - case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted: - newStatus = PeerPushDebitStatus.AbortingRefreshDeleted; - break; - case PeerPushDebitStatus.SuspendedAbortingRefreshExpired: - newStatus = PeerPushDebitStatus.AbortingRefreshExpired; - break; case PeerPushDebitStatus.SuspendedReady: newStatus = PeerPushDebitStatus.PendingReady; break; @@ -355,8 +335,6 @@ export class PeerPushDebitTransactionContext implements TransactionContext { newStatus = PeerPushDebitStatus.PendingCreatePurse; break; case PeerPushDebitStatus.PendingCreatePurse: - case PeerPushDebitStatus.AbortingRefreshDeleted: - case PeerPushDebitStatus.AbortingRefreshExpired: case PeerPushDebitStatus.AbortingDeletePurse: case PeerPushDebitStatus.PendingReady: case PeerPushDebitStatus.Done: @@ -398,15 +376,8 @@ export class PeerPushDebitTransactionContext implements TransactionContext { } let newStatus: PeerPushDebitStatus; switch (pushDebitRec.status) { - case PeerPushDebitStatus.AbortingRefreshDeleted: - case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted: - // FIXME: What to do about the refresh group? - newStatus = PeerPushDebitStatus.Failed; - break; case PeerPushDebitStatus.AbortingDeletePurse: case PeerPushDebitStatus.SuspendedAbortingDeletePurse: - case PeerPushDebitStatus.AbortingRefreshExpired: - case PeerPushDebitStatus.SuspendedAbortingRefreshExpired: case PeerPushDebitStatus.PendingReady: case PeerPushDebitStatus.SuspendedReady: case PeerPushDebitStatus.SuspendedCreatePurse: @@ -937,7 +908,6 @@ async function processPeerPushDebitAbortingDeletePurse( RefreshReason.AbortPeerPushDebit, ctx.transactionId, ); - ppiRec.status = PeerPushDebitStatus.AbortingRefreshDeleted; ppiRec.abortRefreshGroupId = refresh.refreshGroupId; await tx.peerPushDebit.put(ppiRec); await ctx.updateTransactionMeta(tx); @@ -989,103 +959,6 @@ async function transitionPeerPushDebitTransaction( notifyTransition(wex, ctx.transactionId, transitionInfo); } -async function processPeerPushDebitAbortingRefreshDeleted( - wex: WalletExecutionContext, - peerPushInitiation: PeerPushDebitRecord, -): Promise<TaskRunResult> { - const pursePub = peerPushInitiation.pursePub; - const abortRefreshGroupId = peerPushInitiation.abortRefreshGroupId; - checkLogicInvariant(!!abortRefreshGroupId); - const ctx = new PeerPushDebitTransactionContext(wex, pursePub); - if (peerPushInitiation.abortRefreshGroupId) { - await waitRefreshFinal(wex, peerPushInitiation.abortRefreshGroupId); - } - const transitionInfo = await wex.db.runReadWriteTx( - { storeNames: ["refreshGroups", "peerPushDebit", "transactionsMeta"] }, - async (tx) => { - const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId); - let newOpState: PeerPushDebitStatus | undefined; - if (!refreshGroup) { - // Maybe it got manually deleted? Means that we should - // just go into failed. - logger.warn("no aborting refresh group found for deposit group"); - newOpState = PeerPushDebitStatus.Failed; - } else { - if (refreshGroup.operationStatus === RefreshOperationStatus.Finished) { - newOpState = PeerPushDebitStatus.Aborted; - } else if ( - refreshGroup.operationStatus === RefreshOperationStatus.Failed - ) { - newOpState = PeerPushDebitStatus.Failed; - } - } - if (newOpState) { - const newDg = await tx.peerPushDebit.get(pursePub); - if (!newDg) { - return; - } - const oldTxState = computePeerPushDebitTransactionState(newDg); - newDg.status = newOpState; - const newTxState = computePeerPushDebitTransactionState(newDg); - await tx.peerPushDebit.put(newDg); - await ctx.updateTransactionMeta(tx); - return { oldTxState, newTxState }; - } - return undefined; - }, - ); - notifyTransition(wex, ctx.transactionId, transitionInfo); - // FIXME: Shouldn't this be finished in some cases?! - return TaskRunResult.backoff(); -} - -async function processPeerPushDebitAbortingRefreshExpired( - wex: WalletExecutionContext, - peerPushInitiation: PeerPushDebitRecord, -): Promise<TaskRunResult> { - const pursePub = peerPushInitiation.pursePub; - const abortRefreshGroupId = peerPushInitiation.abortRefreshGroupId; - checkLogicInvariant(!!abortRefreshGroupId); - const ctx = new PeerPushDebitTransactionContext(wex, pursePub); - const transitionInfo = await wex.db.runReadWriteTx( - { storeNames: ["peerPushDebit", "refreshGroups", "transactionsMeta"] }, - async (tx) => { - const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId); - let newOpState: PeerPushDebitStatus | undefined; - if (!refreshGroup) { - // Maybe it got manually deleted? Means that we should - // just go into failed. - logger.warn("no aborting refresh group found for deposit group"); - newOpState = PeerPushDebitStatus.Failed; - } else { - if (refreshGroup.operationStatus === RefreshOperationStatus.Finished) { - newOpState = PeerPushDebitStatus.Expired; - } else if ( - refreshGroup.operationStatus === RefreshOperationStatus.Failed - ) { - newOpState = PeerPushDebitStatus.Failed; - } - } - if (newOpState) { - const newDg = await tx.peerPushDebit.get(pursePub); - if (!newDg) { - return; - } - const oldTxState = computePeerPushDebitTransactionState(newDg); - newDg.status = newOpState; - const newTxState = computePeerPushDebitTransactionState(newDg); - await tx.peerPushDebit.put(newDg); - await ctx.updateTransactionMeta(tx); - return { oldTxState, newTxState }; - } - return undefined; - }, - ); - notifyTransition(wex, ctx.transactionId, transitionInfo); - // FIXME: Shouldn't this be finished in some cases?! - return TaskRunResult.backoff(); -} - /** * Process the "pending(ready)" state of a peer-push-debit transaction. */ @@ -1178,7 +1051,7 @@ async function processPeerPushDebitReady( ppiRec.abortRefreshGroupId = refresh.refreshGroupId; } - ppiRec.status = PeerPushDebitStatus.AbortingRefreshExpired; + ppiRec.status = PeerPushDebitStatus.Aborted; await tx.peerPushDebit.put(ppiRec); await ctx.updateTransactionMeta(tx); const newTxState = computePeerPushDebitTransactionState(ppiRec); @@ -1221,16 +1094,6 @@ export async function processPeerPushDebit( return processPeerPushDebitReady(wex, peerPushInitiation); case PeerPushDebitStatus.AbortingDeletePurse: return processPeerPushDebitAbortingDeletePurse(wex, peerPushInitiation); - case PeerPushDebitStatus.AbortingRefreshDeleted: - return processPeerPushDebitAbortingRefreshDeleted( - wex, - peerPushInitiation, - ); - case PeerPushDebitStatus.AbortingRefreshExpired: - return processPeerPushDebitAbortingRefreshExpired( - wex, - peerPushInitiation, - ); default: { const txState = computePeerPushDebitTransactionState(peerPushInitiation); logger.warn( @@ -1426,24 +1289,8 @@ export function computePeerPushDebitTransactionActions( TransactionAction.Suspend, TransactionAction.Fail, ]; - case PeerPushDebitStatus.AbortingRefreshDeleted: - return [ - TransactionAction.Retry, - TransactionAction.Suspend, - TransactionAction.Fail, - ]; - case PeerPushDebitStatus.AbortingRefreshExpired: - return [ - TransactionAction.Retry, - TransactionAction.Suspend, - TransactionAction.Fail, - ]; - case PeerPushDebitStatus.SuspendedAbortingRefreshExpired: - return [TransactionAction.Resume, TransactionAction.Fail]; case PeerPushDebitStatus.SuspendedAbortingDeletePurse: return [TransactionAction.Resume, TransactionAction.Fail]; - case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted: - return [TransactionAction.Resume, TransactionAction.Fail]; case PeerPushDebitStatus.SuspendedCreatePurse: return [TransactionAction.Resume, TransactionAction.Abort]; case PeerPushDebitStatus.SuspendedReady: @@ -1480,31 +1327,11 @@ export function computePeerPushDebitTransactionState( major: TransactionMajorState.Aborting, minor: TransactionMinorState.DeletePurse, }; - case PeerPushDebitStatus.AbortingRefreshDeleted: - return { - major: TransactionMajorState.Aborting, - minor: TransactionMinorState.Refresh, - }; - case PeerPushDebitStatus.AbortingRefreshExpired: - return { - major: TransactionMajorState.Aborting, - minor: TransactionMinorState.RefreshExpired, - }; case PeerPushDebitStatus.SuspendedAbortingDeletePurse: return { major: TransactionMajorState.SuspendedAborting, minor: TransactionMinorState.DeletePurse, }; - case PeerPushDebitStatus.SuspendedAbortingRefreshExpired: - return { - major: TransactionMajorState.SuspendedAborting, - minor: TransactionMinorState.RefreshExpired, - }; - case PeerPushDebitStatus.SuspendedAbortingRefreshDeleted: - return { - major: TransactionMajorState.SuspendedAborting, - minor: TransactionMinorState.Refresh, - }; case PeerPushDebitStatus.SuspendedCreatePurse: return { major: TransactionMajorState.Suspended,