diff options
author | Florian Dold <florian@dold.me> | 2023-06-19 16:03:06 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2023-06-19 16:03:06 +0200 |
commit | 54f0c82999833132baf83995526025ac56d6fe06 (patch) | |
tree | b0138031c4a0432ec5ecddb62be14b0432112a4b /packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts | |
parent | ffa68ce8ddc77bf622af4234696a065cde482554 (diff) | |
download | wallet-core-54f0c82999833132baf83995526025ac56d6fe06.tar.xz |
wallet-core: fix peer-(push,pull)-debit withdrawal states
Diffstat (limited to 'packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts')
-rw-r--r-- | packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts | 256 |
1 files changed, 168 insertions, 88 deletions
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts index 447ffce8f..48b81d6c2 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts @@ -91,7 +91,7 @@ import { const logger = new Logger("pay-peer-pull-credit.ts"); -export async function queryPurseForPeerPullCredit( +async function queryPurseForPeerPullCredit( ws: InternalWalletState, pullIni: PeerPullPaymentInitiationRecord, cancellationToken: CancellationToken, @@ -102,7 +102,7 @@ export async function queryPurseForPeerPullCredit( ); purseDepositUrl.searchParams.set("timeout_ms", "30000"); logger.info(`querying purse status via ${purseDepositUrl.href}`); - const resp = await ws.http.get(purseDepositUrl.href, { + const resp = await ws.http.fetch(purseDepositUrl.href, { timeout: { d_ms: 60000 }, cancellationToken, }); @@ -153,8 +153,11 @@ export async function queryPurseForPeerPullCredit( pub: reserve.reservePub, }, }); - - await ws.db + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.PeerPullCredit, + pursePub: pullIni.pursePub, + }); + const transitionInfo = await ws.db .mktx((x) => [x.peerPullPaymentInitiations]) .runReadWrite(async (tx) => { const finPi = await tx.peerPullPaymentInitiations.get(pullIni.pursePub); @@ -162,11 +165,15 @@ export async function queryPurseForPeerPullCredit( logger.warn("peerPullPaymentInitiation not found anymore"); return; } + const oldTxState = computePeerPullCreditTransactionState(finPi); if (finPi.status === PeerPullPaymentInitiationStatus.PendingReady) { - finPi.status = PeerPullPaymentInitiationStatus.DonePurseDeposited; + finPi.status = PeerPullPaymentInitiationStatus.PendingWithdrawing; } await tx.peerPullPaymentInitiations.put(finPi); + const newTxState = computePeerPullCreditTransactionState(finPi); + return { oldTxState, newTxState }; }); + notifyTransition(ws, transactionId, transitionInfo); return { ready: true, }; @@ -293,91 +300,68 @@ async function processPeerPullCreditAbortingDeletePurse( return OperationAttemptResult.pendingEmpty(); } -export async function processPeerPullCredit( +async function handlePeerPullCreditWithdrawing( ws: InternalWalletState, - pursePub: string, + pullIni: PeerPullPaymentInitiationRecord, ): Promise<OperationAttemptResult> { - const pullIni = await ws.db - .mktx((x) => [x.peerPullPaymentInitiations]) - .runReadOnly(async (tx) => { - return tx.peerPullPaymentInitiations.get(pursePub); - }); - if (!pullIni) { - throw Error("peer pull payment initiation not found in database"); + if (!pullIni.withdrawalGroupId) { + throw Error("invalid db state (withdrawing, but no withdrawal group ID"); } - - const retryTag = constructTaskIdentifier({ - tag: PendingTaskType.PeerPullCredit, - pursePub, + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.PeerPullCredit, + pursePub: pullIni.pursePub, }); - - // We're already running! - if (ws.activeLongpoll[retryTag]) { - logger.info("peer-pull-credit already in long-polling, returning!"); - return { - type: OperationAttemptResultType.Longpoll, - }; - } - - logger.trace(`processing ${retryTag}, status=${pullIni.status}`); - - switch (pullIni.status) { - case PeerPullPaymentInitiationStatus.DonePurseDeposited: { - // We implement this case so that the "retry" action on a peer-pull-credit transaction - // also retries the withdrawal task. - - logger.warn( - "peer pull payment initiation is already finished, retrying withdrawal", + const wgId = pullIni.withdrawalGroupId; + let finished: boolean = false; + const transitionInfo = await ws.db + .mktx((x) => [x.peerPullPaymentInitiations, x.withdrawalGroups]) + .runReadWrite(async (tx) => { + const ppi = await tx.peerPullPaymentInitiations.get( + pullIni.pursePub, ); - - const withdrawalGroupId = pullIni.withdrawalGroupId; - - if (withdrawalGroupId) { - const taskId = constructTaskIdentifier({ - tag: PendingTaskType.Withdraw, - withdrawalGroupId, - }); - stopLongpolling(ws, taskId); - await resetOperationTimeout(ws, taskId); - await runOperationWithErrorReporting(ws, taskId, () => - processWithdrawalGroup(ws, withdrawalGroupId), - ); + if (!ppi) { + finished = true; + return; } + if (ppi.status !== PeerPullPaymentInitiationStatus.PendingWithdrawing) { + finished = true; + return; + } + const oldTxState = computePeerPullCreditTransactionState(ppi); + const wg = await tx.withdrawalGroups.get(wgId); + if (!wg) { + // FIXME: Fail the operation instead? + return undefined; + } + switch (wg.status) { + case WithdrawalGroupStatus.Finished: + finished = true; + ppi.status = PeerPullPaymentInitiationStatus.Done; + break; + // FIXME: Also handle other final states! + } + await tx.peerPullPaymentInitiations.put(ppi); + const newTxState = computePeerPullCreditTransactionState(ppi); return { - type: OperationAttemptResultType.Finished, - result: undefined, - }; - } - case PeerPullPaymentInitiationStatus.PendingReady: - runLongpollAsync(ws, retryTag, async (cancellationToken) => - queryPurseForPeerPullCredit(ws, pullIni, cancellationToken), - ); - logger.trace( - "returning early from processPeerPullCredit for long-polling in background", - ); - return { - type: OperationAttemptResultType.Longpoll, + oldTxState, + newTxState, }; - case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: { - if (!pullIni.kycInfo) { - throw Error("invalid state, kycInfo required"); - } - return await longpollKycStatus( - ws, - pursePub, - pullIni.exchangeBaseUrl, - pullIni.kycInfo, - "individual", - ); - } - case PeerPullPaymentInitiationStatus.PendingCreatePurse: - break; - case PeerPullPaymentInitiationStatus.AbortingDeletePurse: - return await processPeerPullCreditAbortingDeletePurse(ws, pullIni); - default: - throw Error(`unknown PeerPullPaymentInitiationStatus ${pullIni.status}`); + }); + notifyTransition(ws, transactionId, transitionInfo); + if (finished) { + return OperationAttemptResult.finishedEmpty(); + } else { + // FIXME: Return indicator that we depend on the other operation! + return OperationAttemptResult.pendingEmpty(); } +} +async function handlePeerPullCreditCreatePurse( + ws: InternalWalletState, + pullIni: PeerPullPaymentInitiationRecord, +): Promise<OperationAttemptResult> { + const purseFee = Amounts.stringify(Amounts.zeroOfAmount(pullIni.amount)); + const pursePub = pullIni.pursePub; const mergeReserve = await ws.db .mktx((x) => [x.reserves]) .runReadOnly(async (tx) => { @@ -388,8 +372,6 @@ export async function processPeerPullCredit( throw Error("merge reserve for peer pull payment not found in database"); } - const purseFee = Amounts.stringify(Amounts.zeroOfAmount(pullIni.amount)); - const reservePayto = talerPaytoFromExchangeReserve( pullIni.exchangeBaseUrl, mergeReserve.reservePub, @@ -474,6 +456,104 @@ export async function processPeerPullCredit( }; } +export async function processPeerPullCredit( + ws: InternalWalletState, + pursePub: string, +): Promise<OperationAttemptResult> { + const pullIni = await ws.db + .mktx((x) => [x.peerPullPaymentInitiations]) + .runReadOnly(async (tx) => { + return tx.peerPullPaymentInitiations.get(pursePub); + }); + if (!pullIni) { + throw Error("peer pull payment initiation not found in database"); + } + + const retryTag = constructTaskIdentifier({ + tag: PendingTaskType.PeerPullCredit, + pursePub, + }); + + // We're already running! + if (ws.activeLongpoll[retryTag]) { + logger.info("peer-pull-credit already in long-polling, returning!"); + return { + type: OperationAttemptResultType.Longpoll, + }; + } + + logger.trace(`processing ${retryTag}, status=${pullIni.status}`); + + switch (pullIni.status) { + case PeerPullPaymentInitiationStatus.Done: { + // We implement this case so that the "retry" action on a peer-pull-credit transaction + // also retries the withdrawal task. + + logger.warn( + "peer pull payment initiation is already finished, retrying withdrawal", + ); + + const withdrawalGroupId = pullIni.withdrawalGroupId; + + if (withdrawalGroupId) { + const taskId = constructTaskIdentifier({ + tag: PendingTaskType.Withdraw, + withdrawalGroupId, + }); + stopLongpolling(ws, taskId); + await resetOperationTimeout(ws, taskId); + await runOperationWithErrorReporting(ws, taskId, () => + processWithdrawalGroup(ws, withdrawalGroupId), + ); + } + return { + type: OperationAttemptResultType.Finished, + result: undefined, + }; + } + case PeerPullPaymentInitiationStatus.PendingReady: + runLongpollAsync(ws, retryTag, async (cancellationToken) => + queryPurseForPeerPullCredit(ws, pullIni, cancellationToken), + ); + logger.trace( + "returning early from processPeerPullCredit for long-polling in background", + ); + return { + type: OperationAttemptResultType.Longpoll, + }; + case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: { + if (!pullIni.kycInfo) { + throw Error("invalid state, kycInfo required"); + } + return await longpollKycStatus( + ws, + pursePub, + pullIni.exchangeBaseUrl, + pullIni.kycInfo, + "individual", + ); + } + case PeerPullPaymentInitiationStatus.PendingCreatePurse: + return handlePeerPullCreditCreatePurse(ws, pullIni); + case PeerPullPaymentInitiationStatus.AbortingDeletePurse: + return await processPeerPullCreditAbortingDeletePurse(ws, pullIni); + case PeerPullPaymentInitiationStatus.PendingWithdrawing: + return handlePeerPullCreditWithdrawing(ws, pullIni); + case PeerPullPaymentInitiationStatus.Aborted: + case PeerPullPaymentInitiationStatus.Failed: + case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse: + case PeerPullPaymentInitiationStatus.SuspendedCreatePurse: + case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired: + case PeerPullPaymentInitiationStatus.SuspendedReady: + case PeerPullPaymentInitiationStatus.SuspendedWithdrawing: + break; + default: + assertUnreachable(pullIni.status); + } + + return OperationAttemptResult.finishedEmpty(); +} + async function processPeerPullCreditKycRequired( ws: InternalWalletState, peerIni: PeerPullPaymentInitiationRecord, @@ -789,7 +869,7 @@ export async function suspendPeerPullCreditTransaction( newStatus = PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse; break; - case PeerPullPaymentInitiationStatus.DonePurseDeposited: + case PeerPullPaymentInitiationStatus.Done: case PeerPullPaymentInitiationStatus.SuspendedCreatePurse: case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired: case PeerPullPaymentInitiationStatus.SuspendedReady: @@ -848,7 +928,7 @@ export async function abortPeerPullCreditTransaction( case PeerPullPaymentInitiationStatus.PendingReady: newStatus = PeerPullPaymentInitiationStatus.AbortingDeletePurse; break; - case PeerPullPaymentInitiationStatus.DonePurseDeposited: + case PeerPullPaymentInitiationStatus.Done: case PeerPullPaymentInitiationStatus.SuspendedCreatePurse: case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired: case PeerPullPaymentInitiationStatus.SuspendedReady: @@ -903,7 +983,7 @@ export async function failPeerPullCreditTransaction( case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: case PeerPullPaymentInitiationStatus.PendingWithdrawing: case PeerPullPaymentInitiationStatus.PendingReady: - case PeerPullPaymentInitiationStatus.DonePurseDeposited: + case PeerPullPaymentInitiationStatus.Done: case PeerPullPaymentInitiationStatus.SuspendedCreatePurse: case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired: case PeerPullPaymentInitiationStatus.SuspendedReady: @@ -961,7 +1041,7 @@ export async function resumePeerPullCreditTransaction( case PeerPullPaymentInitiationStatus.PendingWithdrawing: case PeerPullPaymentInitiationStatus.PendingReady: case PeerPullPaymentInitiationStatus.AbortingDeletePurse: - case PeerPullPaymentInitiationStatus.DonePurseDeposited: + case PeerPullPaymentInitiationStatus.Done: case PeerPullPaymentInitiationStatus.Failed: case PeerPullPaymentInitiationStatus.Aborted: break; @@ -1018,7 +1098,7 @@ export function computePeerPullCreditTransactionState( major: TransactionMajorState.Pending, minor: TransactionMinorState.Ready, }; - case PeerPullPaymentInitiationStatus.DonePurseDeposited: + case PeerPullPaymentInitiationStatus.Done: return { major: TransactionMajorState.Done, }; @@ -1078,7 +1158,7 @@ export function computePeerPullCreditTransactionActions( return [TransactionAction.Abort, TransactionAction.Suspend]; case PeerPullPaymentInitiationStatus.PendingReady: return [TransactionAction.Abort, TransactionAction.Suspend]; - case PeerPullPaymentInitiationStatus.DonePurseDeposited: + case PeerPullPaymentInitiationStatus.Done: return [TransactionAction.Delete]; case PeerPullPaymentInitiationStatus.PendingWithdrawing: return [TransactionAction.Abort, TransactionAction.Suspend]; |