diff --git a/packages/taler-wallet-core/src/bank-api-client.ts b/packages/taler-wallet-core/src/bank-api-client.ts index 14bf07174..fb859ece3 100644 --- a/packages/taler-wallet-core/src/bank-api-client.ts +++ b/packages/taler-wallet-core/src/bank-api-client.ts @@ -120,7 +120,10 @@ export namespace BankApi { if (respJson.paytoUri) { paytoUri = respJson.paytoUri; } - } catch (e) {} + } catch (e) { + logger.error("error trying to parse json from response", e); + throw TalerError.fromException(e); + } return { password, username, diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index a0a603ca3..16a88fe7c 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -15,54 +15,30 @@ */ import { - BackupPurchase, AmountJson, - Amounts, - BackupDenomSel, - WalletBackupContentV1, - BackupCoinSourceType, - BackupProposalStatus, - codecForContractTerms, - BackupRefundState, - RefreshReason, - BackupRefreshReason, - DenomKeyType, - AbsoluteTime, - TalerProtocolTimestamp, + Amounts, BackupCoinSourceType, BackupDenomSel, BackupProposalStatus, + BackupPurchase, BackupRefreshReason, BackupRefundState, codecForContractTerms, + DenomKeyType, j2s, Logger, RefreshReason, TalerProtocolTimestamp, + WalletBackupContentV1 } from "@gnu-taler/taler-util"; import { - WalletContractData, - DenomSelectionState, - DenominationVerificationStatus, - CoinSource, + AbortStatus, CoinSource, CoinSourceType, - CoinStatus, - ReserveBankInfo, - ReserveRecordStatus, - ProposalDownload, - ProposalStatus, - WalletRefundItem, - RefundState, - AbortStatus, - RefreshSessionRecord, - WireInfo, - WalletStoresV1, - RefreshCoinStatus, - OperationStatus, + CoinStatus, DenominationVerificationStatus, DenomSelectionState, OperationStatus, ProposalDownload, + ProposalStatus, RefreshCoinStatus, RefreshSessionRecord, RefundState, ReserveBankInfo, + ReserveRecordStatus, WalletContractData, WalletRefundItem, WalletStoresV1, WireInfo } from "../../db.js"; +import { InternalWalletState } from "../../internal-wallet-state.js"; import { PayCoinSelection } from "../../util/coinSelection.js"; -import { j2s } from "@gnu-taler/taler-util"; import { checkDbInvariant, - checkLogicInvariant, + checkLogicInvariant } 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 { 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"); @@ -276,7 +252,7 @@ export async function importBackup( protocolVersionRange: backupExchange.protocol_version_range, }, permanent: true, - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), lastUpdate: undefined, nextUpdate: TalerProtocolTimestamp.now(), nextRefreshCheck: TalerProtocolTimestamp.now(), @@ -464,7 +440,7 @@ export async function importBackup( timestampReserveInfoPosted: backupReserve.bank_info?.timestamp_reserve_info_posted, senderWire: backupReserve.sender_wire, - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), lastError: undefined, initialWithdrawalGroupId: backupReserve.initial_withdrawal_group_id, @@ -505,7 +481,7 @@ export async function importBackup( backupWg.raw_withdrawal_amount, ), reservePub, - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), secretSeed: backupWg.secret_seed, timestampStart: backupWg.timestamp_created, timestampFinish: backupWg.timestamp_finish, @@ -618,7 +594,7 @@ export async function importBackup( cryptoComp.proposalNoncePrivToPub[backupProposal.nonce_priv], proposalId: backupProposal.proposal_id, repurchaseProposalId: backupProposal.repurchase_proposal_id, - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), download, proposalStatus, }); @@ -753,7 +729,7 @@ export async function importBackup( cryptoComp.proposalNoncePrivToPub[backupPurchase.nonce_priv], lastPayError: undefined, autoRefundDeadline: TalerProtocolTimestamp.never(), - refundStatusRetryInfo: resetRetryInfo(), + refundStatusRetryInfo: RetryInfo.reset(), lastRefundStatusError: undefined, refundAwaiting: undefined, timestampAccept: backupPurchase.timestamp_accept, @@ -764,7 +740,7 @@ export async function importBackup( lastSessionId: undefined, abortStatus, // FIXME! - payRetryInfo: resetRetryInfo(), + payRetryInfo: RetryInfo.reset(), download, paymentSubmitPending: !backupPurchase.timestamp_first_successful_pay, @@ -865,7 +841,7 @@ export async function importBackup( Amounts.parseOrThrow(x.estimated_output_amount), ), refreshSessionPerCoin, - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), }); } } @@ -891,7 +867,7 @@ export async function importBackup( merchantBaseUrl: backupTip.exchange_base_url, merchantTipId: backupTip.merchant_tip_id, pickedUpTimestamp: backupTip.timestamp_finished, - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), secretSeed: backupTip.secret_seed, tipAmountEffective: denomsSel.totalCoinValue, tipAmountRaw: Amounts.parseOrThrow(backupTip.tip_amount_raw), diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts b/packages/taler-wallet-core/src/operations/backup/index.ts index 0b2bd7b80..69ba1ddbc 100644 --- a/packages/taler-wallet-core/src/operations/backup/index.ts +++ b/packages/taler-wallet-core/src/operations/backup/index.ts @@ -25,10 +25,9 @@ * Imports. */ import { - AmountString, + AbsoluteTime, AmountString, BackupRecovery, - buildCodecForObject, - canonicalizeBaseUrl, + buildCodecForObject, bytesToString, canonicalizeBaseUrl, canonicalJson, Codec, codecForAmountString, @@ -37,39 +36,22 @@ import { codecForNumber, codecForString, codecOptional, - ConfirmPayResultType, - DenomKeyType, - 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, + ConfirmPayResultType, decodeCrock, DenomKeyType, + durationFromSpec, eddsaGetPublic, EddsaKeyPair, encodeCrock, getRandomBytes, - hash, - rsaBlind, - stringToBytes, + hash, hashDenomPub, + HttpStatusCode, + j2s, kdf, Logger, + notEmpty, + PreparePayResultType, + RecoveryLoadRequest, + RecoveryMergeStrategy, rsaBlind, secretbox, secretbox_open, stringToBytes, TalerErrorDetail, TalerProtocolTimestamp, URL, + WalletBackupContentV1 } from "@gnu-taler/taler-util"; -import { CryptoDispatcher } from "../../crypto/workers/cryptoDispatcher.js"; +import { gunzipSync, gzipSync } from "fflate"; +import { TalerCryptoInterface } from "../../crypto/cryptoImplementation.js"; import { BackupProviderRecord, BackupProviderState, @@ -78,28 +60,28 @@ import { ConfigRecord, WalletBackupConfState, WalletStoresV1, - WALLET_BACKUP_STATE_KEY, + WALLET_BACKUP_STATE_KEY } from "../../db.js"; +import { InternalWalletState } from "../../internal-wallet-state.js"; import { readSuccessResponseJsonOrThrow, - readTalerErrorResponse, + readTalerErrorResponse } from "../../util/http.js"; import { checkDbInvariant, - checkLogicInvariant, + checkLogicInvariant } from "../../util/invariants.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 { checkPaymentByProposalId, confirmPay, - preparePayForUri, + preparePayForUri } from "../pay.js"; import { exportBackup } from "./export.js"; import { BackupCryptoPrecomputedData, importBackup } from "./import.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"); @@ -309,8 +291,8 @@ async function runBackupCycleForProvider( "if-none-match": newHash, ...(provider.lastBackupHash ? { - "if-match": provider.lastBackupHash, - } + "if-match": provider.lastBackupHash, + } : {}), }, }); @@ -344,7 +326,7 @@ async function runBackupCycleForProvider( } const res = await preparePayForUri(ws, talerUri); let proposalId = res.proposalId; - let doPay: boolean = false; + let doPay = false; switch (res.status) { case PreparePayResultType.InsufficientBalance: // FIXME: record in provider state! @@ -434,7 +416,7 @@ async function runBackupCycleForProvider( // FIXME: Allocate error code for this situation? prov.state = { tag: BackupProviderStateTag.Retrying, - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), }; await tx.backupProvider.put(prov); }); @@ -472,13 +454,12 @@ async function incrementBackupRetryInTx( return; } if (pr.state.tag === BackupProviderStateTag.Retrying) { - pr.state.retryInfo.retryCounter++; pr.state.lastError = err; - updateRetryInfoTimeout(pr.state.retryInfo); + pr.state.retryInfo = RetryInfo.increment(pr.state.retryInfo); } else if (pr.state.tag === BackupProviderStateTag.Ready) { pr.state = { tag: BackupProviderStateTag.Retrying, - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), lastError: err, }; } @@ -685,7 +666,9 @@ export async function addBackupProvider( }); } -export async function restoreFromRecoverySecret(): Promise {} +export async function restoreFromRecoverySecret(): Promise { + return; +} /** * Information about one provider. diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts index 2f5f9aa15..27460849a 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -47,7 +47,7 @@ import { DepositGroupRecord, OperationStatus, WireFee } from "../db.js"; import { InternalWalletState } from "../internal-wallet-state.js"; import { PayCoinSelection, selectPayCoins } from "../util/coinSelection.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 { getExchangeDetails } from "./exchanges.js"; import { @@ -85,7 +85,7 @@ async function setupDepositGroupRetry( return; } if (options.resetRetry) { - x.retryInfo = resetRetryInfo(); + x.retryInfo = RetryInfo.reset(); } else { x.retryInfo = RetryInfo.increment(x.retryInfo); } @@ -599,7 +599,7 @@ export async function createDepositGroup( payto_uri: req.depositPaytoUri, salt: wireSalt, }, - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), operationStatus: OperationStatus.Pending, lastError: undefined, }; diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index 72cbdc15b..b10505b27 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -63,7 +63,7 @@ import { readSuccessResponseTextOrThrow, } from "../util/http.js"; import { DbAccess, GetReadOnlyAccess } from "../util/query.js"; -import { resetRetryInfo, RetryInfo } from "../util/retries.js"; +import { RetryInfo } from "../util/retries.js"; import { WALLET_CACHE_BREAKER_CLIENT_VERSION, WALLET_EXCHANGE_PROTOCOL_VERSION, @@ -116,6 +116,9 @@ async function reportExchangeUpdateError( if (!exchange) { return; } + if (!exchange.retryInfo) { + logger.reportBreak(); + } exchange.lastError = err; await tx.exchanges.put(exchange); }); @@ -137,7 +140,7 @@ async function setupExchangeUpdateRetry( return; } if (options.reset) { - exchange.retryInfo = resetRetryInfo(); + exchange.retryInfo = RetryInfo.reset(); } else { exchange.retryInfo = RetryInfo.increment(exchange.retryInfo); } @@ -399,7 +402,7 @@ async function provideExchangeRecord( const r: ExchangeRecord = { permanent: true, baseUrl: baseUrl, - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), detailsPointer: undefined, lastUpdate: undefined, nextUpdate: AbsoluteTime.toTimestamp(now), diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts index 325d07bd1..ceba7ef8c 100644 --- a/packages/taler-wallet-core/src/operations/pay.ts +++ b/packages/taler-wallet-core/src/operations/pay.ts @@ -97,10 +97,7 @@ import { } from "../util/http.js"; import { GetReadWriteAccess } from "../util/query.js"; import { - getRetryDuration, - resetRetryInfo, RetryInfo, - updateRetryInfoTimeout, } from "../util/retries.js"; import { getExchangeDetails } from "./exchanges.js"; import { createRefreshGroup, getTotalRefreshCost } from "./refresh.js"; @@ -438,8 +435,8 @@ async function recordConfirmPay( proposalId: proposal.proposalId, lastPayError: undefined, lastRefundStatusError: undefined, - payRetryInfo: resetRetryInfo(), - refundStatusRetryInfo: resetRetryInfo(), + payRetryInfo: RetryInfo.reset(), + refundStatusRetryInfo: RetryInfo.reset(), refundQueryRequested: false, timestampFirstSuccessfulPay: undefined, autoRefundDeadline: undefined, @@ -494,6 +491,7 @@ async function reportProposalError( logger.error( `Asked to report an error for a proposal (${proposalId}) that is not active (no retryInfo)`, ); + logger.reportBreak(); return; } pr.lastError = err; @@ -517,7 +515,7 @@ async function setupProposalRetry( return; } if (options.reset) { - pr.retryInfo = resetRetryInfo(); + pr.retryInfo = RetryInfo.reset(); } else { pr.retryInfo = RetryInfo.increment(pr.retryInfo); } @@ -541,7 +539,7 @@ async function setupPurchasePayRetry( return; } if (options.reset) { - p.payRetryInfo = resetRetryInfo(); + p.payRetryInfo = RetryInfo.reset(); } else { p.payRetryInfo = RetryInfo.increment(p.payRetryInfo); } @@ -610,7 +608,7 @@ async function failProposalPermanently( function getProposalRequestTimeout(proposal: ProposalRecord): Duration { return durationMax( { 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, proposalStatus: ProposalStatus.Downloading, repurchaseProposalId: undefined, - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), lastError: undefined, downloadSessionId: sessionId, }; @@ -986,14 +984,14 @@ async function storeFirstPaySuccess( purchase.paymentSubmitPending = false; purchase.lastPayError = undefined; purchase.lastSessionId = sessionId; - purchase.payRetryInfo = resetRetryInfo(); + purchase.payRetryInfo = RetryInfo.reset(); purchase.merchantPaySig = paySig; const protoAr = purchase.download.contractData.autoRefund; if (protoAr) { const ar = Duration.fromTalerProtocolDuration(protoAr); logger.info("auto_refund present"); purchase.refundQueryRequested = true; - purchase.refundStatusRetryInfo = resetRetryInfo(); + purchase.refundStatusRetryInfo = RetryInfo.reset(); purchase.lastRefundStatusError = undefined; purchase.autoRefundDeadline = AbsoluteTime.toTimestamp( AbsoluteTime.addDuration(AbsoluteTime.now(), ar), @@ -1023,7 +1021,7 @@ async function storePayReplaySuccess( } purchase.paymentSubmitPending = false; purchase.lastPayError = undefined; - purchase.payRetryInfo = resetRetryInfo(); + purchase.payRetryInfo = RetryInfo.reset(); purchase.lastSessionId = sessionId; await tx.purchases.put(purchase); }); diff --git a/packages/taler-wallet-core/src/operations/recoup.ts b/packages/taler-wallet-core/src/operations/recoup.ts index 4fac2ec16..d36a10287 100644 --- a/packages/taler-wallet-core/src/operations/recoup.ts +++ b/packages/taler-wallet-core/src/operations/recoup.ts @@ -26,38 +26,28 @@ */ import { Amounts, - codecForRecoupConfirmation, - j2s, - NotificationType, + codecForRecoupConfirmation, encodeCrock, getRandomBytes, j2s, Logger, NotificationType, RefreshReason, TalerErrorDetail, - TalerProtocolTimestamp, + TalerProtocolTimestamp, URL } from "@gnu-taler/taler-util"; -import { encodeCrock, getRandomBytes } from "@gnu-taler/taler-util"; import { CoinRecord, CoinSourceType, - CoinStatus, - RecoupGroupRecord, + CoinStatus, OperationStatus, RecoupGroupRecord, RefreshCoinSource, - ReserveRecordStatus, - WithdrawCoinSource, - WalletStoresV1, - OperationStatus, + ReserveRecordStatus, WalletStoresV1, WithdrawCoinSource } from "../db.js"; - +import { InternalWalletState } from "../internal-wallet-state.js"; import { readSuccessResponseJsonOrThrow } from "../util/http.js"; -import { Logger, URL } from "@gnu-taler/taler-util"; +import { GetReadWriteAccess } from "../util/query.js"; import { - resetRetryInfo, - RetryInfo, - updateRetryInfoTimeout, + RetryInfo } from "../util/retries.js"; +import { guardOperationException } from "./common.js"; import { createRefreshGroup, processRefreshGroup } from "./refresh.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"); @@ -78,7 +68,7 @@ async function setupRecoupRetry( return; } if (options.reset) { - r.retryInfo = resetRetryInfo(); + r.retryInfo = RetryInfo.reset(); } else { r.retryInfo = RetryInfo.increment(r.retryInfo); } @@ -139,7 +129,7 @@ async function putGroupAsFinished( if (allFinished) { logger.info("all recoups of recoup group are finished"); recoupGroup.timestampFinished = TalerProtocolTimestamp.now(); - recoupGroup.retryInfo = resetRetryInfo(); + recoupGroup.retryInfo = RetryInfo.reset(); recoupGroup.lastError = undefined; if (recoupGroup.scheduleRefreshCoins.length > 0) { const refreshGroupId = await createRefreshGroup( @@ -278,7 +268,7 @@ async function recoupWithdrawCoin( const currency = updatedCoin.currentAmount.currency; updatedCoin.currentAmount = Amounts.getZero(currency); updatedReserve.reserveStatus = ReserveRecordStatus.QueryingStatus; - updatedReserve.retryInfo = resetRetryInfo(); + updatedReserve.retryInfo = RetryInfo.reset(); updatedReserve.operationStatus = OperationStatus.Pending; await tx.coins.put(updatedCoin); await tx.reserves.put(updatedReserve); @@ -482,7 +472,7 @@ export async function createRecoupGroup( lastError: undefined, timestampFinished: undefined, timestampStarted: TalerProtocolTimestamp.now(), - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), recoupFinishedPerCoin: coinPubs.map(() => false), // Will be populated later oldAmountPerCoin: [], diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index 215676118..ce8fd003a 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -15,20 +15,28 @@ */ import { - AgeCommitment, - AgeRestriction, - CoinPublicKeyString, - DenomKeyType, - encodeCrock, + AbsoluteTime, AgeCommitment, + AgeRestriction, AmountJson, Amounts, amountToPretty, codecForExchangeMeltResponse, + codecForExchangeRevealResponse, + CoinPublicKey, CoinPublicKeyString, + DenomKeyType, Duration, + durationFromSpec, + durationMul, encodeCrock, ExchangeMeltRequest, - ExchangeProtocolVersion, - ExchangeRefreshRevealRequest, - getRandomBytes, + ExchangeProtocolVersion, ExchangeRefreshRevealRequest, fnutil, getRandomBytes, HashCodeString, HttpStatusCode, - j2s, - TalerProtocolTimestamp, + j2s, Logger, NotificationType, + RefreshGroupId, + RefreshReason, + TalerErrorDetail, TalerProtocolTimestamp, URL } 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 { CoinRecord, CoinSourceType, @@ -37,57 +45,29 @@ import { OperationStatus, RefreshCoinStatus, RefreshGroupRecord, - WalletStoresV1, + WalletStoresV1 } from "../db.js"; -import { - 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 { TalerError } from "../errors.js"; import { DenomInfo, EXCHANGE_COINS_LOCK, - InternalWalletState, + InternalWalletState } from "../internal-wallet-state.js"; import { - isWithdrawableDenom, - selectWithdrawalDenominations, -} from "./withdraw.js"; -import { - DerivedRefreshSession, - RefreshNewDenomInfo, -} from "../crypto/cryptoTypes.js"; + readSuccessResponseJsonOrThrow, + readUnexpectedResponseDetails +} from "../util/http.js"; +import { checkDbInvariant } from "../util/invariants.js"; import { GetReadWriteAccess } from "../util/query.js"; +import { + RetryInfo +} from "../util/retries.js"; import { guardOperationException } from "./common.js"; -import { CryptoApiStoppedError } from "../crypto/workers/cryptoDispatcher.js"; -import { TalerCryptoInterface } from "../crypto/cryptoImplementation.js"; -import { TalerError } from "../errors.js"; +import { updateExchangeFromUrl } from "./exchanges.js"; +import { + isWithdrawableDenom, + selectWithdrawalDenominations +} from "./withdraw.js"; const logger = new Logger("refresh.ts"); @@ -129,22 +109,22 @@ export function getTotalRefreshCost( } function updateGroupStatus(rg: RefreshGroupRecord): void { - let allDone = fnutil.all( + const allDone = fnutil.all( rg.statusPerCoin, (x) => x === RefreshCoinStatus.Finished || x === RefreshCoinStatus.Frozen, ); - let anyFrozen = fnutil.any( + const anyFrozen = fnutil.any( rg.statusPerCoin, (x) => x === RefreshCoinStatus.Frozen, ); if (allDone) { if (anyFrozen) { rg.frozen = true; - rg.retryInfo = resetRetryInfo(); + rg.retryInfo = RetryInfo.reset(); } else { rg.timestampFinished = AbsoluteTime.toTimestamp(AbsoluteTime.now()); rg.operationStatus = OperationStatus.Finished; - rg.retryInfo = resetRetryInfo(); + rg.retryInfo = RetryInfo.reset(); } } } @@ -753,7 +733,7 @@ async function setupRefreshRetry( return; } if (options.reset) { - r.retryInfo = resetRetryInfo(); + r.retryInfo = RetryInfo.reset(); } else { r.retryInfo = RetryInfo.increment(r.retryInfo); } @@ -987,7 +967,7 @@ export async function createRefreshGroup( reason, refreshGroupId, refreshSessionPerCoin: oldCoinPubs.map(() => undefined), - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), inputPerCoin, estimatedOutputPerCoin, timestampCreated: TalerProtocolTimestamp.now(), diff --git a/packages/taler-wallet-core/src/operations/refund.ts b/packages/taler-wallet-core/src/operations/refund.ts index e5ce37a83..186fbf7d3 100644 --- a/packages/taler-wallet-core/src/operations/refund.ts +++ b/packages/taler-wallet-core/src/operations/refund.ts @@ -25,29 +25,18 @@ */ import { AbortingCoin, - AbortRequest, - AmountJson, + AbortRequest, AbsoluteTime, AmountJson, Amounts, ApplyRefundResponse, codecForAbortResponse, - codecForMerchantOrderRefundPickupResponse, - CoinPublicKey, - Logger, + codecForMerchantOrderRefundPickupResponse, codecForMerchantOrderStatusPaid, CoinPublicKey, Duration, Logger, MerchantCoinRefundFailureStatus, MerchantCoinRefundStatus, MerchantCoinRefundSuccessStatus, NotificationType, - parseRefundUri, - RefreshReason, + parseRefundUri, PrepareRefundResult, RefreshReason, TalerErrorCode, - TalerErrorDetail, - URL, - codecForMerchantOrderStatusPaid, - AbsoluteTime, - TalerProtocolTimestamp, - Duration, - PrepareRefundRequest, - PrepareRefundResult, + TalerErrorDetail, TalerProtocolTimestamp, URL } from "@gnu-taler/taler-util"; import { AbortStatus, @@ -55,19 +44,17 @@ import { PurchaseRecord, RefundReason, RefundState, - WalletStoresV1, + WalletStoresV1 } from "../db.js"; +import { InternalWalletState } from "../internal-wallet-state.js"; import { readSuccessResponseJsonOrThrow } from "../util/http.js"; import { checkDbInvariant } from "../util/invariants.js"; import { GetReadWriteAccess } from "../util/query.js"; import { - resetRetryInfo, - RetryInfo, - updateRetryInfoTimeout, + RetryInfo } from "../util/retries.js"; -import { createRefreshGroup, getTotalRefreshCost } from "./refresh.js"; -import { InternalWalletState } from "../internal-wallet-state.js"; import { guardOperationException } from "./common.js"; +import { createRefreshGroup, getTotalRefreshCost } from "./refresh.js"; const logger = new Logger("refund.ts"); @@ -147,7 +134,7 @@ async function setupPurchaseQueryRefundRetry( return; } if (options.reset) { - pr.refundStatusRetryInfo = resetRetryInfo(); + pr.refundStatusRetryInfo = RetryInfo.reset(); } else { pr.refundStatusRetryInfo = RetryInfo.increment( pr.refundStatusRetryInfo, @@ -500,7 +487,7 @@ async function acceptRefunds( if (queryDone) { p.timestampLastRefundStatus = now; p.lastRefundStatusError = undefined; - p.refundStatusRetryInfo = resetRetryInfo(); + p.refundStatusRetryInfo = RetryInfo.reset(); p.refundQueryRequested = false; if (p.abortStatus === AbortStatus.AbortRefund) { p.abortStatus = AbortStatus.AbortFinished; @@ -509,8 +496,7 @@ async function acceptRefunds( } else { // No error, but we need to try again! p.timestampLastRefundStatus = now; - p.refundStatusRetryInfo.retryCounter++; - updateRetryInfoTimeout(p.refundStatusRetryInfo); + p.refundStatusRetryInfo = RetryInfo.increment(p.refundStatusRetryInfo) p.lastRefundStatusError = undefined; logger.trace("refund query not done"); } @@ -619,7 +605,7 @@ export async function applyRefund( } p.refundQueryRequested = true; p.lastRefundStatusError = undefined; - p.refundStatusRetryInfo = resetRetryInfo(); + p.refundStatusRetryInfo = RetryInfo.reset(); await tx.purchases.put(p); return true; }); @@ -892,7 +878,7 @@ export async function abortFailedPayWithRefund( purchase.paymentSubmitPending = false; purchase.abortStatus = AbortStatus.AbortRefund; purchase.lastPayError = undefined; - purchase.payRetryInfo = resetRetryInfo(); + purchase.payRetryInfo = RetryInfo.reset(); await tx.purchases.put(purchase); }); processPurchaseQueryRefund(ws, proposalId, { diff --git a/packages/taler-wallet-core/src/operations/reserves.ts b/packages/taler-wallet-core/src/operations/reserves.ts index 8e606bd60..8ee620d94 100644 --- a/packages/taler-wallet-core/src/operations/reserves.ts +++ b/packages/taler-wallet-core/src/operations/reserves.ts @@ -58,8 +58,6 @@ import { } from "../util/http.js"; import { GetReadOnlyAccess } from "../util/query.js"; import { - getRetryDuration, - resetRetryInfo, RetryInfo, } from "../util/retries.js"; import { @@ -100,7 +98,7 @@ async function setupReserveRetry( return; } if (options.reset) { - r.retryInfo = resetRetryInfo(); + r.retryInfo = RetryInfo.reset(); } else { r.retryInfo = RetryInfo.increment(r.retryInfo); } @@ -196,7 +194,7 @@ export async function createReserve( timestampReserveInfoPosted: undefined, bankInfo, reserveStatus, - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), lastError: undefined, currency: req.amount.currency, operationStatus: OperationStatus.Pending, @@ -297,7 +295,7 @@ export async function forceQueryReserve( case ReserveRecordStatus.Dormant: reserve.reserveStatus = ReserveRecordStatus.QueryingStatus; reserve.operationStatus = OperationStatus.Pending; - reserve.retryInfo = resetRetryInfo(); + reserve.retryInfo = RetryInfo.reset(); break; default: break; @@ -392,7 +390,7 @@ async function registerReserveWithBank( if (!r.bankInfo) { throw Error("invariant failed"); } - r.retryInfo = resetRetryInfo(); + r.retryInfo = RetryInfo.reset(); await tx.reserves.put(r); }); ws.notify({ type: NotificationType.ReserveRegisteredWithBank }); @@ -402,7 +400,7 @@ async function registerReserveWithBank( export function getReserveRequestTimeout(r: ReserveRecord): Duration { return durationMax( { 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.reserveStatus = ReserveRecordStatus.BankAborted; r.operationStatus = OperationStatus.Finished; - r.retryInfo = resetRetryInfo(); + r.retryInfo = RetryInfo.reset(); await tx.reserves.put(r); }); return; @@ -496,7 +494,7 @@ async function processReserveBankStatus( r.timestampBankConfirmed = now; r.reserveStatus = ReserveRecordStatus.QueryingStatus; r.operationStatus = OperationStatus.Pending; - r.retryInfo = resetRetryInfo(); + r.retryInfo = RetryInfo.reset(); } else { switch (r.reserveStatus) { case ReserveRecordStatus.WaitConfirmBank: @@ -555,7 +553,7 @@ async function updateReserve( if ( resp.status === 404 && result.talerErrorResponse.code === - TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN + TalerErrorCode.EXCHANGE_RESERVES_STATUS_UNKNOWN ) { ws.notify({ type: NotificationType.ReserveNotYetFound, @@ -662,7 +660,7 @@ async function updateReserve( reservePub: reserve.reservePub, rawWithdrawalAmount: remainingAmount, timestampStart: AbsoluteTime.toTimestamp(AbsoluteTime.now()), - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), lastError: undefined, denomsSel: denomSel, secretSeed: encodeCrock(getRandomBytes(64)), @@ -721,12 +719,13 @@ async function processReserveImpl( case ReserveRecordStatus.RegisteringBank: await processReserveBankStatus(ws, reservePub); return await processReserveImpl(ws, reservePub, { forceNow: true }); - case ReserveRecordStatus.QueryingStatus: + case ReserveRecordStatus.QueryingStatus: { const res = await updateReserve(ws, reservePub); if (res.ready) { return await processReserveImpl(ws, reservePub, { forceNow: true }); } break; + } case ReserveRecordStatus.Dormant: // nothing to do break; diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts index c0dcae911..da7673f35 100644 --- a/packages/taler-wallet-core/src/operations/tip.ts +++ b/packages/taler-wallet-core/src/operations/tip.ts @@ -18,51 +18,30 @@ * Imports. */ import { - PrepareTipResult, - parseTipUri, - codecForTipPickupGetResponse, - Amounts, - TalerErrorDetail, - NotificationType, - TipPlanchetDetail, - TalerErrorCode, - Logger, - URL, - DenomKeyType, - BlindedDenominationSignature, - codecForMerchantTipResponseV2, - TalerProtocolTimestamp, + Amounts, BlindedDenominationSignature, + codecForMerchantTipResponseV2, codecForTipPickupGetResponse, DenomKeyType, encodeCrock, getRandomBytes, j2s, Logger, NotificationType, parseTipUri, PrepareTipResult, TalerErrorCode, TalerErrorDetail, TalerProtocolTimestamp, TipPlanchetDetail, URL } from "@gnu-taler/taler-util"; import { DerivedTipPlanchet } from "../crypto/cryptoTypes.js"; import { - DenominationRecord, CoinRecord, CoinSourceType, - CoinStatus, - TipRecord, + CoinStatus, DenominationRecord, TipRecord } 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 { updateExchangeFromUrl } from "./exchanges.js"; import { InternalWalletState } from "../internal-wallet-state.js"; -import { - getExchangeWithdrawalInfo, - updateWithdrawalDenoms, - getCandidateWithdrawalDenoms, - selectWithdrawalDenominations, -} from "./withdraw.js"; import { getHttpResponseErrorDetails, - readSuccessResponseJsonOrThrow, + readSuccessResponseJsonOrThrow } 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 { updateExchangeFromUrl } from "./exchanges.js"; +import { + getCandidateWithdrawalDenoms, getExchangeWithdrawalInfo, selectWithdrawalDenominations, updateWithdrawalDenoms +} from "./withdraw.js"; const logger = new Logger("operations/tip.ts"); @@ -130,7 +109,7 @@ export async function prepareTip( createdTimestamp: TalerProtocolTimestamp.now(), merchantTipId: res.merchantTipId, tipAmountEffective: selectedDenoms.totalCoinValue, - retryInfo: resetRetryInfo(), + retryInfo: RetryInfo.reset(), lastError: undefined, denomsSel: selectedDenoms, pickedUpTimestamp: undefined, @@ -202,7 +181,7 @@ async function setupTipRetry( return; } if (options.reset) { - t.retryInfo = resetRetryInfo(); + t.retryInfo = RetryInfo.reset(); } else { t.retryInfo = RetryInfo.increment(t.retryInfo); } @@ -237,7 +216,7 @@ async function resetTipRetry( .runReadWrite(async (tx) => { const x = await tx.tips.get(tipId); if (x) { - x.retryInfo = resetRetryInfo(); + x.retryInfo = RetryInfo.reset(); await tx.tips.put(x); } }); @@ -430,7 +409,7 @@ async function processTipImpl( } tr.pickedUpTimestamp = TalerProtocolTimestamp.now(); tr.lastError = undefined; - tr.retryInfo = resetRetryInfo(); + tr.retryInfo = RetryInfo.reset(); await tx.tips.put(tr); for (const cr of newCoinRecords) { await tx.coins.put(cr); diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 2edc3ed98..ea9e22331 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -72,7 +72,7 @@ import { readSuccessResponseJsonOrThrow, } from "../util/http.js"; import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js"; -import { resetRetryInfo, RetryInfo } from "../util/retries.js"; +import { RetryInfo } from "../util/retries.js"; import { WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, WALLET_EXCHANGE_PROTOCOL_VERSION, @@ -215,7 +215,7 @@ export function selectWithdrawalDenominations( for (const d of denoms) { let count = 0; const cost = Amounts.add(d.value, d.feeWithdraw).amount; - for (;;) { + for (; ;) { if (Amounts.cmp(remaining, cost) < 0) { break; } @@ -875,11 +875,10 @@ export async function updateWithdrawalDenoms( denom.verificationStatus === DenominationVerificationStatus.Unverified ) { logger.trace( - `Validating denomination (${current + 1}/${ - denominations.length + `Validating denomination (${current + 1}/${denominations.length }) signature of ${denom.denomPubHash}`, ); - let valid: boolean = false; + let valid = false; if (ws.insecureTrustExchange) { valid = true; } else { @@ -932,7 +931,7 @@ async function setupWithdrawalRetry( return; } if (options.reset) { - wsr.retryInfo = resetRetryInfo(); + wsr.retryInfo = RetryInfo.reset(); } else { wsr.retryInfo = RetryInfo.increment(wsr.retryInfo); } @@ -1097,7 +1096,7 @@ async function processWithdrawGroupImpl( wg.timestampFinish = TalerProtocolTimestamp.now(); wg.operationStatus = OperationStatus.Finished; delete wg.lastError; - wg.retryInfo = resetRetryInfo(); + wg.retryInfo = RetryInfo.reset(); } await tx.withdrawalGroups.put(wg); @@ -1203,7 +1202,7 @@ export async function getExchangeWithdrawalInfo( ) { console.warn( `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`, ); } } diff --git a/packages/taler-wallet-core/src/util/retries.ts b/packages/taler-wallet-core/src/util/retries.ts index 8b7d64406..965ba033a 100644 --- a/packages/taler-wallet-core/src/util/retries.ts +++ b/packages/taler-wallet-core/src/util/retries.ts @@ -41,7 +41,7 @@ const defaultRetryPolicy: RetryPolicy = { maxTimeout: { d_ms: 6000 }, }; -export function updateRetryInfoTimeout( +function updateTimeout( r: RetryInfo, p: RetryPolicy = defaultRetryPolicy, ): void { @@ -65,45 +65,46 @@ export function updateRetryInfoTimeout( 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 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( r: RetryInfo | undefined, p: RetryPolicy = defaultRetryPolicy, - ) { + ): RetryInfo { if (!r) { - return resetRetryInfo(p); + return reset(p); } const r2 = { ...r }; r2.retryCounter++; - updateRetryInfoTimeout(r2, p); + updateTimeout(r2, p); return r2; } } diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 0590251e3..e2d504446 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -335,6 +335,7 @@ async function runTaskLoop( let numGivingLiveness = 0; let numDue = 0; let minDue: AbsoluteTime = AbsoluteTime.never(); + for (const p of pending.pendingOperations) { minDue = AbsoluteTime.min(minDue, p.timestampDue); if (AbsoluteTime.isExpired(p.timestampDue)) { @@ -683,9 +684,13 @@ async function dumpCoins(ws: InternalWalletState): Promise { c.exchangeBaseUrl, c.denomPubHash, ); + if (!denomInfo) { + console.error("no denomination found for coin") + continue; + } coinsJson.coins.push({ coin_pub: c.coinPub, - denom_pub: denomInfo?.denomPub!, + denom_pub: denomInfo.denomPub, denom_pub_hash: c.denomPubHash, denom_value: Amounts.stringify(denom.value), exchange_base_url: c.exchangeBaseUrl,