all retryInfo function in the same namespace, adding missing retryInfo increment

This commit is contained in:
Sebastian 2022-05-18 14:41:51 -03:00
parent d3a857743d
commit c67d0bff1d
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
14 changed files with 213 additions and 311 deletions

View File

@ -120,7 +120,10 @@ export namespace BankApi {
if (respJson.paytoUri) { if (respJson.paytoUri) {
paytoUri = respJson.paytoUri; paytoUri = respJson.paytoUri;
} }
} catch (e) {} } catch (e) {
logger.error("error trying to parse json from response", e);
throw TalerError.fromException(e);
}
return { return {
password, password,
username, username,

View File

@ -15,54 +15,30 @@
*/ */
import { import {
BackupPurchase,
AmountJson, AmountJson,
Amounts, Amounts, BackupCoinSourceType, BackupDenomSel, BackupProposalStatus,
BackupDenomSel, BackupPurchase, BackupRefreshReason, BackupRefundState, codecForContractTerms,
WalletBackupContentV1, DenomKeyType, j2s, Logger, RefreshReason, TalerProtocolTimestamp,
BackupCoinSourceType, WalletBackupContentV1
BackupProposalStatus,
codecForContractTerms,
BackupRefundState,
RefreshReason,
BackupRefreshReason,
DenomKeyType,
AbsoluteTime,
TalerProtocolTimestamp,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { import {
WalletContractData, AbortStatus, CoinSource,
DenomSelectionState,
DenominationVerificationStatus,
CoinSource,
CoinSourceType, CoinSourceType,
CoinStatus, CoinStatus, DenominationVerificationStatus, DenomSelectionState, OperationStatus, ProposalDownload,
ReserveBankInfo, ProposalStatus, RefreshCoinStatus, RefreshSessionRecord, RefundState, ReserveBankInfo,
ReserveRecordStatus, ReserveRecordStatus, WalletContractData, WalletRefundItem, WalletStoresV1, WireInfo
ProposalDownload,
ProposalStatus,
WalletRefundItem,
RefundState,
AbortStatus,
RefreshSessionRecord,
WireInfo,
WalletStoresV1,
RefreshCoinStatus,
OperationStatus,
} from "../../db.js"; } from "../../db.js";
import { InternalWalletState } from "../../internal-wallet-state.js";
import { PayCoinSelection } from "../../util/coinSelection.js"; import { PayCoinSelection } from "../../util/coinSelection.js";
import { j2s } from "@gnu-taler/taler-util";
import { import {
checkDbInvariant, checkDbInvariant,
checkLogicInvariant, checkLogicInvariant
} from "../../util/invariants.js"; } from "../../util/invariants.js";
import { Logger } from "@gnu-taler/taler-util";
import { resetRetryInfo } from "../../util/retries.js";
import { InternalWalletState } from "../../internal-wallet-state.js";
import { provideBackupState } from "./state.js";
import { makeEventId, TombstoneTag } from "../transactions.js";
import { getExchangeDetails } from "../exchanges.js";
import { GetReadOnlyAccess, GetReadWriteAccess } from "../../util/query.js"; import { GetReadOnlyAccess, GetReadWriteAccess } from "../../util/query.js";
import { RetryInfo } from "../../util/retries.js";
import { getExchangeDetails } from "../exchanges.js";
import { makeEventId, TombstoneTag } from "../transactions.js";
import { provideBackupState } from "./state.js";
const logger = new Logger("operations/backup/import.ts"); const logger = new Logger("operations/backup/import.ts");
@ -276,7 +252,7 @@ export async function importBackup(
protocolVersionRange: backupExchange.protocol_version_range, protocolVersionRange: backupExchange.protocol_version_range,
}, },
permanent: true, permanent: true,
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
lastUpdate: undefined, lastUpdate: undefined,
nextUpdate: TalerProtocolTimestamp.now(), nextUpdate: TalerProtocolTimestamp.now(),
nextRefreshCheck: TalerProtocolTimestamp.now(), nextRefreshCheck: TalerProtocolTimestamp.now(),
@ -464,7 +440,7 @@ export async function importBackup(
timestampReserveInfoPosted: timestampReserveInfoPosted:
backupReserve.bank_info?.timestamp_reserve_info_posted, backupReserve.bank_info?.timestamp_reserve_info_posted,
senderWire: backupReserve.sender_wire, senderWire: backupReserve.sender_wire,
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
lastError: undefined, lastError: undefined,
initialWithdrawalGroupId: initialWithdrawalGroupId:
backupReserve.initial_withdrawal_group_id, backupReserve.initial_withdrawal_group_id,
@ -505,7 +481,7 @@ export async function importBackup(
backupWg.raw_withdrawal_amount, backupWg.raw_withdrawal_amount,
), ),
reservePub, reservePub,
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
secretSeed: backupWg.secret_seed, secretSeed: backupWg.secret_seed,
timestampStart: backupWg.timestamp_created, timestampStart: backupWg.timestamp_created,
timestampFinish: backupWg.timestamp_finish, timestampFinish: backupWg.timestamp_finish,
@ -618,7 +594,7 @@ export async function importBackup(
cryptoComp.proposalNoncePrivToPub[backupProposal.nonce_priv], cryptoComp.proposalNoncePrivToPub[backupProposal.nonce_priv],
proposalId: backupProposal.proposal_id, proposalId: backupProposal.proposal_id,
repurchaseProposalId: backupProposal.repurchase_proposal_id, repurchaseProposalId: backupProposal.repurchase_proposal_id,
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
download, download,
proposalStatus, proposalStatus,
}); });
@ -753,7 +729,7 @@ export async function importBackup(
cryptoComp.proposalNoncePrivToPub[backupPurchase.nonce_priv], cryptoComp.proposalNoncePrivToPub[backupPurchase.nonce_priv],
lastPayError: undefined, lastPayError: undefined,
autoRefundDeadline: TalerProtocolTimestamp.never(), autoRefundDeadline: TalerProtocolTimestamp.never(),
refundStatusRetryInfo: resetRetryInfo(), refundStatusRetryInfo: RetryInfo.reset(),
lastRefundStatusError: undefined, lastRefundStatusError: undefined,
refundAwaiting: undefined, refundAwaiting: undefined,
timestampAccept: backupPurchase.timestamp_accept, timestampAccept: backupPurchase.timestamp_accept,
@ -764,7 +740,7 @@ export async function importBackup(
lastSessionId: undefined, lastSessionId: undefined,
abortStatus, abortStatus,
// FIXME! // FIXME!
payRetryInfo: resetRetryInfo(), payRetryInfo: RetryInfo.reset(),
download, download,
paymentSubmitPending: paymentSubmitPending:
!backupPurchase.timestamp_first_successful_pay, !backupPurchase.timestamp_first_successful_pay,
@ -865,7 +841,7 @@ export async function importBackup(
Amounts.parseOrThrow(x.estimated_output_amount), Amounts.parseOrThrow(x.estimated_output_amount),
), ),
refreshSessionPerCoin, refreshSessionPerCoin,
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
}); });
} }
} }
@ -891,7 +867,7 @@ export async function importBackup(
merchantBaseUrl: backupTip.exchange_base_url, merchantBaseUrl: backupTip.exchange_base_url,
merchantTipId: backupTip.merchant_tip_id, merchantTipId: backupTip.merchant_tip_id,
pickedUpTimestamp: backupTip.timestamp_finished, pickedUpTimestamp: backupTip.timestamp_finished,
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
secretSeed: backupTip.secret_seed, secretSeed: backupTip.secret_seed,
tipAmountEffective: denomsSel.totalCoinValue, tipAmountEffective: denomsSel.totalCoinValue,
tipAmountRaw: Amounts.parseOrThrow(backupTip.tip_amount_raw), tipAmountRaw: Amounts.parseOrThrow(backupTip.tip_amount_raw),

View File

@ -25,10 +25,9 @@
* Imports. * Imports.
*/ */
import { import {
AmountString, AbsoluteTime, AmountString,
BackupRecovery, BackupRecovery,
buildCodecForObject, buildCodecForObject, bytesToString, canonicalizeBaseUrl,
canonicalizeBaseUrl,
canonicalJson, canonicalJson,
Codec, Codec,
codecForAmountString, codecForAmountString,
@ -37,39 +36,22 @@ import {
codecForNumber, codecForNumber,
codecForString, codecForString,
codecOptional, codecOptional,
ConfirmPayResultType, ConfirmPayResultType, decodeCrock, DenomKeyType,
DenomKeyType, durationFromSpec, eddsaGetPublic,
durationFromSpec,
hashDenomPub,
HttpStatusCode,
j2s,
Logger,
notEmpty,
PreparePayResultType,
RecoveryLoadRequest,
RecoveryMergeStrategy,
TalerErrorDetail,
AbsoluteTime,
URL,
WalletBackupContentV1,
TalerProtocolTimestamp,
} from "@gnu-taler/taler-util";
import { gunzipSync, gzipSync } from "fflate";
import { InternalWalletState } from "../../internal-wallet-state.js";
import { kdf } from "@gnu-taler/taler-util";
import { secretbox, secretbox_open } from "@gnu-taler/taler-util";
import {
bytesToString,
decodeCrock,
eddsaGetPublic,
EddsaKeyPair, EddsaKeyPair,
encodeCrock, encodeCrock,
getRandomBytes, getRandomBytes,
hash, hash, hashDenomPub,
rsaBlind, HttpStatusCode,
stringToBytes, j2s, kdf, Logger,
notEmpty,
PreparePayResultType,
RecoveryLoadRequest,
RecoveryMergeStrategy, rsaBlind, secretbox, secretbox_open, stringToBytes, TalerErrorDetail, TalerProtocolTimestamp, URL,
WalletBackupContentV1
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { CryptoDispatcher } from "../../crypto/workers/cryptoDispatcher.js"; import { gunzipSync, gzipSync } from "fflate";
import { TalerCryptoInterface } from "../../crypto/cryptoImplementation.js";
import { import {
BackupProviderRecord, BackupProviderRecord,
BackupProviderState, BackupProviderState,
@ -78,28 +60,28 @@ import {
ConfigRecord, ConfigRecord,
WalletBackupConfState, WalletBackupConfState,
WalletStoresV1, WalletStoresV1,
WALLET_BACKUP_STATE_KEY, WALLET_BACKUP_STATE_KEY
} from "../../db.js"; } from "../../db.js";
import { InternalWalletState } from "../../internal-wallet-state.js";
import { import {
readSuccessResponseJsonOrThrow, readSuccessResponseJsonOrThrow,
readTalerErrorResponse, readTalerErrorResponse
} from "../../util/http.js"; } from "../../util/http.js";
import { import {
checkDbInvariant, checkDbInvariant,
checkLogicInvariant, checkLogicInvariant
} from "../../util/invariants.js"; } from "../../util/invariants.js";
import { GetReadWriteAccess } from "../../util/query.js"; import { GetReadWriteAccess } from "../../util/query.js";
import { resetRetryInfo, updateRetryInfoTimeout } from "../../util/retries.js"; import { RetryInfo } from "../../util/retries.js";
import { guardOperationException } from "../common.js";
import { import {
checkPaymentByProposalId, checkPaymentByProposalId,
confirmPay, confirmPay,
preparePayForUri, preparePayForUri
} from "../pay.js"; } from "../pay.js";
import { exportBackup } from "./export.js"; import { exportBackup } from "./export.js";
import { BackupCryptoPrecomputedData, importBackup } from "./import.js"; import { BackupCryptoPrecomputedData, importBackup } from "./import.js";
import { getWalletBackupState, provideBackupState } from "./state.js"; import { getWalletBackupState, provideBackupState } from "./state.js";
import { guardOperationException } from "../common.js";
import { TalerCryptoInterface } from "../../crypto/cryptoImplementation.js";
const logger = new Logger("operations/backup.ts"); const logger = new Logger("operations/backup.ts");
@ -309,8 +291,8 @@ async function runBackupCycleForProvider(
"if-none-match": newHash, "if-none-match": newHash,
...(provider.lastBackupHash ...(provider.lastBackupHash
? { ? {
"if-match": provider.lastBackupHash, "if-match": provider.lastBackupHash,
} }
: {}), : {}),
}, },
}); });
@ -344,7 +326,7 @@ async function runBackupCycleForProvider(
} }
const res = await preparePayForUri(ws, talerUri); const res = await preparePayForUri(ws, talerUri);
let proposalId = res.proposalId; let proposalId = res.proposalId;
let doPay: boolean = false; let doPay = false;
switch (res.status) { switch (res.status) {
case PreparePayResultType.InsufficientBalance: case PreparePayResultType.InsufficientBalance:
// FIXME: record in provider state! // FIXME: record in provider state!
@ -434,7 +416,7 @@ async function runBackupCycleForProvider(
// FIXME: Allocate error code for this situation? // FIXME: Allocate error code for this situation?
prov.state = { prov.state = {
tag: BackupProviderStateTag.Retrying, tag: BackupProviderStateTag.Retrying,
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
}; };
await tx.backupProvider.put(prov); await tx.backupProvider.put(prov);
}); });
@ -472,13 +454,12 @@ async function incrementBackupRetryInTx(
return; return;
} }
if (pr.state.tag === BackupProviderStateTag.Retrying) { if (pr.state.tag === BackupProviderStateTag.Retrying) {
pr.state.retryInfo.retryCounter++;
pr.state.lastError = err; pr.state.lastError = err;
updateRetryInfoTimeout(pr.state.retryInfo); pr.state.retryInfo = RetryInfo.increment(pr.state.retryInfo);
} else if (pr.state.tag === BackupProviderStateTag.Ready) { } else if (pr.state.tag === BackupProviderStateTag.Ready) {
pr.state = { pr.state = {
tag: BackupProviderStateTag.Retrying, tag: BackupProviderStateTag.Retrying,
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
lastError: err, lastError: err,
}; };
} }
@ -685,7 +666,9 @@ export async function addBackupProvider(
}); });
} }
export async function restoreFromRecoverySecret(): Promise<void> {} export async function restoreFromRecoverySecret(): Promise<void> {
return;
}
/** /**
* Information about one provider. * Information about one provider.

View File

@ -47,7 +47,7 @@ import { DepositGroupRecord, OperationStatus, WireFee } from "../db.js";
import { InternalWalletState } from "../internal-wallet-state.js"; import { InternalWalletState } from "../internal-wallet-state.js";
import { PayCoinSelection, selectPayCoins } from "../util/coinSelection.js"; import { PayCoinSelection, selectPayCoins } from "../util/coinSelection.js";
import { readSuccessResponseJsonOrThrow } from "../util/http.js"; import { readSuccessResponseJsonOrThrow } from "../util/http.js";
import { resetRetryInfo, RetryInfo } from "../util/retries.js"; import { RetryInfo } from "../util/retries.js";
import { guardOperationException } from "./common.js"; import { guardOperationException } from "./common.js";
import { getExchangeDetails } from "./exchanges.js"; import { getExchangeDetails } from "./exchanges.js";
import { import {
@ -85,7 +85,7 @@ async function setupDepositGroupRetry(
return; return;
} }
if (options.resetRetry) { if (options.resetRetry) {
x.retryInfo = resetRetryInfo(); x.retryInfo = RetryInfo.reset();
} else { } else {
x.retryInfo = RetryInfo.increment(x.retryInfo); x.retryInfo = RetryInfo.increment(x.retryInfo);
} }
@ -599,7 +599,7 @@ export async function createDepositGroup(
payto_uri: req.depositPaytoUri, payto_uri: req.depositPaytoUri,
salt: wireSalt, salt: wireSalt,
}, },
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
operationStatus: OperationStatus.Pending, operationStatus: OperationStatus.Pending,
lastError: undefined, lastError: undefined,
}; };

View File

@ -63,7 +63,7 @@ import {
readSuccessResponseTextOrThrow, readSuccessResponseTextOrThrow,
} from "../util/http.js"; } from "../util/http.js";
import { DbAccess, GetReadOnlyAccess } from "../util/query.js"; import { DbAccess, GetReadOnlyAccess } from "../util/query.js";
import { resetRetryInfo, RetryInfo } from "../util/retries.js"; import { RetryInfo } from "../util/retries.js";
import { import {
WALLET_CACHE_BREAKER_CLIENT_VERSION, WALLET_CACHE_BREAKER_CLIENT_VERSION,
WALLET_EXCHANGE_PROTOCOL_VERSION, WALLET_EXCHANGE_PROTOCOL_VERSION,
@ -116,6 +116,9 @@ async function reportExchangeUpdateError(
if (!exchange) { if (!exchange) {
return; return;
} }
if (!exchange.retryInfo) {
logger.reportBreak();
}
exchange.lastError = err; exchange.lastError = err;
await tx.exchanges.put(exchange); await tx.exchanges.put(exchange);
}); });
@ -137,7 +140,7 @@ async function setupExchangeUpdateRetry(
return; return;
} }
if (options.reset) { if (options.reset) {
exchange.retryInfo = resetRetryInfo(); exchange.retryInfo = RetryInfo.reset();
} else { } else {
exchange.retryInfo = RetryInfo.increment(exchange.retryInfo); exchange.retryInfo = RetryInfo.increment(exchange.retryInfo);
} }
@ -399,7 +402,7 @@ async function provideExchangeRecord(
const r: ExchangeRecord = { const r: ExchangeRecord = {
permanent: true, permanent: true,
baseUrl: baseUrl, baseUrl: baseUrl,
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
detailsPointer: undefined, detailsPointer: undefined,
lastUpdate: undefined, lastUpdate: undefined,
nextUpdate: AbsoluteTime.toTimestamp(now), nextUpdate: AbsoluteTime.toTimestamp(now),

View File

@ -97,10 +97,7 @@ import {
} from "../util/http.js"; } from "../util/http.js";
import { GetReadWriteAccess } from "../util/query.js"; import { GetReadWriteAccess } from "../util/query.js";
import { import {
getRetryDuration,
resetRetryInfo,
RetryInfo, RetryInfo,
updateRetryInfoTimeout,
} from "../util/retries.js"; } from "../util/retries.js";
import { getExchangeDetails } from "./exchanges.js"; import { getExchangeDetails } from "./exchanges.js";
import { createRefreshGroup, getTotalRefreshCost } from "./refresh.js"; import { createRefreshGroup, getTotalRefreshCost } from "./refresh.js";
@ -438,8 +435,8 @@ async function recordConfirmPay(
proposalId: proposal.proposalId, proposalId: proposal.proposalId,
lastPayError: undefined, lastPayError: undefined,
lastRefundStatusError: undefined, lastRefundStatusError: undefined,
payRetryInfo: resetRetryInfo(), payRetryInfo: RetryInfo.reset(),
refundStatusRetryInfo: resetRetryInfo(), refundStatusRetryInfo: RetryInfo.reset(),
refundQueryRequested: false, refundQueryRequested: false,
timestampFirstSuccessfulPay: undefined, timestampFirstSuccessfulPay: undefined,
autoRefundDeadline: undefined, autoRefundDeadline: undefined,
@ -494,6 +491,7 @@ async function reportProposalError(
logger.error( logger.error(
`Asked to report an error for a proposal (${proposalId}) that is not active (no retryInfo)`, `Asked to report an error for a proposal (${proposalId}) that is not active (no retryInfo)`,
); );
logger.reportBreak();
return; return;
} }
pr.lastError = err; pr.lastError = err;
@ -517,7 +515,7 @@ async function setupProposalRetry(
return; return;
} }
if (options.reset) { if (options.reset) {
pr.retryInfo = resetRetryInfo(); pr.retryInfo = RetryInfo.reset();
} else { } else {
pr.retryInfo = RetryInfo.increment(pr.retryInfo); pr.retryInfo = RetryInfo.increment(pr.retryInfo);
} }
@ -541,7 +539,7 @@ async function setupPurchasePayRetry(
return; return;
} }
if (options.reset) { if (options.reset) {
p.payRetryInfo = resetRetryInfo(); p.payRetryInfo = RetryInfo.reset();
} else { } else {
p.payRetryInfo = RetryInfo.increment(p.payRetryInfo); p.payRetryInfo = RetryInfo.increment(p.payRetryInfo);
} }
@ -610,7 +608,7 @@ async function failProposalPermanently(
function getProposalRequestTimeout(proposal: ProposalRecord): Duration { function getProposalRequestTimeout(proposal: ProposalRecord): Duration {
return durationMax( return durationMax(
{ d_ms: 60000 }, { d_ms: 60000 },
durationMin({ d_ms: 5000 }, getRetryDuration(proposal.retryInfo)), durationMin({ d_ms: 5000 }, RetryInfo.getDuration(proposal.retryInfo)),
); );
} }
@ -938,7 +936,7 @@ async function startDownloadProposal(
proposalId: proposalId, proposalId: proposalId,
proposalStatus: ProposalStatus.Downloading, proposalStatus: ProposalStatus.Downloading,
repurchaseProposalId: undefined, repurchaseProposalId: undefined,
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
lastError: undefined, lastError: undefined,
downloadSessionId: sessionId, downloadSessionId: sessionId,
}; };
@ -986,14 +984,14 @@ async function storeFirstPaySuccess(
purchase.paymentSubmitPending = false; purchase.paymentSubmitPending = false;
purchase.lastPayError = undefined; purchase.lastPayError = undefined;
purchase.lastSessionId = sessionId; purchase.lastSessionId = sessionId;
purchase.payRetryInfo = resetRetryInfo(); purchase.payRetryInfo = RetryInfo.reset();
purchase.merchantPaySig = paySig; purchase.merchantPaySig = paySig;
const protoAr = purchase.download.contractData.autoRefund; const protoAr = purchase.download.contractData.autoRefund;
if (protoAr) { if (protoAr) {
const ar = Duration.fromTalerProtocolDuration(protoAr); const ar = Duration.fromTalerProtocolDuration(protoAr);
logger.info("auto_refund present"); logger.info("auto_refund present");
purchase.refundQueryRequested = true; purchase.refundQueryRequested = true;
purchase.refundStatusRetryInfo = resetRetryInfo(); purchase.refundStatusRetryInfo = RetryInfo.reset();
purchase.lastRefundStatusError = undefined; purchase.lastRefundStatusError = undefined;
purchase.autoRefundDeadline = AbsoluteTime.toTimestamp( purchase.autoRefundDeadline = AbsoluteTime.toTimestamp(
AbsoluteTime.addDuration(AbsoluteTime.now(), ar), AbsoluteTime.addDuration(AbsoluteTime.now(), ar),
@ -1023,7 +1021,7 @@ async function storePayReplaySuccess(
} }
purchase.paymentSubmitPending = false; purchase.paymentSubmitPending = false;
purchase.lastPayError = undefined; purchase.lastPayError = undefined;
purchase.payRetryInfo = resetRetryInfo(); purchase.payRetryInfo = RetryInfo.reset();
purchase.lastSessionId = sessionId; purchase.lastSessionId = sessionId;
await tx.purchases.put(purchase); await tx.purchases.put(purchase);
}); });

View File

@ -26,38 +26,28 @@
*/ */
import { import {
Amounts, Amounts,
codecForRecoupConfirmation, codecForRecoupConfirmation, encodeCrock, getRandomBytes, j2s, Logger, NotificationType,
j2s,
NotificationType,
RefreshReason, RefreshReason,
TalerErrorDetail, TalerErrorDetail,
TalerProtocolTimestamp, TalerProtocolTimestamp, URL
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { encodeCrock, getRandomBytes } from "@gnu-taler/taler-util";
import { import {
CoinRecord, CoinRecord,
CoinSourceType, CoinSourceType,
CoinStatus, CoinStatus, OperationStatus, RecoupGroupRecord,
RecoupGroupRecord,
RefreshCoinSource, RefreshCoinSource,
ReserveRecordStatus, ReserveRecordStatus, WalletStoresV1, WithdrawCoinSource
WithdrawCoinSource,
WalletStoresV1,
OperationStatus,
} from "../db.js"; } from "../db.js";
import { InternalWalletState } from "../internal-wallet-state.js";
import { readSuccessResponseJsonOrThrow } from "../util/http.js"; import { readSuccessResponseJsonOrThrow } from "../util/http.js";
import { Logger, URL } from "@gnu-taler/taler-util"; import { GetReadWriteAccess } from "../util/query.js";
import { import {
resetRetryInfo, RetryInfo
RetryInfo,
updateRetryInfoTimeout,
} from "../util/retries.js"; } from "../util/retries.js";
import { guardOperationException } from "./common.js";
import { createRefreshGroup, processRefreshGroup } from "./refresh.js"; import { createRefreshGroup, processRefreshGroup } from "./refresh.js";
import { getReserveRequestTimeout, processReserve } from "./reserves.js"; import { getReserveRequestTimeout, processReserve } from "./reserves.js";
import { InternalWalletState } from "../internal-wallet-state.js";
import { GetReadWriteAccess } from "../util/query.js";
import { guardOperationException } from "./common.js";
const logger = new Logger("operations/recoup.ts"); const logger = new Logger("operations/recoup.ts");
@ -78,7 +68,7 @@ async function setupRecoupRetry(
return; return;
} }
if (options.reset) { if (options.reset) {
r.retryInfo = resetRetryInfo(); r.retryInfo = RetryInfo.reset();
} else { } else {
r.retryInfo = RetryInfo.increment(r.retryInfo); r.retryInfo = RetryInfo.increment(r.retryInfo);
} }
@ -139,7 +129,7 @@ async function putGroupAsFinished(
if (allFinished) { if (allFinished) {
logger.info("all recoups of recoup group are finished"); logger.info("all recoups of recoup group are finished");
recoupGroup.timestampFinished = TalerProtocolTimestamp.now(); recoupGroup.timestampFinished = TalerProtocolTimestamp.now();
recoupGroup.retryInfo = resetRetryInfo(); recoupGroup.retryInfo = RetryInfo.reset();
recoupGroup.lastError = undefined; recoupGroup.lastError = undefined;
if (recoupGroup.scheduleRefreshCoins.length > 0) { if (recoupGroup.scheduleRefreshCoins.length > 0) {
const refreshGroupId = await createRefreshGroup( const refreshGroupId = await createRefreshGroup(
@ -278,7 +268,7 @@ async function recoupWithdrawCoin(
const currency = updatedCoin.currentAmount.currency; const currency = updatedCoin.currentAmount.currency;
updatedCoin.currentAmount = Amounts.getZero(currency); updatedCoin.currentAmount = Amounts.getZero(currency);
updatedReserve.reserveStatus = ReserveRecordStatus.QueryingStatus; updatedReserve.reserveStatus = ReserveRecordStatus.QueryingStatus;
updatedReserve.retryInfo = resetRetryInfo(); updatedReserve.retryInfo = RetryInfo.reset();
updatedReserve.operationStatus = OperationStatus.Pending; updatedReserve.operationStatus = OperationStatus.Pending;
await tx.coins.put(updatedCoin); await tx.coins.put(updatedCoin);
await tx.reserves.put(updatedReserve); await tx.reserves.put(updatedReserve);
@ -482,7 +472,7 @@ export async function createRecoupGroup(
lastError: undefined, lastError: undefined,
timestampFinished: undefined, timestampFinished: undefined,
timestampStarted: TalerProtocolTimestamp.now(), timestampStarted: TalerProtocolTimestamp.now(),
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
recoupFinishedPerCoin: coinPubs.map(() => false), recoupFinishedPerCoin: coinPubs.map(() => false),
// Will be populated later // Will be populated later
oldAmountPerCoin: [], oldAmountPerCoin: [],

View File

@ -15,20 +15,28 @@
*/ */
import { import {
AgeCommitment, AbsoluteTime, AgeCommitment,
AgeRestriction, AgeRestriction, AmountJson, Amounts, amountToPretty, codecForExchangeMeltResponse,
CoinPublicKeyString, codecForExchangeRevealResponse,
DenomKeyType, CoinPublicKey, CoinPublicKeyString,
encodeCrock, DenomKeyType, Duration,
durationFromSpec,
durationMul, encodeCrock,
ExchangeMeltRequest, ExchangeMeltRequest,
ExchangeProtocolVersion, ExchangeProtocolVersion, ExchangeRefreshRevealRequest, fnutil, getRandomBytes,
ExchangeRefreshRevealRequest,
getRandomBytes,
HashCodeString, HashCodeString,
HttpStatusCode, HttpStatusCode,
j2s, j2s, Logger, NotificationType,
TalerProtocolTimestamp, RefreshGroupId,
RefreshReason,
TalerErrorDetail, TalerProtocolTimestamp, URL
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { TalerCryptoInterface } from "../crypto/cryptoImplementation.js";
import {
DerivedRefreshSession,
RefreshNewDenomInfo
} from "../crypto/cryptoTypes.js";
import { CryptoApiStoppedError } from "../crypto/workers/cryptoDispatcher.js";
import { import {
CoinRecord, CoinRecord,
CoinSourceType, CoinSourceType,
@ -37,57 +45,29 @@ import {
OperationStatus, OperationStatus,
RefreshCoinStatus, RefreshCoinStatus,
RefreshGroupRecord, RefreshGroupRecord,
WalletStoresV1, WalletStoresV1
} from "../db.js"; } from "../db.js";
import { import { TalerError } from "../errors.js";
codecForExchangeMeltResponse,
codecForExchangeRevealResponse,
CoinPublicKey,
fnutil,
NotificationType,
RefreshGroupId,
RefreshReason,
TalerErrorDetail,
} from "@gnu-taler/taler-util";
import { AmountJson, Amounts } from "@gnu-taler/taler-util";
import { amountToPretty } from "@gnu-taler/taler-util";
import {
readSuccessResponseJsonOrThrow,
readUnexpectedResponseDetails,
} from "../util/http.js";
import { checkDbInvariant } from "../util/invariants.js";
import { Logger } from "@gnu-taler/taler-util";
import {
resetRetryInfo,
RetryInfo,
updateRetryInfoTimeout,
} from "../util/retries.js";
import {
Duration,
durationFromSpec,
durationMul,
AbsoluteTime,
URL,
} from "@gnu-taler/taler-util";
import { updateExchangeFromUrl } from "./exchanges.js";
import { import {
DenomInfo, DenomInfo,
EXCHANGE_COINS_LOCK, EXCHANGE_COINS_LOCK,
InternalWalletState, InternalWalletState
} from "../internal-wallet-state.js"; } from "../internal-wallet-state.js";
import { import {
isWithdrawableDenom, readSuccessResponseJsonOrThrow,
selectWithdrawalDenominations, readUnexpectedResponseDetails
} from "./withdraw.js"; } from "../util/http.js";
import { import { checkDbInvariant } from "../util/invariants.js";
DerivedRefreshSession,
RefreshNewDenomInfo,
} from "../crypto/cryptoTypes.js";
import { GetReadWriteAccess } from "../util/query.js"; import { GetReadWriteAccess } from "../util/query.js";
import {
RetryInfo
} from "../util/retries.js";
import { guardOperationException } from "./common.js"; import { guardOperationException } from "./common.js";
import { CryptoApiStoppedError } from "../crypto/workers/cryptoDispatcher.js"; import { updateExchangeFromUrl } from "./exchanges.js";
import { TalerCryptoInterface } from "../crypto/cryptoImplementation.js"; import {
import { TalerError } from "../errors.js"; isWithdrawableDenom,
selectWithdrawalDenominations
} from "./withdraw.js";
const logger = new Logger("refresh.ts"); const logger = new Logger("refresh.ts");
@ -129,22 +109,22 @@ export function getTotalRefreshCost(
} }
function updateGroupStatus(rg: RefreshGroupRecord): void { function updateGroupStatus(rg: RefreshGroupRecord): void {
let allDone = fnutil.all( const allDone = fnutil.all(
rg.statusPerCoin, rg.statusPerCoin,
(x) => x === RefreshCoinStatus.Finished || x === RefreshCoinStatus.Frozen, (x) => x === RefreshCoinStatus.Finished || x === RefreshCoinStatus.Frozen,
); );
let anyFrozen = fnutil.any( const anyFrozen = fnutil.any(
rg.statusPerCoin, rg.statusPerCoin,
(x) => x === RefreshCoinStatus.Frozen, (x) => x === RefreshCoinStatus.Frozen,
); );
if (allDone) { if (allDone) {
if (anyFrozen) { if (anyFrozen) {
rg.frozen = true; rg.frozen = true;
rg.retryInfo = resetRetryInfo(); rg.retryInfo = RetryInfo.reset();
} else { } else {
rg.timestampFinished = AbsoluteTime.toTimestamp(AbsoluteTime.now()); rg.timestampFinished = AbsoluteTime.toTimestamp(AbsoluteTime.now());
rg.operationStatus = OperationStatus.Finished; rg.operationStatus = OperationStatus.Finished;
rg.retryInfo = resetRetryInfo(); rg.retryInfo = RetryInfo.reset();
} }
} }
} }
@ -753,7 +733,7 @@ async function setupRefreshRetry(
return; return;
} }
if (options.reset) { if (options.reset) {
r.retryInfo = resetRetryInfo(); r.retryInfo = RetryInfo.reset();
} else { } else {
r.retryInfo = RetryInfo.increment(r.retryInfo); r.retryInfo = RetryInfo.increment(r.retryInfo);
} }
@ -987,7 +967,7 @@ export async function createRefreshGroup(
reason, reason,
refreshGroupId, refreshGroupId,
refreshSessionPerCoin: oldCoinPubs.map(() => undefined), refreshSessionPerCoin: oldCoinPubs.map(() => undefined),
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
inputPerCoin, inputPerCoin,
estimatedOutputPerCoin, estimatedOutputPerCoin,
timestampCreated: TalerProtocolTimestamp.now(), timestampCreated: TalerProtocolTimestamp.now(),

View File

@ -25,29 +25,18 @@
*/ */
import { import {
AbortingCoin, AbortingCoin,
AbortRequest, AbortRequest, AbsoluteTime, AmountJson,
AmountJson,
Amounts, Amounts,
ApplyRefundResponse, ApplyRefundResponse,
codecForAbortResponse, codecForAbortResponse,
codecForMerchantOrderRefundPickupResponse, codecForMerchantOrderRefundPickupResponse, codecForMerchantOrderStatusPaid, CoinPublicKey, Duration, Logger,
CoinPublicKey,
Logger,
MerchantCoinRefundFailureStatus, MerchantCoinRefundFailureStatus,
MerchantCoinRefundStatus, MerchantCoinRefundStatus,
MerchantCoinRefundSuccessStatus, MerchantCoinRefundSuccessStatus,
NotificationType, NotificationType,
parseRefundUri, parseRefundUri, PrepareRefundResult, RefreshReason,
RefreshReason,
TalerErrorCode, TalerErrorCode,
TalerErrorDetail, TalerErrorDetail, TalerProtocolTimestamp, URL
URL,
codecForMerchantOrderStatusPaid,
AbsoluteTime,
TalerProtocolTimestamp,
Duration,
PrepareRefundRequest,
PrepareRefundResult,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { import {
AbortStatus, AbortStatus,
@ -55,19 +44,17 @@ import {
PurchaseRecord, PurchaseRecord,
RefundReason, RefundReason,
RefundState, RefundState,
WalletStoresV1, WalletStoresV1
} from "../db.js"; } from "../db.js";
import { InternalWalletState } from "../internal-wallet-state.js";
import { readSuccessResponseJsonOrThrow } from "../util/http.js"; import { readSuccessResponseJsonOrThrow } from "../util/http.js";
import { checkDbInvariant } from "../util/invariants.js"; import { checkDbInvariant } from "../util/invariants.js";
import { GetReadWriteAccess } from "../util/query.js"; import { GetReadWriteAccess } from "../util/query.js";
import { import {
resetRetryInfo, RetryInfo
RetryInfo,
updateRetryInfoTimeout,
} from "../util/retries.js"; } from "../util/retries.js";
import { createRefreshGroup, getTotalRefreshCost } from "./refresh.js";
import { InternalWalletState } from "../internal-wallet-state.js";
import { guardOperationException } from "./common.js"; import { guardOperationException } from "./common.js";
import { createRefreshGroup, getTotalRefreshCost } from "./refresh.js";
const logger = new Logger("refund.ts"); const logger = new Logger("refund.ts");
@ -147,7 +134,7 @@ async function setupPurchaseQueryRefundRetry(
return; return;
} }
if (options.reset) { if (options.reset) {
pr.refundStatusRetryInfo = resetRetryInfo(); pr.refundStatusRetryInfo = RetryInfo.reset();
} else { } else {
pr.refundStatusRetryInfo = RetryInfo.increment( pr.refundStatusRetryInfo = RetryInfo.increment(
pr.refundStatusRetryInfo, pr.refundStatusRetryInfo,
@ -500,7 +487,7 @@ async function acceptRefunds(
if (queryDone) { if (queryDone) {
p.timestampLastRefundStatus = now; p.timestampLastRefundStatus = now;
p.lastRefundStatusError = undefined; p.lastRefundStatusError = undefined;
p.refundStatusRetryInfo = resetRetryInfo(); p.refundStatusRetryInfo = RetryInfo.reset();
p.refundQueryRequested = false; p.refundQueryRequested = false;
if (p.abortStatus === AbortStatus.AbortRefund) { if (p.abortStatus === AbortStatus.AbortRefund) {
p.abortStatus = AbortStatus.AbortFinished; p.abortStatus = AbortStatus.AbortFinished;
@ -509,8 +496,7 @@ async function acceptRefunds(
} else { } else {
// No error, but we need to try again! // No error, but we need to try again!
p.timestampLastRefundStatus = now; p.timestampLastRefundStatus = now;
p.refundStatusRetryInfo.retryCounter++; p.refundStatusRetryInfo = RetryInfo.increment(p.refundStatusRetryInfo)
updateRetryInfoTimeout(p.refundStatusRetryInfo);
p.lastRefundStatusError = undefined; p.lastRefundStatusError = undefined;
logger.trace("refund query not done"); logger.trace("refund query not done");
} }
@ -619,7 +605,7 @@ export async function applyRefund(
} }
p.refundQueryRequested = true; p.refundQueryRequested = true;
p.lastRefundStatusError = undefined; p.lastRefundStatusError = undefined;
p.refundStatusRetryInfo = resetRetryInfo(); p.refundStatusRetryInfo = RetryInfo.reset();
await tx.purchases.put(p); await tx.purchases.put(p);
return true; return true;
}); });
@ -892,7 +878,7 @@ export async function abortFailedPayWithRefund(
purchase.paymentSubmitPending = false; purchase.paymentSubmitPending = false;
purchase.abortStatus = AbortStatus.AbortRefund; purchase.abortStatus = AbortStatus.AbortRefund;
purchase.lastPayError = undefined; purchase.lastPayError = undefined;
purchase.payRetryInfo = resetRetryInfo(); purchase.payRetryInfo = RetryInfo.reset();
await tx.purchases.put(purchase); await tx.purchases.put(purchase);
}); });
processPurchaseQueryRefund(ws, proposalId, { processPurchaseQueryRefund(ws, proposalId, {

View File

@ -58,8 +58,6 @@ import {
} from "../util/http.js"; } from "../util/http.js";
import { GetReadOnlyAccess } from "../util/query.js"; import { GetReadOnlyAccess } from "../util/query.js";
import { import {
getRetryDuration,
resetRetryInfo,
RetryInfo, RetryInfo,
} from "../util/retries.js"; } from "../util/retries.js";
import { import {
@ -100,7 +98,7 @@ async function setupReserveRetry(
return; return;
} }
if (options.reset) { if (options.reset) {
r.retryInfo = resetRetryInfo(); r.retryInfo = RetryInfo.reset();
} else { } else {
r.retryInfo = RetryInfo.increment(r.retryInfo); r.retryInfo = RetryInfo.increment(r.retryInfo);
} }
@ -196,7 +194,7 @@ export async function createReserve(
timestampReserveInfoPosted: undefined, timestampReserveInfoPosted: undefined,
bankInfo, bankInfo,
reserveStatus, reserveStatus,
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
lastError: undefined, lastError: undefined,
currency: req.amount.currency, currency: req.amount.currency,
operationStatus: OperationStatus.Pending, operationStatus: OperationStatus.Pending,
@ -297,7 +295,7 @@ export async function forceQueryReserve(
case ReserveRecordStatus.Dormant: case ReserveRecordStatus.Dormant:
reserve.reserveStatus = ReserveRecordStatus.QueryingStatus; reserve.reserveStatus = ReserveRecordStatus.QueryingStatus;
reserve.operationStatus = OperationStatus.Pending; reserve.operationStatus = OperationStatus.Pending;
reserve.retryInfo = resetRetryInfo(); reserve.retryInfo = RetryInfo.reset();
break; break;
default: default:
break; break;
@ -392,7 +390,7 @@ async function registerReserveWithBank(
if (!r.bankInfo) { if (!r.bankInfo) {
throw Error("invariant failed"); throw Error("invariant failed");
} }
r.retryInfo = resetRetryInfo(); r.retryInfo = RetryInfo.reset();
await tx.reserves.put(r); await tx.reserves.put(r);
}); });
ws.notify({ type: NotificationType.ReserveRegisteredWithBank }); ws.notify({ type: NotificationType.ReserveRegisteredWithBank });
@ -402,7 +400,7 @@ async function registerReserveWithBank(
export function getReserveRequestTimeout(r: ReserveRecord): Duration { export function getReserveRequestTimeout(r: ReserveRecord): Duration {
return durationMax( return durationMax(
{ d_ms: 60000 }, { d_ms: 60000 },
durationMin({ d_ms: 5000 }, getRetryDuration(r.retryInfo)), durationMin({ d_ms: 5000 }, RetryInfo.getDuration(r.retryInfo)),
); );
} }
@ -459,7 +457,7 @@ async function processReserveBankStatus(
r.timestampBankConfirmed = now; r.timestampBankConfirmed = now;
r.reserveStatus = ReserveRecordStatus.BankAborted; r.reserveStatus = ReserveRecordStatus.BankAborted;
r.operationStatus = OperationStatus.Finished; r.operationStatus = OperationStatus.Finished;
r.retryInfo = resetRetryInfo(); r.retryInfo = RetryInfo.reset();
await tx.reserves.put(r); await tx.reserves.put(r);
}); });
return; return;
@ -496,7 +494,7 @@ async function processReserveBankStatus(
r.timestampBankConfirmed = now; r.timestampBankConfirmed = now;
r.reserveStatus = ReserveRecordStatus.QueryingStatus; r.reserveStatus = ReserveRecordStatus.QueryingStatus;
r.operationStatus = OperationStatus.Pending; r.operationStatus = OperationStatus.Pending;
r.retryInfo = resetRetryInfo(); r.retryInfo = RetryInfo.reset();
} else { } else {
switch (r.reserveStatus) { switch (r.reserveStatus) {
case ReserveRecordStatus.WaitConfirmBank: case ReserveRecordStatus.WaitConfirmBank:
@ -555,7 +553,7 @@ async function updateReserve(
if ( if (
resp.status === 404 && resp.status === 404 &&
result.talerErrorResponse.code === result.talerErrorResponse.code ===
TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN
) { ) {
ws.notify({ ws.notify({
type: NotificationType.ReserveNotYetFound, type: NotificationType.ReserveNotYetFound,
@ -662,7 +660,7 @@ async function updateReserve(
reservePub: reserve.reservePub, reservePub: reserve.reservePub,
rawWithdrawalAmount: remainingAmount, rawWithdrawalAmount: remainingAmount,
timestampStart: AbsoluteTime.toTimestamp(AbsoluteTime.now()), timestampStart: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
lastError: undefined, lastError: undefined,
denomsSel: denomSel, denomsSel: denomSel,
secretSeed: encodeCrock(getRandomBytes(64)), secretSeed: encodeCrock(getRandomBytes(64)),
@ -721,12 +719,13 @@ async function processReserveImpl(
case ReserveRecordStatus.RegisteringBank: case ReserveRecordStatus.RegisteringBank:
await processReserveBankStatus(ws, reservePub); await processReserveBankStatus(ws, reservePub);
return await processReserveImpl(ws, reservePub, { forceNow: true }); return await processReserveImpl(ws, reservePub, { forceNow: true });
case ReserveRecordStatus.QueryingStatus: case ReserveRecordStatus.QueryingStatus: {
const res = await updateReserve(ws, reservePub); const res = await updateReserve(ws, reservePub);
if (res.ready) { if (res.ready) {
return await processReserveImpl(ws, reservePub, { forceNow: true }); return await processReserveImpl(ws, reservePub, { forceNow: true });
} }
break; break;
}
case ReserveRecordStatus.Dormant: case ReserveRecordStatus.Dormant:
// nothing to do // nothing to do
break; break;

View File

@ -18,51 +18,30 @@
* Imports. * Imports.
*/ */
import { import {
PrepareTipResult, Amounts, BlindedDenominationSignature,
parseTipUri, codecForMerchantTipResponseV2, codecForTipPickupGetResponse, DenomKeyType, encodeCrock, getRandomBytes, j2s, Logger, NotificationType, parseTipUri, PrepareTipResult, TalerErrorCode, TalerErrorDetail, TalerProtocolTimestamp, TipPlanchetDetail, URL
codecForTipPickupGetResponse,
Amounts,
TalerErrorDetail,
NotificationType,
TipPlanchetDetail,
TalerErrorCode,
Logger,
URL,
DenomKeyType,
BlindedDenominationSignature,
codecForMerchantTipResponseV2,
TalerProtocolTimestamp,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { DerivedTipPlanchet } from "../crypto/cryptoTypes.js"; import { DerivedTipPlanchet } from "../crypto/cryptoTypes.js";
import { import {
DenominationRecord,
CoinRecord, CoinRecord,
CoinSourceType, CoinSourceType,
CoinStatus, CoinStatus, DenominationRecord, TipRecord
TipRecord,
} from "../db.js"; } from "../db.js";
import { j2s } from "@gnu-taler/taler-util";
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
import {
resetRetryInfo,
RetryInfo,
updateRetryInfoTimeout,
} from "../util/retries.js";
import { makeErrorDetail } from "../errors.js"; import { makeErrorDetail } from "../errors.js";
import { updateExchangeFromUrl } from "./exchanges.js";
import { InternalWalletState } from "../internal-wallet-state.js"; import { InternalWalletState } from "../internal-wallet-state.js";
import {
getExchangeWithdrawalInfo,
updateWithdrawalDenoms,
getCandidateWithdrawalDenoms,
selectWithdrawalDenominations,
} from "./withdraw.js";
import { import {
getHttpResponseErrorDetails, getHttpResponseErrorDetails,
readSuccessResponseJsonOrThrow, readSuccessResponseJsonOrThrow
} from "../util/http.js"; } from "../util/http.js";
import { encodeCrock, getRandomBytes } from "@gnu-taler/taler-util"; import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
import {
RetryInfo
} from "../util/retries.js";
import { guardOperationException } from "./common.js"; import { guardOperationException } from "./common.js";
import { updateExchangeFromUrl } from "./exchanges.js";
import {
getCandidateWithdrawalDenoms, getExchangeWithdrawalInfo, selectWithdrawalDenominations, updateWithdrawalDenoms
} from "./withdraw.js";
const logger = new Logger("operations/tip.ts"); const logger = new Logger("operations/tip.ts");
@ -130,7 +109,7 @@ export async function prepareTip(
createdTimestamp: TalerProtocolTimestamp.now(), createdTimestamp: TalerProtocolTimestamp.now(),
merchantTipId: res.merchantTipId, merchantTipId: res.merchantTipId,
tipAmountEffective: selectedDenoms.totalCoinValue, tipAmountEffective: selectedDenoms.totalCoinValue,
retryInfo: resetRetryInfo(), retryInfo: RetryInfo.reset(),
lastError: undefined, lastError: undefined,
denomsSel: selectedDenoms, denomsSel: selectedDenoms,
pickedUpTimestamp: undefined, pickedUpTimestamp: undefined,
@ -202,7 +181,7 @@ async function setupTipRetry(
return; return;
} }
if (options.reset) { if (options.reset) {
t.retryInfo = resetRetryInfo(); t.retryInfo = RetryInfo.reset();
} else { } else {
t.retryInfo = RetryInfo.increment(t.retryInfo); t.retryInfo = RetryInfo.increment(t.retryInfo);
} }
@ -237,7 +216,7 @@ async function resetTipRetry(
.runReadWrite(async (tx) => { .runReadWrite(async (tx) => {
const x = await tx.tips.get(tipId); const x = await tx.tips.get(tipId);
if (x) { if (x) {
x.retryInfo = resetRetryInfo(); x.retryInfo = RetryInfo.reset();
await tx.tips.put(x); await tx.tips.put(x);
} }
}); });
@ -430,7 +409,7 @@ async function processTipImpl(
} }
tr.pickedUpTimestamp = TalerProtocolTimestamp.now(); tr.pickedUpTimestamp = TalerProtocolTimestamp.now();
tr.lastError = undefined; tr.lastError = undefined;
tr.retryInfo = resetRetryInfo(); tr.retryInfo = RetryInfo.reset();
await tx.tips.put(tr); await tx.tips.put(tr);
for (const cr of newCoinRecords) { for (const cr of newCoinRecords) {
await tx.coins.put(cr); await tx.coins.put(cr);

View File

@ -72,7 +72,7 @@ import {
readSuccessResponseJsonOrThrow, readSuccessResponseJsonOrThrow,
} from "../util/http.js"; } from "../util/http.js";
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js"; import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
import { resetRetryInfo, RetryInfo } from "../util/retries.js"; import { RetryInfo } from "../util/retries.js";
import { import {
WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
WALLET_EXCHANGE_PROTOCOL_VERSION, WALLET_EXCHANGE_PROTOCOL_VERSION,
@ -215,7 +215,7 @@ export function selectWithdrawalDenominations(
for (const d of denoms) { for (const d of denoms) {
let count = 0; let count = 0;
const cost = Amounts.add(d.value, d.feeWithdraw).amount; const cost = Amounts.add(d.value, d.feeWithdraw).amount;
for (;;) { for (; ;) {
if (Amounts.cmp(remaining, cost) < 0) { if (Amounts.cmp(remaining, cost) < 0) {
break; break;
} }
@ -875,11 +875,10 @@ export async function updateWithdrawalDenoms(
denom.verificationStatus === DenominationVerificationStatus.Unverified denom.verificationStatus === DenominationVerificationStatus.Unverified
) { ) {
logger.trace( logger.trace(
`Validating denomination (${current + 1}/${ `Validating denomination (${current + 1}/${denominations.length
denominations.length
}) signature of ${denom.denomPubHash}`, }) signature of ${denom.denomPubHash}`,
); );
let valid: boolean = false; let valid = false;
if (ws.insecureTrustExchange) { if (ws.insecureTrustExchange) {
valid = true; valid = true;
} else { } else {
@ -932,7 +931,7 @@ async function setupWithdrawalRetry(
return; return;
} }
if (options.reset) { if (options.reset) {
wsr.retryInfo = resetRetryInfo(); wsr.retryInfo = RetryInfo.reset();
} else { } else {
wsr.retryInfo = RetryInfo.increment(wsr.retryInfo); wsr.retryInfo = RetryInfo.increment(wsr.retryInfo);
} }
@ -1097,7 +1096,7 @@ async function processWithdrawGroupImpl(
wg.timestampFinish = TalerProtocolTimestamp.now(); wg.timestampFinish = TalerProtocolTimestamp.now();
wg.operationStatus = OperationStatus.Finished; wg.operationStatus = OperationStatus.Finished;
delete wg.lastError; delete wg.lastError;
wg.retryInfo = resetRetryInfo(); wg.retryInfo = RetryInfo.reset();
} }
await tx.withdrawalGroups.put(wg); await tx.withdrawalGroups.put(wg);
@ -1203,7 +1202,7 @@ export async function getExchangeWithdrawalInfo(
) { ) {
console.warn( console.warn(
`wallet's support for exchange protocol version ${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` + `wallet's support for exchange protocol version ${WALLET_EXCHANGE_PROTOCOL_VERSION} might be outdated ` +
`(exchange has ${exchangeDetails.protocolVersion}), checking for updates`, `(exchange has ${exchangeDetails.protocolVersion}), checking for updates`,
); );
} }
} }

View File

@ -41,7 +41,7 @@ const defaultRetryPolicy: RetryPolicy = {
maxTimeout: { d_ms: 6000 }, maxTimeout: { d_ms: 6000 },
}; };
export function updateRetryInfoTimeout( function updateTimeout(
r: RetryInfo, r: RetryInfo,
p: RetryPolicy = defaultRetryPolicy, p: RetryPolicy = defaultRetryPolicy,
): void { ): void {
@ -65,45 +65,46 @@ export function updateRetryInfoTimeout(
r.nextRetry = { t_ms: t }; r.nextRetry = { t_ms: t };
} }
export function getRetryDuration(
r: RetryInfo | undefined,
p: RetryPolicy = defaultRetryPolicy,
): Duration {
if (!r) {
// If we don't have any retry info, run immediately.
return { d_ms: 0 };
}
if (p.backoffDelta.d_ms === "forever") {
return { d_ms: "forever" };
}
const t = p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter);
return {
d_ms: p.maxTimeout.d_ms === "forever" ? t : Math.min(p.maxTimeout.d_ms, t),
};
}
export function resetRetryInfo(p: RetryPolicy = defaultRetryPolicy): RetryInfo {
const now = AbsoluteTime.now();
const info = {
firstTry: now,
nextRetry: now,
retryCounter: 0,
};
updateRetryInfoTimeout(info, p);
return info;
}
export namespace RetryInfo { export namespace RetryInfo {
export function getDuration(
r: RetryInfo | undefined,
p: RetryPolicy = defaultRetryPolicy,
): Duration {
if (!r) {
// If we don't have any retry info, run immediately.
return { d_ms: 0 };
}
if (p.backoffDelta.d_ms === "forever") {
return { d_ms: "forever" };
}
const t = p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter);
return {
d_ms: p.maxTimeout.d_ms === "forever" ? t : Math.min(p.maxTimeout.d_ms, t),
};
}
export function reset(p: RetryPolicy = defaultRetryPolicy): RetryInfo {
const now = AbsoluteTime.now();
const info = {
firstTry: now,
nextRetry: now,
retryCounter: 0,
};
updateTimeout(info, p);
return info;
}
export function increment( export function increment(
r: RetryInfo | undefined, r: RetryInfo | undefined,
p: RetryPolicy = defaultRetryPolicy, p: RetryPolicy = defaultRetryPolicy,
) { ): RetryInfo {
if (!r) { if (!r) {
return resetRetryInfo(p); return reset(p);
} }
const r2 = { ...r }; const r2 = { ...r };
r2.retryCounter++; r2.retryCounter++;
updateRetryInfoTimeout(r2, p); updateTimeout(r2, p);
return r2; return r2;
} }
} }

View File

@ -335,6 +335,7 @@ async function runTaskLoop(
let numGivingLiveness = 0; let numGivingLiveness = 0;
let numDue = 0; let numDue = 0;
let minDue: AbsoluteTime = AbsoluteTime.never(); let minDue: AbsoluteTime = AbsoluteTime.never();
for (const p of pending.pendingOperations) { for (const p of pending.pendingOperations) {
minDue = AbsoluteTime.min(minDue, p.timestampDue); minDue = AbsoluteTime.min(minDue, p.timestampDue);
if (AbsoluteTime.isExpired(p.timestampDue)) { if (AbsoluteTime.isExpired(p.timestampDue)) {
@ -683,9 +684,13 @@ async function dumpCoins(ws: InternalWalletState): Promise<CoinDumpJson> {
c.exchangeBaseUrl, c.exchangeBaseUrl,
c.denomPubHash, c.denomPubHash,
); );
if (!denomInfo) {
console.error("no denomination found for coin")
continue;
}
coinsJson.coins.push({ coinsJson.coins.push({
coin_pub: c.coinPub, coin_pub: c.coinPub,
denom_pub: denomInfo?.denomPub!, denom_pub: denomInfo.denomPub,
denom_pub_hash: c.denomPubHash, denom_pub_hash: c.denomPubHash,
denom_value: Amounts.stringify(denom.value), denom_value: Amounts.stringify(denom.value),
exchange_base_url: c.exchangeBaseUrl, exchange_base_url: c.exchangeBaseUrl,