wallet-core: reset reserve retry when resetting withdrawal retry

This commit is contained in:
Florian Dold 2021-12-13 11:28:15 +01:00
parent 38d8239f93
commit c493a3069e
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
5 changed files with 102 additions and 28 deletions

View File

@ -65,6 +65,14 @@ export interface MerchantOperations {
): Promise<MerchantInfo>; ): Promise<MerchantInfo>;
} }
export interface ReserveOperations {
processReserve(
ws: InternalWalletState,
reservePub: string,
forceNow?: boolean,
): Promise<void>;
}
/** /**
* Interface for exchange-related operations. * Interface for exchange-related operations.
*/ */
@ -152,6 +160,7 @@ export interface InternalWalletState {
exchangeOps: ExchangeOperations; exchangeOps: ExchangeOperations;
recoupOps: RecoupOperations; recoupOps: RecoupOperations;
merchantOps: MerchantOperations; merchantOps: MerchantOperations;
reserveOps: ReserveOperations;
db: DbAccess<typeof WalletStoresV1>; db: DbAccess<typeof WalletStoresV1>;
http: HttpRequestLibrary; http: HttpRequestLibrary;

View File

@ -15,35 +15,63 @@
*/ */
import { import {
AcceptWithdrawalResponse, addPaytoQueryParams, Amounts, canonicalizeBaseUrl, codecForBankWithdrawalOperationPostResponse, AcceptWithdrawalResponse,
addPaytoQueryParams,
Amounts,
canonicalizeBaseUrl,
codecForBankWithdrawalOperationPostResponse,
codecForReserveStatus, codecForReserveStatus,
codecForWithdrawOperationStatusResponse, CreateReserveRequest, codecForWithdrawOperationStatusResponse,
CreateReserveResponse, Duration, CreateReserveRequest,
CreateReserveResponse,
Duration,
durationMax, durationMax,
durationMin, encodeCrock, getRandomBytes, getTimestampNow, Logger, NotificationType, randomBytes, ReserveTransactionType, durationMin,
TalerErrorCode, TalerErrorDetails, URL encodeCrock,
getRandomBytes,
getTimestampNow,
Logger,
NotificationType,
randomBytes,
ReserveTransactionType,
TalerErrorCode,
TalerErrorDetails,
URL,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { InternalWalletState } from "../common.js"; import { InternalWalletState } from "../common.js";
import { import {
ReserveBankInfo, ReserveBankInfo,
ReserveRecord, ReserveRecordStatus, WalletStoresV1, WithdrawalGroupRecord ReserveRecord,
ReserveRecordStatus,
WalletStoresV1,
WithdrawalGroupRecord,
} from "../db.js"; } from "../db.js";
import { guardOperationException, OperationFailedError } from "../errors.js"; import { guardOperationException, OperationFailedError } from "../errors.js";
import { assertUnreachable } from "../util/assertUnreachable.js"; import { assertUnreachable } from "../util/assertUnreachable.js";
import { import {
readSuccessResponseJsonOrErrorCode, readSuccessResponseJsonOrErrorCode,
readSuccessResponseJsonOrThrow, readSuccessResponseJsonOrThrow,
throwUnexpectedRequestError throwUnexpectedRequestError,
} from "../util/http.js"; } from "../util/http.js";
import { GetReadOnlyAccess } from "../util/query.js"; import { GetReadOnlyAccess } from "../util/query.js";
import { import {
getRetryDuration, initRetryInfo, updateRetryInfoTimeout getRetryDuration,
initRetryInfo,
updateRetryInfoTimeout,
} from "../util/retries.js"; } from "../util/retries.js";
import { import {
getExchangeDetails, getExchangePaytoUri, getExchangeTrust, updateExchangeFromUrl getExchangeDetails,
getExchangePaytoUri,
getExchangeTrust,
updateExchangeFromUrl,
} from "./exchanges.js"; } from "./exchanges.js";
import { import {
denomSelectionInfoToState, getBankWithdrawalInfo, getCandidateWithdrawalDenoms, processWithdrawGroup, selectWithdrawalDenominations, updateWithdrawalDenoms denomSelectionInfoToState,
getBankWithdrawalInfo,
getCandidateWithdrawalDenoms,
processWithdrawGroup,
selectWithdrawalDenominations,
updateWithdrawalDenoms,
} from "./withdraw.js"; } from "./withdraw.js";
const logger = new Logger("reserves.ts"); const logger = new Logger("reserves.ts");
@ -617,7 +645,8 @@ async function updateReserve(
logger.trace( logger.trace(
`Remaining unclaimed amount in reseve is ${Amounts.stringify( `Remaining unclaimed amount in reseve is ${Amounts.stringify(
remainingAmount, remainingAmount,
)} and can be withdrawn with ${denomSelInfo.selectedDenoms.length )} and can be withdrawn with ${
denomSelInfo.selectedDenoms.length
} coins`, } coins`,
); );

View File

@ -19,12 +19,25 @@
*/ */
import { import {
AmountJson, AmountJson,
Amounts, OrderShortInfo, PaymentStatus, timestampCmp, Transaction, TransactionsRequest, Amounts,
TransactionsResponse, TransactionType, WithdrawalDetails, WithdrawalType Logger,
OrderShortInfo,
PaymentStatus,
timestampCmp,
Transaction,
TransactionsRequest,
TransactionsResponse,
TransactionType,
WithdrawalDetails,
WithdrawalType,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { InternalWalletState } from "../common.js"; import { InternalWalletState } from "../common.js";
import { import {
AbortStatus, RefundState, ReserveRecord, ReserveRecordStatus, WalletRefundItem AbortStatus,
RefundState,
ReserveRecord,
ReserveRecordStatus,
WalletRefundItem,
} from "../db.js"; } from "../db.js";
import { processDepositGroup } from "./deposits.js"; import { processDepositGroup } from "./deposits.js";
import { getExchangeDetails } from "./exchanges.js"; import { getExchangeDetails } from "./exchanges.js";
@ -34,6 +47,8 @@ import { getFundingPaytoUris } from "./reserves.js";
import { processTip } from "./tip.js"; import { processTip } from "./tip.js";
import { processWithdrawGroup } from "./withdraw.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. * Create an event ID from the type and the primary key for the event.
*/ */
@ -404,13 +419,6 @@ export enum TombstoneTag {
DeleteRefund = "delete-refund", DeleteRefund = "delete-refund",
} }
export async function retryTransactionNow(
ws: InternalWalletState,
transactionId: string,
): Promise<void> {
const [type, ...rest] = transactionId.split(":");
}
/** /**
* Immediately retry the underlying operation * Immediately retry the underlying operation
* of a transaction. * of a transaction.
@ -419,6 +427,8 @@ export async function retryTransaction(
ws: InternalWalletState, ws: InternalWalletState,
transactionId: string, transactionId: string,
): Promise<void> { ): Promise<void> {
logger.info(`retrying transaction ${transactionId}`);
const [type, ...rest] = transactionId.split(":"); const [type, ...rest] = transactionId.split(":");
switch (type) { switch (type) {

View File

@ -854,7 +854,10 @@ async function resetWithdrawalGroupRetry(
withdrawalGroupId: string, withdrawalGroupId: string,
): Promise<void> { ): Promise<void> {
await ws.db await ws.db
.mktx((x) => ({ withdrawalGroups: x.withdrawalGroups })) .mktx((x) => ({
withdrawalGroups: x.withdrawalGroups,
reserves: x.reserves,
}))
.runReadWrite(async (tx) => { .runReadWrite(async (tx) => {
const x = await tx.withdrawalGroups.get(withdrawalGroupId); const x = await tx.withdrawalGroups.get(withdrawalGroupId);
if (x) { if (x) {
@ -879,9 +882,27 @@ async function processWithdrawGroupImpl(
return tx.withdrawalGroups.get(withdrawalGroupId); return tx.withdrawalGroups.get(withdrawalGroupId);
}); });
if (!withdrawalGroup) { if (!withdrawalGroup) {
logger.trace("withdraw session doesn't exist"); // 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;
} }
return await ws.reserveOps.processReserve(ws, reservePub, forceNow);
}
await ws.exchangeOps.updateExchangeFromUrl( await ws.exchangeOps.updateExchangeFromUrl(
ws, ws,

View File

@ -103,6 +103,7 @@ import {
MerchantOperations, MerchantOperations,
NotificationListener, NotificationListener,
RecoupOperations, RecoupOperations,
ReserveOperations,
} from "./common.js"; } from "./common.js";
import { import {
runIntegrationTest, runIntegrationTest,
@ -1122,6 +1123,10 @@ class InternalWalletStateImpl implements InternalWalletState {
getMerchantInfo: getMerchantInfo, getMerchantInfo: getMerchantInfo,
}; };
reserveOps: ReserveOperations = {
processReserve: processReserve,
}
/** /**
* Promises that are waiting for a particular resource. * Promises that are waiting for a particular resource.
*/ */