wallet-core: implement cancelAbortingTransaction for deposit groups

This commit is contained in:
Florian Dold 2023-05-25 19:26:40 +02:00
parent 8624d798b7
commit fe8749c3f8
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
5 changed files with 100 additions and 33 deletions

View File

@ -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;
}

View File

@ -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 {

View File

@ -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(

View File

@ -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.
*/

View File

@ -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);