aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2023-05-25 19:26:40 +0200
committerFlorian Dold <florian@dold.me>2023-05-25 19:26:40 +0200
commitfe8749c3f81547d080ea23d580497750d52fed91 (patch)
tree2c0e8960fa33fc318b460538fd09fe33e5896500
parent8624d798b799d78a4b6393493a0750027094904d (diff)
wallet-core: implement cancelAbortingTransaction for deposit groups
-rw-r--r--packages/taler-util/src/wallet-types.ts14
-rw-r--r--packages/taler-wallet-core/src/db.ts14
-rw-r--r--packages/taler-wallet-core/src/operations/deposits.ts80
-rw-r--r--packages/taler-wallet-core/src/operations/transactions.ts18
-rw-r--r--packages/taler-wallet-core/src/wallet.ts7
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);