summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/operations/withdraw.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2024-01-15 17:36:50 +0100
committerFlorian Dold <florian@dold.me>2024-01-15 18:43:20 +0100
commit8da08fe4205c1e03eec3d4925c598be0b6769ba5 (patch)
treee81d59fa7a1ef40687fc16199e4d44dd70e02b5c /packages/taler-wallet-core/src/operations/withdraw.ts
parent68f3bcdc6cece62176849ab065e82630ebc4deae (diff)
downloadwallet-core-8da08fe4205c1e03eec3d4925c598be0b6769ba5.tar.gz
wallet-core-8da08fe4205c1e03eec3d4925c598be0b6769ba5.tar.bz2
wallet-core-8da08fe4205c1e03eec3d4925c598be0b6769ba5.zip
wallet-core: uniform transaction interface, cleanup
Diffstat (limited to 'packages/taler-wallet-core/src/operations/withdraw.ts')
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.ts482
1 files changed, 247 insertions, 235 deletions
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts
index d02cf0597..58df75964 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -105,6 +105,8 @@ import {
TaskIdentifiers,
TaskRunResult,
TaskRunResultType,
+ TombstoneTag,
+ TransactionContext,
constructTaskIdentifier,
makeCoinAvailable,
makeCoinsVisible,
@@ -146,246 +148,246 @@ import {
*/
const logger = new Logger("operations/withdraw.ts");
-export async function suspendWithdrawalTransaction(
- ws: InternalWalletState,
- withdrawalGroupId: string,
-) {
- const taskId = constructTaskIdentifier({
- tag: PendingTaskType.Withdraw,
- withdrawalGroupId,
- });
- stopLongpolling(ws, taskId);
- const transitionInfo = await ws.db
- .mktx((x) => [x.withdrawalGroups])
- .runReadWrite(async (tx) => {
- const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
- if (!wg) {
- logger.warn(`withdrawal group ${withdrawalGroupId} not found`);
- return;
- }
- let newStatus: WithdrawalGroupStatus | undefined = undefined;
- switch (wg.status) {
- case WithdrawalGroupStatus.PendingReady:
- newStatus = WithdrawalGroupStatus.SuspendedReady;
- break;
- case WithdrawalGroupStatus.AbortingBank:
- newStatus = WithdrawalGroupStatus.SuspendedAbortingBank;
- break;
- case WithdrawalGroupStatus.PendingWaitConfirmBank:
- newStatus = WithdrawalGroupStatus.SuspendedWaitConfirmBank;
- break;
- case WithdrawalGroupStatus.PendingRegisteringBank:
- newStatus = WithdrawalGroupStatus.SuspendedRegisteringBank;
- break;
- case WithdrawalGroupStatus.PendingQueryingStatus:
- newStatus = WithdrawalGroupStatus.SuspendedQueryingStatus;
- break;
- case WithdrawalGroupStatus.PendingKyc:
- newStatus = WithdrawalGroupStatus.SuspendedKyc;
- break;
- case WithdrawalGroupStatus.PendingAml:
- newStatus = WithdrawalGroupStatus.SuspendedAml;
- break;
- default:
- logger.warn(
- `Unsupported 'suspend' on withdrawal transaction in status ${wg.status}`,
- );
- }
- if (newStatus != null) {
- const oldTxState = computeWithdrawalTransactionStatus(wg);
- wg.status = newStatus;
- const newTxState = computeWithdrawalTransactionStatus(wg);
- await tx.withdrawalGroups.put(wg);
- return {
- oldTxState,
- newTxState,
- };
- }
- return undefined;
+export class WithdrawTransactionContext implements TransactionContext {
+ public transactionId: string;
+ public retryTag: string;
+
+ constructor(
+ public ws: InternalWalletState,
+ public withdrawalGroupId: string,
+ ) {
+ this.transactionId = constructTransactionIdentifier({
+ tag: TransactionType.Withdrawal,
+ withdrawalGroupId,
});
+ this.retryTag = constructTaskIdentifier({
+ tag: PendingTaskType.Withdraw,
+ withdrawalGroupId,
+ });
+ }
- const transactionId = constructTransactionIdentifier({
- tag: TransactionType.Withdrawal,
- withdrawalGroupId,
- });
- notifyTransition(ws, transactionId, transitionInfo);
-}
+ async deleteTransaction(): Promise<void> {
+ const { ws, withdrawalGroupId } = this;
+ await ws.db
+ .mktx((x) => [x.withdrawalGroups, x.tombstones])
+ .runReadWrite(async (tx) => {
+ const withdrawalGroupRecord =
+ await tx.withdrawalGroups.get(withdrawalGroupId);
+ if (withdrawalGroupRecord) {
+ await tx.withdrawalGroups.delete(withdrawalGroupId);
+ await tx.tombstones.put({
+ id: TombstoneTag.DeleteWithdrawalGroup + ":" + withdrawalGroupId,
+ });
+ return;
+ }
+ });
+ }
-export async function resumeWithdrawalTransaction(
- ws: InternalWalletState,
- withdrawalGroupId: string,
-) {
- const transitionInfo = await ws.db
- .mktx((x) => [x.withdrawalGroups])
- .runReadWrite(async (tx) => {
- const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
- if (!wg) {
- logger.warn(`withdrawal group ${withdrawalGroupId} not found`);
- return;
- }
- let newStatus: WithdrawalGroupStatus | undefined = undefined;
- switch (wg.status) {
- case WithdrawalGroupStatus.SuspendedReady:
- newStatus = WithdrawalGroupStatus.PendingReady;
- break;
- case WithdrawalGroupStatus.SuspendedAbortingBank:
- newStatus = WithdrawalGroupStatus.AbortingBank;
- break;
- case WithdrawalGroupStatus.SuspendedWaitConfirmBank:
- newStatus = WithdrawalGroupStatus.PendingWaitConfirmBank;
- break;
- case WithdrawalGroupStatus.SuspendedQueryingStatus:
- newStatus = WithdrawalGroupStatus.PendingQueryingStatus;
- break;
- case WithdrawalGroupStatus.SuspendedRegisteringBank:
- newStatus = WithdrawalGroupStatus.PendingRegisteringBank;
- break;
- case WithdrawalGroupStatus.SuspendedAml:
- newStatus = WithdrawalGroupStatus.PendingAml;
- break;
- case WithdrawalGroupStatus.SuspendedKyc:
- newStatus = WithdrawalGroupStatus.PendingKyc;
- break;
- default:
- logger.warn(
- `Unsupported 'resume' on withdrawal transaction in status ${wg.status}`,
- );
- }
- if (newStatus != null) {
- const oldTxState = computeWithdrawalTransactionStatus(wg);
- wg.status = newStatus;
- const newTxState = computeWithdrawalTransactionStatus(wg);
- await tx.withdrawalGroups.put(wg);
- return {
- oldTxState,
- newTxState,
- };
- }
- return undefined;
- });
- ws.workAvailable.trigger();
- const transactionId = constructTransactionIdentifier({
- tag: TransactionType.Withdrawal,
- withdrawalGroupId,
- });
- notifyTransition(ws, transactionId, transitionInfo);
-}
+ async suspendTransaction(): Promise<void> {
+ const { ws, withdrawalGroupId, transactionId, retryTag } = this;
+ stopLongpolling(ws, retryTag);
+ const transitionInfo = await ws.db
+ .mktx((x) => [x.withdrawalGroups])
+ .runReadWrite(async (tx) => {
+ const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
+ if (!wg) {
+ logger.warn(`withdrawal group ${withdrawalGroupId} not found`);
+ return;
+ }
+ let newStatus: WithdrawalGroupStatus | undefined = undefined;
+ switch (wg.status) {
+ case WithdrawalGroupStatus.PendingReady:
+ newStatus = WithdrawalGroupStatus.SuspendedReady;
+ break;
+ case WithdrawalGroupStatus.AbortingBank:
+ newStatus = WithdrawalGroupStatus.SuspendedAbortingBank;
+ break;
+ case WithdrawalGroupStatus.PendingWaitConfirmBank:
+ newStatus = WithdrawalGroupStatus.SuspendedWaitConfirmBank;
+ break;
+ case WithdrawalGroupStatus.PendingRegisteringBank:
+ newStatus = WithdrawalGroupStatus.SuspendedRegisteringBank;
+ break;
+ case WithdrawalGroupStatus.PendingQueryingStatus:
+ newStatus = WithdrawalGroupStatus.SuspendedQueryingStatus;
+ break;
+ case WithdrawalGroupStatus.PendingKyc:
+ newStatus = WithdrawalGroupStatus.SuspendedKyc;
+ break;
+ case WithdrawalGroupStatus.PendingAml:
+ newStatus = WithdrawalGroupStatus.SuspendedAml;
+ break;
+ default:
+ logger.warn(
+ `Unsupported 'suspend' on withdrawal transaction in status ${wg.status}`,
+ );
+ }
+ if (newStatus != null) {
+ const oldTxState = computeWithdrawalTransactionStatus(wg);
+ wg.status = newStatus;
+ const newTxState = computeWithdrawalTransactionStatus(wg);
+ await tx.withdrawalGroups.put(wg);
+ return {
+ oldTxState,
+ newTxState,
+ };
+ }
+ return undefined;
+ });
-export async function abortWithdrawalTransaction(
- ws: InternalWalletState,
- withdrawalGroupId: string,
-) {
- const taskId = constructTaskIdentifier({
- tag: PendingTaskType.Withdraw,
- withdrawalGroupId,
- });
- const transactionId = constructTransactionIdentifier({
- tag: TransactionType.Withdrawal,
- withdrawalGroupId,
- });
- stopLongpolling(ws, taskId);
- const transitionInfo = await ws.db
- .mktx((x) => [x.withdrawalGroups])
- .runReadWrite(async (tx) => {
- const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
- if (!wg) {
- logger.warn(`withdrawal group ${withdrawalGroupId} not found`);
- return;
- }
- let newStatus: WithdrawalGroupStatus | undefined = undefined;
- switch (wg.status) {
- case WithdrawalGroupStatus.SuspendedRegisteringBank:
- case WithdrawalGroupStatus.SuspendedWaitConfirmBank:
- case WithdrawalGroupStatus.PendingWaitConfirmBank:
- case WithdrawalGroupStatus.PendingRegisteringBank:
- newStatus = WithdrawalGroupStatus.AbortingBank;
- break;
- case WithdrawalGroupStatus.SuspendedAml:
- case WithdrawalGroupStatus.SuspendedKyc:
- case WithdrawalGroupStatus.SuspendedQueryingStatus:
- case WithdrawalGroupStatus.SuspendedReady:
- case WithdrawalGroupStatus.PendingAml:
- case WithdrawalGroupStatus.PendingKyc:
- case WithdrawalGroupStatus.PendingQueryingStatus:
- newStatus = WithdrawalGroupStatus.AbortedExchange;
- break;
- case WithdrawalGroupStatus.PendingReady:
- newStatus = WithdrawalGroupStatus.SuspendedReady;
- break;
- case WithdrawalGroupStatus.SuspendedAbortingBank:
- case WithdrawalGroupStatus.AbortingBank:
- // No transition needed, but not an error
- break;
- case WithdrawalGroupStatus.Done:
- case WithdrawalGroupStatus.FailedBankAborted:
- case WithdrawalGroupStatus.AbortedExchange:
- case WithdrawalGroupStatus.AbortedBank:
- case WithdrawalGroupStatus.FailedAbortingBank:
- // Not allowed
- throw Error("abort not allowed in current state");
- break;
- default:
- assertUnreachable(wg.status);
- }
- if (newStatus != null) {
- const oldTxState = computeWithdrawalTransactionStatus(wg);
- wg.status = newStatus;
- const newTxState = computeWithdrawalTransactionStatus(wg);
- await tx.withdrawalGroups.put(wg);
- return {
- oldTxState,
- newTxState,
- };
- }
- return undefined;
- });
- ws.workAvailable.trigger();
- notifyTransition(ws, transactionId, transitionInfo);
-}
+ notifyTransition(ws, transactionId, transitionInfo);
+ }
-export async function failWithdrawalTransaction(
- ws: InternalWalletState,
- withdrawalGroupId: string,
-) {
- const taskId = constructTaskIdentifier({
- tag: PendingTaskType.Withdraw,
- withdrawalGroupId,
- });
- const transactionId = constructTransactionIdentifier({
- tag: TransactionType.Withdrawal,
- withdrawalGroupId,
- });
- stopLongpolling(ws, taskId);
- const stateUpdate = await ws.db
- .mktx((x) => [x.withdrawalGroups])
- .runReadWrite(async (tx) => {
- const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
- if (!wg) {
- logger.warn(`withdrawal group ${withdrawalGroupId} not found`);
- return;
- }
- let newStatus: WithdrawalGroupStatus | undefined = undefined;
- switch (wg.status) {
- case WithdrawalGroupStatus.SuspendedAbortingBank:
- case WithdrawalGroupStatus.AbortingBank:
- newStatus = WithdrawalGroupStatus.FailedAbortingBank;
- break;
- default:
- break;
- }
- if (newStatus != null) {
- const oldTxState = computeWithdrawalTransactionStatus(wg);
- wg.status = newStatus;
- const newTxState = computeWithdrawalTransactionStatus(wg);
- await tx.withdrawalGroups.put(wg);
- return {
- oldTxState,
- newTxState,
- };
- }
- return undefined;
- });
- notifyTransition(ws, transactionId, stateUpdate);
+ async abortTransaction(): Promise<void> {
+ const { ws, withdrawalGroupId, transactionId } = this;
+ stopLongpolling(ws, this.retryTag);
+ const transitionInfo = await ws.db
+ .mktx((x) => [x.withdrawalGroups])
+ .runReadWrite(async (tx) => {
+ const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
+ if (!wg) {
+ logger.warn(`withdrawal group ${withdrawalGroupId} not found`);
+ return;
+ }
+ let newStatus: WithdrawalGroupStatus | undefined = undefined;
+ switch (wg.status) {
+ case WithdrawalGroupStatus.SuspendedRegisteringBank:
+ case WithdrawalGroupStatus.SuspendedWaitConfirmBank:
+ case WithdrawalGroupStatus.PendingWaitConfirmBank:
+ case WithdrawalGroupStatus.PendingRegisteringBank:
+ newStatus = WithdrawalGroupStatus.AbortingBank;
+ break;
+ case WithdrawalGroupStatus.SuspendedAml:
+ case WithdrawalGroupStatus.SuspendedKyc:
+ case WithdrawalGroupStatus.SuspendedQueryingStatus:
+ case WithdrawalGroupStatus.SuspendedReady:
+ case WithdrawalGroupStatus.PendingAml:
+ case WithdrawalGroupStatus.PendingKyc:
+ case WithdrawalGroupStatus.PendingQueryingStatus:
+ newStatus = WithdrawalGroupStatus.AbortedExchange;
+ break;
+ case WithdrawalGroupStatus.PendingReady:
+ newStatus = WithdrawalGroupStatus.SuspendedReady;
+ break;
+ case WithdrawalGroupStatus.SuspendedAbortingBank:
+ case WithdrawalGroupStatus.AbortingBank:
+ // No transition needed, but not an error
+ break;
+ case WithdrawalGroupStatus.Done:
+ case WithdrawalGroupStatus.FailedBankAborted:
+ case WithdrawalGroupStatus.AbortedExchange:
+ case WithdrawalGroupStatus.AbortedBank:
+ case WithdrawalGroupStatus.FailedAbortingBank:
+ // Not allowed
+ throw Error("abort not allowed in current state");
+ break;
+ default:
+ assertUnreachable(wg.status);
+ }
+ if (newStatus != null) {
+ const oldTxState = computeWithdrawalTransactionStatus(wg);
+ wg.status = newStatus;
+ const newTxState = computeWithdrawalTransactionStatus(wg);
+ await tx.withdrawalGroups.put(wg);
+ return {
+ oldTxState,
+ newTxState,
+ };
+ }
+ return undefined;
+ });
+ ws.workAvailable.trigger();
+ notifyTransition(ws, transactionId, transitionInfo);
+ }
+
+ async resumeTransaction(): Promise<void> {
+ const { ws, withdrawalGroupId, transactionId } = this;
+ const transitionInfo = await ws.db
+ .mktx((x) => [x.withdrawalGroups])
+ .runReadWrite(async (tx) => {
+ const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
+ if (!wg) {
+ logger.warn(`withdrawal group ${withdrawalGroupId} not found`);
+ return;
+ }
+ let newStatus: WithdrawalGroupStatus | undefined = undefined;
+ switch (wg.status) {
+ case WithdrawalGroupStatus.SuspendedReady:
+ newStatus = WithdrawalGroupStatus.PendingReady;
+ break;
+ case WithdrawalGroupStatus.SuspendedAbortingBank:
+ newStatus = WithdrawalGroupStatus.AbortingBank;
+ break;
+ case WithdrawalGroupStatus.SuspendedWaitConfirmBank:
+ newStatus = WithdrawalGroupStatus.PendingWaitConfirmBank;
+ break;
+ case WithdrawalGroupStatus.SuspendedQueryingStatus:
+ newStatus = WithdrawalGroupStatus.PendingQueryingStatus;
+ break;
+ case WithdrawalGroupStatus.SuspendedRegisteringBank:
+ newStatus = WithdrawalGroupStatus.PendingRegisteringBank;
+ break;
+ case WithdrawalGroupStatus.SuspendedAml:
+ newStatus = WithdrawalGroupStatus.PendingAml;
+ break;
+ case WithdrawalGroupStatus.SuspendedKyc:
+ newStatus = WithdrawalGroupStatus.PendingKyc;
+ break;
+ default:
+ logger.warn(
+ `Unsupported 'resume' on withdrawal transaction in status ${wg.status}`,
+ );
+ }
+ if (newStatus != null) {
+ const oldTxState = computeWithdrawalTransactionStatus(wg);
+ wg.status = newStatus;
+ const newTxState = computeWithdrawalTransactionStatus(wg);
+ await tx.withdrawalGroups.put(wg);
+ return {
+ oldTxState,
+ newTxState,
+ };
+ }
+ return undefined;
+ });
+ ws.workAvailable.trigger();
+ notifyTransition(ws, transactionId, transitionInfo);
+ }
+
+ async failTransaction(): Promise<void> {
+ const { ws, withdrawalGroupId, transactionId, retryTag } = this;
+ stopLongpolling(ws, retryTag);
+ const stateUpdate = await ws.db
+ .mktx((x) => [x.withdrawalGroups])
+ .runReadWrite(async (tx) => {
+ const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
+ if (!wg) {
+ logger.warn(`withdrawal group ${withdrawalGroupId} not found`);
+ return;
+ }
+ let newStatus: WithdrawalGroupStatus | undefined = undefined;
+ switch (wg.status) {
+ case WithdrawalGroupStatus.SuspendedAbortingBank:
+ case WithdrawalGroupStatus.AbortingBank:
+ newStatus = WithdrawalGroupStatus.FailedAbortingBank;
+ break;
+ default:
+ break;
+ }
+ if (newStatus != null) {
+ const oldTxState = computeWithdrawalTransactionStatus(wg);
+ wg.status = newStatus;
+ const newTxState = computeWithdrawalTransactionStatus(wg);
+ await tx.withdrawalGroups.put(wg);
+ return {
+ oldTxState,
+ newTxState,
+ };
+ }
+ return undefined;
+ });
+ notifyTransition(ws, transactionId, stateUpdate);
+ }
}
export function computeWithdrawalTransactionStatus(
@@ -2413,6 +2415,16 @@ export async function internalPerformCreateWithdrawalGroup(
exchangeNotif: undefined,
};
}
+ const existingWg = await tx.withdrawalGroups.get(
+ withdrawalGroup.withdrawalGroupId,
+ );
+ if (existingWg) {
+ return {
+ withdrawalGroup: existingWg,
+ exchangeNotif: undefined,
+ transitionInfo: undefined,
+ };
+ }
await tx.withdrawalGroups.add(withdrawalGroup);
await tx.reserves.put({
reservePub: withdrawalGroup.reservePub,