From 16d30adf0d57f6d954230c437e56e8a8700ef2ae Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 2 May 2023 10:59:50 +0200 Subject: -withdrawal notifications --- .../taler-wallet-core/src/operations/withdraw.ts | 205 +++++++++++++-------- 1 file changed, 131 insertions(+), 74 deletions(-) (limited to 'packages/taler-wallet-core/src/operations/withdraw.ts') diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 3f3eb3784..d1816de03 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -132,6 +132,7 @@ import { import { PendingTaskType, isWithdrawableDenom } from "../index.js"; import { constructTransactionIdentifier, + notifyTransition, stopLongpolling, } from "./transactions.js"; @@ -149,7 +150,7 @@ export async function suspendWithdrawalTransaction( withdrawalGroupId, }); stopLongpolling(ws, taskId); - const stateUpdate = await ws.db + const transitionInfo = await ws.db .mktx((x) => [x.withdrawalGroups]) .runReadWrite(async (tx) => { const wg = await tx.withdrawalGroups.get(withdrawalGroupId); @@ -198,24 +199,18 @@ export async function suspendWithdrawalTransaction( return undefined; }); - if (stateUpdate) { - ws.notify({ - type: NotificationType.TransactionStateTransition, - transactionId: constructTransactionIdentifier({ - tag: TransactionType.Withdrawal, - withdrawalGroupId, - }), - oldTxState: stateUpdate.oldTxState, - newTxState: stateUpdate.newTxState, - }); - } + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.Withdrawal, + withdrawalGroupId, + }); + notifyTransition(ws, transactionId, transitionInfo); } export async function resumeWithdrawalTransaction( ws: InternalWalletState, withdrawalGroupId: string, ) { - const stateUpdate = await ws.db + const transitionInfo = await ws.db .mktx((x) => [x.withdrawalGroups]) .runReadWrite(async (tx) => { const wg = await tx.withdrawalGroups.get(withdrawalGroupId); @@ -264,17 +259,11 @@ export async function resumeWithdrawalTransaction( return undefined; }); - if (stateUpdate) { - ws.notify({ - type: NotificationType.TransactionStateTransition, - transactionId: constructTransactionIdentifier({ - tag: TransactionType.Withdrawal, - withdrawalGroupId, - }), - oldTxState: stateUpdate.oldTxState, - newTxState: stateUpdate.newTxState, - }); - } + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.Withdrawal, + withdrawalGroupId, + }); + notifyTransition(ws, transactionId, transitionInfo); } export async function abortWithdrawalTransaction( @@ -285,8 +274,12 @@ export async function abortWithdrawalTransaction( tag: PendingTaskType.Withdraw, withdrawalGroupId, }); + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.Withdrawal, + withdrawalGroupId, + }); stopLongpolling(ws, taskId); - const stateUpdate = await ws.db + const transitionInfo = await ws.db .mktx((x) => [x.withdrawalGroups]) .runReadWrite(async (tx) => { const wg = await tx.withdrawalGroups.get(withdrawalGroupId); @@ -339,18 +332,7 @@ export async function abortWithdrawalTransaction( } return undefined; }); - - if (stateUpdate) { - ws.notify({ - type: NotificationType.TransactionStateTransition, - transactionId: constructTransactionIdentifier({ - tag: TransactionType.Withdrawal, - withdrawalGroupId, - }), - oldTxState: stateUpdate.oldTxState, - newTxState: stateUpdate.newTxState, - }); - } + notifyTransition(ws, transactionId, transitionInfo); } // Called "cancel" in the spec right now, @@ -363,6 +345,10 @@ export async function cancelAbortingWithdrawalTransaction( tag: PendingTaskType.Withdraw, withdrawalGroupId, }); + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.Withdrawal, + withdrawalGroupId, + }); stopLongpolling(ws, taskId); const stateUpdate = await ws.db .mktx((x) => [x.withdrawalGroups]) @@ -392,21 +378,9 @@ export async function cancelAbortingWithdrawalTransaction( } return undefined; }); - - if (stateUpdate) { - ws.notify({ - type: NotificationType.TransactionStateTransition, - transactionId: constructTransactionIdentifier({ - tag: TransactionType.Withdrawal, - withdrawalGroupId, - }), - oldTxState: stateUpdate.oldTxState, - newTxState: stateUpdate.newTxState, - }); - } + notifyTransition(ws, transactionId, stateUpdate); } - export function computeWithdrawalTransactionStatus( wgRecord: WithdrawalGroupRecord, ): TransactionState { @@ -1140,6 +1114,10 @@ async function queryReserve( withdrawalGroupId: string, cancellationToken: CancellationToken, ): Promise<{ ready: boolean }> { + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.Withdrawal, + withdrawalGroupId, + }); const withdrawalGroup = await getWithdrawalGroupRecordTx(ws.db, { withdrawalGroupId, }); @@ -1190,25 +1168,31 @@ async function queryReserve( logger.trace(`got reserve status ${j2s(result.response)}`); - await ws.db + const transitionResult = 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; + return undefined; } + const txStateOld = computeWithdrawalTransactionStatus(wg); wg.status = WithdrawalGroupStatus.Ready; + const txStateNew = computeWithdrawalTransactionStatus(wg); wg.reserveBalanceAmount = Amounts.stringify(result.response.balance); await tx.withdrawalGroups.put(wg); + return { + oldTxState: txStateOld, + newTxState: txStateNew, + }; }); + notifyTransition(ws, transactionId, transitionResult); + + // FIXME: This notification is deprecated with DD37 ws.notify({ type: NotificationType.WithdrawalGroupReserveReady, - transactionId: makeTransactionId( - TransactionType.Withdrawal, - withdrawalGroupId, - ), + transactionId, }); return { ready: true }; @@ -1252,6 +1236,10 @@ export async function processWithdrawalGroup( } const retryTag = TaskIdentifiers.forWithdrawal(withdrawalGroup); + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.Withdrawal, + withdrawalGroupId, + }); // We're already running! if (ws.activeLongpoll[retryTag]) { @@ -1322,17 +1310,24 @@ export async function processWithdrawalGroup( if (withdrawalGroup.denomsSel.selectedDenoms.length === 0) { logger.warn("Finishing empty withdrawal group (no denoms)"); - await ws.db + const transitionInfo = await ws.db .mktx((x) => [x.withdrawalGroups]) .runReadWrite(async (tx) => { const wg = await tx.withdrawalGroups.get(withdrawalGroupId); if (!wg) { - return; + return undefined; } + const txStatusOld = computeWithdrawalTransactionStatus(wg); wg.status = WithdrawalGroupStatus.Finished; wg.timestampFinish = TalerProtocolTimestamp.now(); + const txStatusNew = computeWithdrawalTransactionStatus(wg); await tx.withdrawalGroups.put(wg); + return { + oldTxState: txStatusOld, + newTxState: txStatusNew, + }; }); + notifyTransition(ws, transactionId, transitionInfo); return { type: OperationAttemptResultType.Finished, result: undefined, @@ -1421,6 +1416,7 @@ export async function processWithdrawalGroup( errorsPerCoin[x.coinIdx] = x.lastError; } }); + const oldTxState = computeWithdrawalTransactionStatus(wg); logger.info(`now withdrawn ${numFinished} of ${numTotalCoins} coins`); if (wg.timestampFinish === undefined && numFinished === numTotalCoins) { finishedForFirstTime = true; @@ -1428,10 +1424,15 @@ export async function processWithdrawalGroup( wg.status = WithdrawalGroupStatus.Finished; } + const newTxState = computeWithdrawalTransactionStatus(wg); await tx.withdrawalGroups.put(wg); return { kycInfo: wg.kycPending, + transitionInfo: { + oldTxState, + newTxState, + }, }; }); @@ -1439,6 +1440,8 @@ export async function processWithdrawalGroup( throw Error("withdrawal group does not exist anymore"); } + notifyTransition(ws, transactionId, res.transitionInfo); + const { kycInfo } = res; if (numKycRequired > 0) { @@ -1478,6 +1481,7 @@ export async function processWithdrawalGroup( ); } + // FIXME: Deprecated with DD37 if (finishedForFirstTime) { ws.notify({ type: NotificationType.WithdrawGroupFinished, @@ -1838,6 +1842,10 @@ async function registerReserveWithBank( .runReadOnly(async (tx) => { return await tx.withdrawalGroups.get(withdrawalGroupId); }); + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.Withdrawal, + withdrawalGroupId, + }); switch (withdrawalGroup?.status) { case WithdrawalGroupStatus.WaitConfirmBank: case WithdrawalGroupStatus.RegisteringBank: @@ -1860,19 +1868,21 @@ async function registerReserveWithBank( selected_exchange: bankInfo.exchangePaytoUri, }; logger.info(`registering reserve with bank: ${j2s(reqBody)}`); - const httpResp = await ws.http.postJson(bankStatusUrl, reqBody, { + const httpResp = await ws.http.fetch(bankStatusUrl, { + method: "POST", + body: reqBody, timeout: getReserveRequestTimeout(withdrawalGroup), }); await readSuccessResponseJsonOrThrow( httpResp, codecForBankWithdrawalOperationPostResponse(), ); - await ws.db + const transitionInfo = await ws.db .mktx((x) => [x.withdrawalGroups]) .runReadWrite(async (tx) => { const r = await tx.withdrawalGroups.get(withdrawalGroupId); if (!r) { - return; + return undefined; } switch (r.status) { case WithdrawalGroupStatus.RegisteringBank: @@ -1887,9 +1897,18 @@ async function registerReserveWithBank( r.wgInfo.bankInfo.timestampReserveInfoPosted = AbsoluteTime.toTimestamp( AbsoluteTime.now(), ); + const oldTxState = computeWithdrawalTransactionStatus(r); r.status = WithdrawalGroupStatus.WaitConfirmBank; + const newTxState = computeWithdrawalTransactionStatus(r); await tx.withdrawalGroups.put(r); + return { + oldTxState, + newTxState, + }; }); + + notifyTransition(ws, transactionId, transitionInfo); + // FIXME: This notification is deprecated with DD37 ws.notify({ type: NotificationType.ReserveRegisteredWithBank }); } @@ -1904,6 +1923,10 @@ async function processReserveBankStatus( const withdrawalGroup = await getWithdrawalGroupRecordTx(ws.db, { withdrawalGroupId, }); + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.Withdrawal, + withdrawalGroupId, + }); switch (withdrawalGroup?.status) { case WithdrawalGroupStatus.WaitConfirmBank: case WithdrawalGroupStatus.RegisteringBank: @@ -1938,7 +1961,7 @@ async function processReserveBankStatus( if (status.aborted) { logger.info("bank aborted the withdrawal"); - await ws.db + const transitionInfo = await ws.db .mktx((x) => [x.withdrawalGroups]) .runReadWrite(async (tx) => { const r = await tx.withdrawalGroups.get(withdrawalGroupId); @@ -1956,10 +1979,17 @@ async function processReserveBankStatus( throw Error("invariant failed"); } const now = AbsoluteTime.toTimestamp(AbsoluteTime.now()); + const oldTxState = computeWithdrawalTransactionStatus(r); r.wgInfo.bankInfo.timestampBankConfirmed = now; r.status = WithdrawalGroupStatus.BankAborted; + const newTxState = computeWithdrawalTransactionStatus(r); await tx.withdrawalGroups.put(r); + return { + oldTxState, + newTxState, + } }); + notifyTransition(ws, transactionId, transitionInfo); return { status: BankStatusResultCode.Aborted, }; @@ -1977,12 +2007,12 @@ async function processReserveBankStatus( return await processReserveBankStatus(ws, withdrawalGroupId); } - await ws.db + const transitionInfo = await ws.db .mktx((x) => [x.withdrawalGroups]) .runReadWrite(async (tx) => { const r = await tx.withdrawalGroups.get(withdrawalGroupId); if (!r) { - return; + return undefined; } // Re-check reserve status within transaction switch (r.status) { @@ -1990,16 +2020,18 @@ async function processReserveBankStatus( case WithdrawalGroupStatus.WaitConfirmBank: break; default: - return; + return undefined; } if (r.wgInfo.withdrawalType !== WithdrawalRecordType.BankIntegrated) { throw Error("invariant failed"); } + const oldTxState = computeWithdrawalTransactionStatus(r); if (status.transfer_done) { logger.info("withdrawal: transfer confirmed by bank."); const now = AbsoluteTime.toTimestamp(AbsoluteTime.now()); r.wgInfo.bankInfo.timestampBankConfirmed = now; r.status = WithdrawalGroupStatus.QueryingStatus; + // FIXME: Notification is deprecated with DD37. ws.notify({ type: NotificationType.WithdrawalGroupBankConfirmed, transactionId: makeTransactionId( @@ -2012,9 +2044,16 @@ async function processReserveBankStatus( r.wgInfo.bankInfo.confirmUrl = status.confirm_transfer_url; r.senderWire = status.sender_wire; } + const newTxState = computeWithdrawalTransactionStatus(r); await tx.withdrawalGroups.put(r); + return { + oldTxState, + newTxState, + } }); + notifyTransition(ws, transactionId, transitionInfo); + if (status.transfer_done) { return { status: BankStatusResultCode.Done, @@ -2071,6 +2110,11 @@ export async function internalCreateWithdrawalGroup( withdrawalGroupId = encodeCrock(getRandomBytes(32)); } + const transactionId = constructTransactionIdentifier({ + tag: TransactionType.Withdrawal, + withdrawalGroupId, + }); + await updateWithdrawalDenoms(ws, canonExchange); const denoms = await getCandidateWithdrawalDenoms(ws, canonExchange); @@ -2122,7 +2166,7 @@ export async function internalCreateWithdrawalGroup( exchangeInfo.exchange, ); - await ws.db + const transitionInfo = await ws.db .mktx((x) => [ x.withdrawalGroups, x.reserves, @@ -2151,8 +2195,19 @@ export async function internalCreateWithdrawalGroup( uids: [encodeCrock(getRandomBytes(32))], }); } + + const oldTxState = { + major: TransactionMajorState.None, + } + const newTxState = computeWithdrawalTransactionStatus(withdrawalGroup); + return { + oldTxState, + newTxState, + } }); + notifyTransition(ws, transactionId, transitionInfo); + return withdrawalGroup; } @@ -2225,6 +2280,10 @@ export async function acceptWithdrawalFromUri( }); const withdrawalGroupId = withdrawalGroup.withdrawalGroupId; + const transactionId = constructTaskIdentifier({ + tag: PendingTaskType.Withdraw, + withdrawalGroupId, + }); // We do this here, as the reserve should be registered before we return, // so that we can redirect the user to the bank's status page. @@ -2249,10 +2308,7 @@ export async function acceptWithdrawalFromUri( return { reservePub: withdrawalGroup.reservePub, confirmTransferUrl: withdrawInfo.confirmTransferUrl, - transactionId: makeTransactionId( - TransactionType.Withdrawal, - withdrawalGroupId, - ), + transactionId, }; } @@ -2285,6 +2341,10 @@ export async function createManualWithdrawal( }); const withdrawalGroupId = withdrawalGroup.withdrawalGroupId; + const transactionId = constructTaskIdentifier({ + tag: PendingTaskType.Withdraw, + withdrawalGroupId, + }); const exchangePaytoUris = await ws.db .mktx((x) => [ @@ -2313,9 +2373,6 @@ export async function createManualWithdrawal( return { reservePub: withdrawalGroup.reservePub, exchangePaytoUris: exchangePaytoUris, - transactionId: makeTransactionId( - TransactionType.Withdrawal, - withdrawalGroupId, - ), + transactionId, }; } -- cgit v1.2.3