diff options
author | Florian Dold <florian@dold.me> | 2023-05-25 19:26:40 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2023-05-25 19:26:40 +0200 |
commit | fe8749c3f81547d080ea23d580497750d52fed91 (patch) | |
tree | 2c0e8960fa33fc318b460538fd09fe33e5896500 | |
parent | 8624d798b799d78a4b6393493a0750027094904d (diff) |
wallet-core: implement cancelAbortingTransaction for deposit groups
-rw-r--r-- | packages/taler-util/src/wallet-types.ts | 14 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/db.ts | 14 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/operations/deposits.ts | 80 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/operations/transactions.ts | 18 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/wallet.ts | 7 |
5 files changed, 100 insertions, 33 deletions
diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts index 1ede15919..38358e734 100644 --- a/packages/taler-util/src/wallet-types.ts +++ b/packages/taler-util/src/wallet-types.ts @@ -396,7 +396,7 @@ export interface PrepareTipResult { /** * Unique ID for the tip assigned by the wallet. * Typically different from the merchant-generated tip ID. - * + * * @deprecated use transactionId instead */ walletTipId: string; @@ -1144,7 +1144,7 @@ export interface ManualWithdrawalDetails { /** * Number of coins that would be used for withdrawal. - * + * * The UIs should warn if this number is too high (rougly at >100). */ numCoins: number; @@ -1713,6 +1713,16 @@ export const codecForAcceptTipRequest = (): Codec<AcceptTipRequest> => .property("walletTipId", codecForString()) .build("AcceptTipRequest"); +export interface CancelAbortingTransactionRequest { + transactionId: TransactionIdStr; +} + +export const codecForCancelAbortingTransactionRequest = + (): Codec<CancelAbortingTransactionRequest> => + buildCodecForObject<CancelAbortingTransactionRequest>() + .property("transactionId", codecForTransactionIdStr()) + .build("CancelAbortingTransactionRequest"); + export interface SuspendTransactionRequest { transactionId: TransactionIdStr; } diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index d2a91aac4..8d66ee27b 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -1628,11 +1628,15 @@ export interface BackupProviderRecord { } export enum DepositOperationStatus { - Finished = 50 /* OperationStatusRange.DORMANT_START */, - Suspended = 51 /* OperationStatusRange.DORMANT_START + 1 */, - Aborted = 52 /* OperationStatusRange.DORMANT_START + 2 */, - Pending = 10 /* OperationStatusRange.ACTIVE_START */, - Aborting = 11 /* OperationStatusRange.ACTIVE_START + 1 */, + Pending = 10, + Aborting = 11, + + Suspended = 20, + SuspendedAborting = 21, + + Finished = 50, + Failed = 51, + Aborted = 52, } export interface DepositTrackingInfo { diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts index 6c41d76f6..dc743e17f 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -167,6 +167,14 @@ export function computeDepositTransactionStatus( return { major: TransactionMajorState.Aborted, } + case DepositOperationStatus.Failed: + return { + major: TransactionMajorState.Failed, + } + case DepositOperationStatus.SuspendedAborting: + return { + major: TransactionMajorState.SuspendedAborting, + } default: throw Error(`unexpected deposit group state (${dg.operationStatus})`); } @@ -184,7 +192,7 @@ export async function suspendDepositGroup( tag: PendingTaskType.Deposit, depositGroupId, }); - let res = await ws.db + const transitionInfo = await ws.db .mktx((x) => [x.depositGroups]) .runReadWrite(async (tx) => { const dg = await tx.depositGroups.get(depositGroupId); @@ -212,14 +220,7 @@ export async function suspendDepositGroup( return undefined; }); stopLongpolling(ws, retryTag); - if (res) { - ws.notify({ - type: NotificationType.TransactionStateTransition, - transactionId, - oldTxState: res.oldTxState, - newTxState: res.newTxState, - }); - } + notifyTransition(ws, transactionId, transitionInfo); } export async function resumeDepositGroup( @@ -230,7 +231,7 @@ export async function resumeDepositGroup( tag: TransactionType.Deposit, depositGroupId, }); - let res = await ws.db + const transitionInfo = await ws.db .mktx((x) => [x.depositGroups]) .runReadWrite(async (tx) => { const dg = await tx.depositGroups.get(depositGroupId); @@ -258,14 +259,7 @@ export async function resumeDepositGroup( return undefined; }); ws.workAvailable.trigger(); - if (res) { - ws.notify({ - type: NotificationType.TransactionStateTransition, - transactionId, - oldTxState: res.oldTxState, - newTxState: res.newTxState, - }); - } + notifyTransition(ws, transactionId, transitionInfo); } export async function abortDepositGroup( @@ -280,7 +274,7 @@ export async function abortDepositGroup( tag: PendingTaskType.Deposit, depositGroupId, }); - let res = await ws.db + const transitionInfo = await ws.db .mktx((x) => [x.depositGroups]) .runReadWrite(async (tx) => { const dg = await tx.depositGroups.get(depositGroupId); @@ -311,14 +305,48 @@ export async function abortDepositGroup( stopLongpolling(ws, retryTag); // Need to process the operation again. ws.workAvailable.trigger(); - if (res) { - ws.notify({ - type: NotificationType.TransactionStateTransition, - transactionId, - oldTxState: res.oldTxState, - newTxState: res.newTxState, + notifyTransition(ws, transactionId, transitionInfo); +} + +export async function cancelAbortingDepositGroup( + ws: InternalWalletState, + depositGroupId: string, +): Promise<void> { + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.Deposit, + depositGroupId, + }); + const retryTag = constructTaskIdentifier({ + tag: PendingTaskType.Deposit, + depositGroupId, + }); + const transitionInfo = await ws.db + .mktx((x) => [x.depositGroups]) + .runReadWrite(async (tx) => { + const dg = await tx.depositGroups.get(depositGroupId); + if (!dg) { + logger.warn( + `can't cancel aborting deposit group, depositGroupId=${depositGroupId} not found`, + ); + return undefined; + } + const oldState = computeDepositTransactionStatus(dg); + switch (dg.operationStatus) { + case DepositOperationStatus.SuspendedAborting: + case DepositOperationStatus.Aborting: { + dg.operationStatus = DepositOperationStatus.Failed; + await tx.depositGroups.put(dg); + return { + oldTxState: oldState, + newTxState: computeDepositTransactionStatus(dg), + }; + } + } + return undefined; }); - } + // FIXME: Also cancel ongoing work (via cancellation token, once implemented) + stopLongpolling(ws, retryTag); + notifyTransition(ws, transactionId, transitionInfo); } export async function deleteDepositGroup( diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 74df9acfb..84f879f58 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -75,6 +75,7 @@ import { } from "./common.js"; import { abortDepositGroup, + cancelAbortingDepositGroup, computeDepositTransactionStatus, processDepositGroup, resumeDepositGroup, @@ -1401,6 +1402,23 @@ export async function suspendTransaction( } } +export async function cancelAbortingTransaction( + ws: InternalWalletState, + transactionId: string, +): Promise<void> { + const tx = parseTransactionIdentifier(transactionId); + if (!tx) { + throw Error("invalid transaction ID"); + } + switch (tx.tag) { + case TransactionType.Deposit: + await cancelAbortingDepositGroup(ws, tx.depositGroupId); + return; + default: + logger.warn(`unable to suspend transaction of type '${tx.tag}'`); + } +} + /** * Resume a suspended transaction. */ diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index b20791241..85b0b9250 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -65,6 +65,7 @@ import { codecForApplyDevExperiment, codecForApplyRefundFromPurchaseIdRequest, codecForApplyRefundRequest, + codecForCancelAbortingTransactionRequest, codecForCheckPeerPullPaymentRequest, codecForCheckPeerPushDebitRequest, codecForConfirmPayRequest, @@ -231,6 +232,7 @@ import { import { acceptTip, prepareTip, processTip } from "./operations/tip.js"; import { abortTransaction, + cancelAbortingTransaction, deleteTransaction, getTransactionById, getTransactions, @@ -1229,6 +1231,11 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>( await suspendTransaction(ws, req.transactionId); return {}; } + case WalletApiOperation.CancelAbortingTransaction: { + const req = codecForCancelAbortingTransactionRequest().decode(payload); + await cancelAbortingTransaction(ws, req.transactionId); + return {}; + } case WalletApiOperation.ResumeTransaction: { const req = codecForResumeTransaction().decode(payload); await resumeTransaction(ws, req.transactionId); |