From c493a3069ec330b955efd8604cb9d8b76277c74e Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Mon, 13 Dec 2021 11:28:15 +0100 Subject: wallet-core: reset reserve retry when resetting withdrawal retry --- packages/taler-wallet-core/src/common.ts | 11 ++++- .../taler-wallet-core/src/operations/reserves.ts | 53 +++++++++++++++++----- .../src/operations/transactions.ts | 34 +++++++++----- .../taler-wallet-core/src/operations/withdraw.ts | 27 +++++++++-- packages/taler-wallet-core/src/wallet.ts | 5 ++ 5 files changed, 102 insertions(+), 28 deletions(-) diff --git a/packages/taler-wallet-core/src/common.ts b/packages/taler-wallet-core/src/common.ts index d3c4a5229..5b61ef14a 100644 --- a/packages/taler-wallet-core/src/common.ts +++ b/packages/taler-wallet-core/src/common.ts @@ -51,7 +51,7 @@ export interface TrustInfo { isAudited: boolean; } -export interface MerchantInfo { +export interface MerchantInfo { protocolVersionCurrent: number; } @@ -65,6 +65,14 @@ export interface MerchantOperations { ): Promise; } +export interface ReserveOperations { + processReserve( + ws: InternalWalletState, + reservePub: string, + forceNow?: boolean, + ): Promise; +} + /** * Interface for exchange-related operations. */ @@ -152,6 +160,7 @@ export interface InternalWalletState { exchangeOps: ExchangeOperations; recoupOps: RecoupOperations; merchantOps: MerchantOperations; + reserveOps: ReserveOperations; db: DbAccess; http: HttpRequestLibrary; diff --git a/packages/taler-wallet-core/src/operations/reserves.ts b/packages/taler-wallet-core/src/operations/reserves.ts index 43e0d7d36..5a9fbb405 100644 --- a/packages/taler-wallet-core/src/operations/reserves.ts +++ b/packages/taler-wallet-core/src/operations/reserves.ts @@ -15,35 +15,63 @@ */ import { - AcceptWithdrawalResponse, addPaytoQueryParams, Amounts, canonicalizeBaseUrl, codecForBankWithdrawalOperationPostResponse, + AcceptWithdrawalResponse, + addPaytoQueryParams, + Amounts, + canonicalizeBaseUrl, + codecForBankWithdrawalOperationPostResponse, codecForReserveStatus, - codecForWithdrawOperationStatusResponse, CreateReserveRequest, - CreateReserveResponse, Duration, + codecForWithdrawOperationStatusResponse, + CreateReserveRequest, + CreateReserveResponse, + Duration, durationMax, - durationMin, encodeCrock, getRandomBytes, getTimestampNow, Logger, NotificationType, randomBytes, ReserveTransactionType, - TalerErrorCode, TalerErrorDetails, URL + durationMin, + encodeCrock, + getRandomBytes, + getTimestampNow, + Logger, + NotificationType, + randomBytes, + ReserveTransactionType, + TalerErrorCode, + TalerErrorDetails, + URL, } from "@gnu-taler/taler-util"; import { InternalWalletState } from "../common.js"; import { ReserveBankInfo, - ReserveRecord, ReserveRecordStatus, WalletStoresV1, WithdrawalGroupRecord + ReserveRecord, + ReserveRecordStatus, + WalletStoresV1, + WithdrawalGroupRecord, } from "../db.js"; import { guardOperationException, OperationFailedError } from "../errors.js"; import { assertUnreachable } from "../util/assertUnreachable.js"; import { readSuccessResponseJsonOrErrorCode, readSuccessResponseJsonOrThrow, - throwUnexpectedRequestError + throwUnexpectedRequestError, } from "../util/http.js"; import { GetReadOnlyAccess } from "../util/query.js"; import { - getRetryDuration, initRetryInfo, updateRetryInfoTimeout + getRetryDuration, + initRetryInfo, + updateRetryInfoTimeout, } from "../util/retries.js"; import { - getExchangeDetails, getExchangePaytoUri, getExchangeTrust, updateExchangeFromUrl + getExchangeDetails, + getExchangePaytoUri, + getExchangeTrust, + updateExchangeFromUrl, } from "./exchanges.js"; import { - denomSelectionInfoToState, getBankWithdrawalInfo, getCandidateWithdrawalDenoms, processWithdrawGroup, selectWithdrawalDenominations, updateWithdrawalDenoms + denomSelectionInfoToState, + getBankWithdrawalInfo, + getCandidateWithdrawalDenoms, + processWithdrawGroup, + selectWithdrawalDenominations, + updateWithdrawalDenoms, } from "./withdraw.js"; const logger = new Logger("reserves.ts"); @@ -514,7 +542,7 @@ async function updateReserve( if ( resp.status === 404 && result.talerErrorResponse.code === - TalerErrorCode.EXCHANGE_RESERVES_GET_STATUS_UNKNOWN + TalerErrorCode.EXCHANGE_RESERVES_GET_STATUS_UNKNOWN ) { ws.notify({ type: NotificationType.ReserveNotYetFound, @@ -617,7 +645,8 @@ async function updateReserve( logger.trace( `Remaining unclaimed amount in reseve is ${Amounts.stringify( remainingAmount, - )} and can be withdrawn with ${denomSelInfo.selectedDenoms.length + )} and can be withdrawn with ${ + denomSelInfo.selectedDenoms.length } coins`, ); diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index b00779fa2..106d72d02 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -19,12 +19,25 @@ */ import { AmountJson, - Amounts, OrderShortInfo, PaymentStatus, timestampCmp, Transaction, TransactionsRequest, - TransactionsResponse, TransactionType, WithdrawalDetails, WithdrawalType + Amounts, + Logger, + OrderShortInfo, + PaymentStatus, + timestampCmp, + Transaction, + TransactionsRequest, + TransactionsResponse, + TransactionType, + WithdrawalDetails, + WithdrawalType, } from "@gnu-taler/taler-util"; import { InternalWalletState } from "../common.js"; import { - AbortStatus, RefundState, ReserveRecord, ReserveRecordStatus, WalletRefundItem + AbortStatus, + RefundState, + ReserveRecord, + ReserveRecordStatus, + WalletRefundItem, } from "../db.js"; import { processDepositGroup } from "./deposits.js"; import { getExchangeDetails } from "./exchanges.js"; @@ -34,6 +47,8 @@ import { getFundingPaytoUris } from "./reserves.js"; import { processTip } from "./tip.js"; import { processWithdrawGroup } from "./withdraw.js"; +const logger = new Logger("taler-wallet-core:transactions.ts"); + /** * Create an event ID from the type and the primary key for the event. */ @@ -404,13 +419,6 @@ export enum TombstoneTag { DeleteRefund = "delete-refund", } -export async function retryTransactionNow( - ws: InternalWalletState, - transactionId: string, -): Promise { - const [type, ...rest] = transactionId.split(":"); -} - /** * Immediately retry the underlying operation * of a transaction. @@ -419,6 +427,8 @@ export async function retryTransaction( ws: InternalWalletState, transactionId: string, ): Promise { + logger.info(`retrying transaction ${transactionId}`); + const [type, ...rest] = transactionId.split(":"); switch (type) { @@ -478,8 +488,8 @@ export async function deleteTransaction( const reserveRecord: | ReserveRecord | undefined = await tx.reserves.indexes.byInitialWithdrawalGroupId.get( - withdrawalGroupId, - ); + withdrawalGroupId, + ); if (reserveRecord && !reserveRecord.initialWithdrawalStarted) { const reservePub = reserveRecord.reservePub; await tx.reserves.delete(reservePub); diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 48d308b60..652b26c1e 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -854,7 +854,10 @@ async function resetWithdrawalGroupRetry( withdrawalGroupId: string, ): Promise { await ws.db - .mktx((x) => ({ withdrawalGroups: x.withdrawalGroups })) + .mktx((x) => ({ + withdrawalGroups: x.withdrawalGroups, + reserves: x.reserves, + })) .runReadWrite(async (tx) => { const x = await tx.withdrawalGroups.get(withdrawalGroupId); if (x) { @@ -879,8 +882,26 @@ async function processWithdrawGroupImpl( return tx.withdrawalGroups.get(withdrawalGroupId); }); if (!withdrawalGroup) { - logger.trace("withdraw session doesn't exist"); - return; + // Withdrawal group doesn't exist yet, but reserve might exist + // (and reference the yet to be created withdrawal group) + const reservePub = await ws.db + .mktx((x) => ({ reserves: x.reserves })) + .runReadOnly(async (tx) => { + const r = await tx.reserves.indexes.byInitialWithdrawalGroupId.get( + withdrawalGroupId, + ); + if (r) { + return r.reservePub; + } + return undefined; + }); + if (!reservePub) { + logger.warn( + "withdrawal group doesn't exist (and reserve doesn't exist either)", + ); + return; + } + return await ws.reserveOps.processReserve(ws, reservePub, forceNow); } await ws.exchangeOps.updateExchangeFromUrl( diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 7e7980795..ed0046c59 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -103,6 +103,7 @@ import { MerchantOperations, NotificationListener, RecoupOperations, + ReserveOperations, } from "./common.js"; import { runIntegrationTest, @@ -1122,6 +1123,10 @@ class InternalWalletStateImpl implements InternalWalletState { getMerchantInfo: getMerchantInfo, }; + reserveOps: ReserveOperations = { + processReserve: processReserve, + } + /** * Promises that are waiting for a particular resource. */ -- cgit v1.2.3