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

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

View File

@ -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`,
);

View File

@ -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<void> {
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<void> {
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);

View File

@ -854,7 +854,10 @@ async function resetWithdrawalGroupRetry(
withdrawalGroupId: string,
): Promise<void> {
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(

View File

@ -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.
*/