diff --git a/packages/taler-util/src/transactions-types.ts b/packages/taler-util/src/transactions-types.ts index 304183ceb..63db206bd 100644 --- a/packages/taler-util/src/transactions-types.ts +++ b/packages/taler-util/src/transactions-types.ts @@ -67,7 +67,7 @@ export interface TransactionsRequest { */ includeRefreshes?: boolean; - filterByState?: TransactionStateFilter + filterByState?: TransactionStateFilter; } export interface TransactionState { @@ -629,6 +629,17 @@ export interface TransactionRefresh extends TransactionCommon { refreshOutputAmount: AmountString; } +export interface DepositTransactionTrackingState { + // Raw wire transfer identifier of the deposit. + wireTransferId: string; + // When was the wire transfer given to the bank. + timestampExecuted: TalerProtocolTimestamp; + // Total amount transfer for this wtid (including fees) + amountRaw: AmountString; + // Wire fee amount for this exchange + wireFee: AmountString; +} + /** * Deposit transaction, which effectively sends * money from this wallet somewhere else. @@ -662,16 +673,7 @@ export interface TransactionDeposit extends TransactionCommon { */ deposited: boolean; - trackingState: Array<{ - // Raw wire transfer identifier of the deposit. - wireTransferId: string; - // When was the wire transfer given to the bank. - timestampExecuted: TalerProtocolTimestamp; - // Total amount transfer for this wtid (including fees) - amountRaw: AmountString; - // Wire fee amount for this exchange - wireFee: AmountString; - }>; + trackingState: Array; } export interface TransactionByIdRequest { diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts index 35777e714..56392f090 100644 --- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts +++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts @@ -87,7 +87,7 @@ import { WithdrawalPlanchet, } from "@gnu-taler/taler-util"; // FIXME: Crypto should not use DB Types! -import { DenominationRecord } from "../db.js"; +import { DenominationRecord, timestampProtocolFromDb } from "../db.js"; import { CreateRecoupRefreshReqRequest, CreateRecoupReqRequest, @@ -962,10 +962,22 @@ export const nativeCryptoR: TalerCryptoInterfaceR = { const value: AmountJson = Amounts.parseOrThrow(denom.value); const p = buildSigPS(TalerSignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY) .put(decodeCrock(masterPub)) - .put(timestampRoundedToBuffer(denom.stampStart)) - .put(timestampRoundedToBuffer(denom.stampExpireWithdraw)) - .put(timestampRoundedToBuffer(denom.stampExpireDeposit)) - .put(timestampRoundedToBuffer(denom.stampExpireLegal)) + .put(timestampRoundedToBuffer(timestampProtocolFromDb(denom.stampStart))) + .put( + timestampRoundedToBuffer( + timestampProtocolFromDb(denom.stampExpireWithdraw), + ), + ) + .put( + timestampRoundedToBuffer( + timestampProtocolFromDb(denom.stampExpireDeposit), + ), + ) + .put( + timestampRoundedToBuffer( + timestampProtocolFromDb(denom.stampExpireLegal), + ), + ) .put(amountToBuffer(value)) .put(amountToBuffer(denom.fees.feeWithdraw)) .put(amountToBuffer(denom.fees.feeDeposit)) diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index cebe3635b..4fc6db68a 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -27,6 +27,7 @@ import { structuredEncapsulate, } from "@gnu-taler/idb-bridge"; import { + AbsoluteTime, AgeCommitmentProof, AmountString, Amounts, @@ -51,12 +52,13 @@ import { TalerPreciseTimestamp, TalerProtocolDuration, TalerProtocolTimestamp, + //TalerProtocolTimestamp, TransactionIdStr, UnblindedSignature, WireInfo, codecForAny, } from "@gnu-taler/taler-util"; -import { RetryInfo, TaskIdentifiers } from "./operations/common.js"; +import { DbRetryInfo, TaskIdentifiers } from "./operations/common.js"; import { DbAccess, DbReadOnlyTransaction, @@ -193,6 +195,44 @@ export function timestampPreciseToDb( } } +export function timestampProtocolToDb( + stamp: TalerProtocolTimestamp, +): DbProtocolTimestamp { + if (stamp.t_s === "never") { + return DB_TIMESTAMP_FOREVER as DbProtocolTimestamp; + } else { + let tUs = stamp.t_s * 1000000; + return tUs as DbProtocolTimestamp; + } +} + +export function timestampProtocolFromDb( + stamp: DbProtocolTimestamp, +): TalerProtocolTimestamp { + return TalerProtocolTimestamp.fromSeconds(Math.floor(stamp / 1000000)); +} + +export function timestampAbsoluteFromDb( + stamp: DbProtocolTimestamp | DbPreciseTimestamp, +): AbsoluteTime { + if (stamp >= DB_TIMESTAMP_FOREVER) { + return AbsoluteTime.never(); + } + return AbsoluteTime.fromMilliseconds(Math.floor(stamp / 1000)); +} + +export function timestampOptionalAbsoluteFromDb( + stamp: DbProtocolTimestamp | DbPreciseTimestamp | undefined, +): AbsoluteTime | undefined { + if (stamp == null) { + return undefined; + } + if (stamp >= DB_TIMESTAMP_FOREVER) { + return AbsoluteTime.never(); + } + return AbsoluteTime.fromMilliseconds(Math.floor(stamp / 1000)); +} + /** * Format of the operation status code: 0x0abc_nnnn @@ -391,22 +431,22 @@ export interface DenominationRecord { /** * Validity start date of the denomination. */ - stampStart: TalerProtocolTimestamp; + stampStart: DbProtocolTimestamp; /** * Date after which the currency can't be withdrawn anymore. */ - stampExpireWithdraw: TalerProtocolTimestamp; + stampExpireWithdraw: DbProtocolTimestamp; /** * Date after the denomination officially doesn't exist anymore. */ - stampExpireLegal: TalerProtocolTimestamp; + stampExpireLegal: DbProtocolTimestamp; /** * Data after which coins of this denomination can't be deposited anymore. */ - stampExpireDeposit: TalerProtocolTimestamp; + stampExpireDeposit: DbProtocolTimestamp; /** * Signature by the exchange's master key over the denomination @@ -448,7 +488,7 @@ export interface DenominationRecord { * Latest list issue date of the "/keys" response * that includes this denomination. */ - listIssueDate: TalerProtocolTimestamp; + listIssueDate: DbProtocolTimestamp; } export namespace DenominationRecord { @@ -460,10 +500,10 @@ export namespace DenominationRecord { feeRefresh: Amounts.stringify(d.fees.feeRefresh), feeRefund: Amounts.stringify(d.fees.feeRefund), feeWithdraw: Amounts.stringify(d.fees.feeWithdraw), - stampExpireDeposit: d.stampExpireDeposit, - stampExpireLegal: d.stampExpireLegal, - stampExpireWithdraw: d.stampExpireWithdraw, - stampStart: d.stampStart, + stampExpireDeposit: timestampProtocolFromDb(d.stampExpireDeposit), + stampExpireLegal: timestampProtocolFromDb(d.stampExpireLegal), + stampExpireWithdraw: timestampProtocolFromDb(d.stampExpireWithdraw), + stampStart: timestampProtocolFromDb(d.stampStart), value: Amounts.stringify(d.value), exchangeBaseUrl: d.exchangeBaseUrl, }; @@ -471,9 +511,9 @@ export namespace DenominationRecord { } export interface ExchangeSignkeysRecord { - stampStart: TalerProtocolTimestamp; - stampExpire: TalerProtocolTimestamp; - stampEnd: TalerProtocolTimestamp; + stampStart: DbProtocolTimestamp; + stampExpire: DbProtocolTimestamp; + stampEnd: DbProtocolTimestamp; signkeyPub: EddsaPublicKeyString; masterSig: EddsaSignatureString; @@ -590,11 +630,6 @@ export enum ExchangeEntryDbUpdateStatus { ReadyUpdate = 7, } -/** - * Timestamp stored as a IEEE 754 double, in milliseconds. - */ -export type DbIndexableTimestampMs = number; - /** * Exchange record as stored in the wallet's database. */ @@ -634,13 +669,8 @@ export interface ExchangeEntryRecord { /** * Next scheduled update for the exchange. - * - * (This field must always be present, so we can index on the timestamp.) - * - * FIXME: To index on the timestamp, this needs to be a number of - * binary timestamp! */ - nextUpdateStampMs: DbIndexableTimestampMs; + nextUpdateStamp: DbPreciseTimestamp; lastKeysEtag: string | undefined; @@ -650,7 +680,7 @@ export interface ExchangeEntryRecord { * Updated whenever the exchange's denominations are updated or when * the refresh check has been done. */ - nextRefreshCheckStampMs: DbIndexableTimestampMs; + nextRefreshCheckStamp: DbPreciseTimestamp; /** * Public key of the reserve that we're currently using for @@ -873,7 +903,7 @@ export interface RewardRecord { /** * Timestamp, the tip can't be picked up anymore after this deadline. */ - rewardExpiration: TalerProtocolTimestamp; + rewardExpiration: DbProtocolTimestamp; /** * The exchange that will sign our coins, chosen by the merchant. @@ -1287,7 +1317,7 @@ export interface PurchaseRecord { /** * Continue querying the refund status until this deadline has expired. */ - autoRefundDeadline: TalerProtocolTimestamp | undefined; + autoRefundDeadline: DbProtocolTimestamp | undefined; /** * How much merchant has refund to be taken but the wallet @@ -1668,7 +1698,7 @@ export interface DepositTrackingInfo { // Raw wire transfer identifier of the deposit. wireTransferId: string; // When was the wire transfer given to the bank. - timestampExecuted: TalerProtocolTimestamp; + timestampExecuted: DbProtocolTimestamp; // Total amount transfer for this wtid (including fees) amountRaw: AmountString; // Wire fee amount for this exchange @@ -1690,7 +1720,7 @@ export interface DepositGroupRecord { */ amount: AmountString; - wireTransferDeadline: TalerProtocolTimestamp; + wireTransferDeadline: DbProtocolTimestamp; merchantPub: string; merchantPriv: string; @@ -1831,7 +1861,7 @@ export interface PeerPushDebitRecord { */ contractEncNonce: string; - purseExpiration: TalerProtocolTimestamp; + purseExpiration: DbProtocolTimestamp; timestampCreated: DbPreciseTimestamp; @@ -2077,7 +2107,7 @@ export interface OperationRetryRecord { lastError?: TalerErrorDetail; - retryInfo: RetryInfo; + retryInfo: DbRetryInfo; } /** @@ -2130,9 +2160,8 @@ export interface UserAttentionRecord { /** * When the notification was created. - * FIXME: This should be a TalerPreciseTimestamp */ - createdMs: number; + created: DbPreciseTimestamp; /** * When the user mark this notification as read. @@ -2233,7 +2262,7 @@ export interface RefundItemRecord { /** * Execution time as claimed by the merchant */ - executionTime: TalerProtocolTimestamp; + executionTime: DbProtocolTimestamp; /** * Time when the wallet became aware of the refund. diff --git a/packages/taler-wallet-core/src/operations/attention.ts b/packages/taler-wallet-core/src/operations/attention.ts index 1030db0a6..92d69e93e 100644 --- a/packages/taler-wallet-core/src/operations/attention.ts +++ b/packages/taler-wallet-core/src/operations/attention.ts @@ -31,7 +31,7 @@ import { UserAttentionUnreadList, } from "@gnu-taler/taler-util"; import { InternalWalletState } from "../internal-wallet-state.js"; -import { timestampPreciseToDb } from "../index.js"; +import { timestampPreciseFromDb, timestampPreciseToDb } from "../index.js"; const logger = new Logger("operations/attention.ts"); @@ -75,7 +75,7 @@ export async function getUserAttentions( return; pending.push({ info: x.info, - when: TalerPreciseTimestamp.fromMilliseconds(x.createdMs), + when: timestampPreciseFromDb(x.created), read: x.read !== undefined, }); }); @@ -118,7 +118,7 @@ export async function addAttentionRequest( await tx.userAttention.put({ info, entityId, - createdMs: AbsoluteTime.now().t_ms as number, + created: timestampPreciseToDb(TalerPreciseTimestamp.now()), read: undefined, }); }); diff --git a/packages/taler-wallet-core/src/operations/common.ts b/packages/taler-wallet-core/src/operations/common.ts index 50dd3dc5c..e8e492c08 100644 --- a/packages/taler-wallet-core/src/operations/common.ts +++ b/packages/taler-wallet-core/src/operations/common.ts @@ -40,6 +40,7 @@ import { TalerError, TalerErrorCode, TalerErrorDetail, + TalerPreciseTimestamp, TombstoneIdStr, TransactionIdStr, TransactionType, @@ -49,6 +50,7 @@ import { CryptoApiStoppedError } from "../crypto/workers/crypto-dispatcher.js"; import { BackupProviderRecord, CoinRecord, + DbPreciseTimestamp, DepositGroupRecord, ExchangeDetailsRecord, ExchangeEntryDbRecordStatus, @@ -62,6 +64,7 @@ import { RecoupGroupRecord, RefreshGroupRecord, RewardRecord, + timestampPreciseToDb, WalletStoresV1, WithdrawalGroupRecord, } from "../db.js"; @@ -360,11 +363,11 @@ async function storePendingTaskError( retryRecord = { id: pendingTaskId, lastError: e, - retryInfo: RetryInfo.reset(), + retryInfo: DbRetryInfo.reset(), }; } else { retryRecord.lastError = e; - retryRecord.retryInfo = RetryInfo.increment(retryRecord.retryInfo); + retryRecord.retryInfo = DbRetryInfo.increment(retryRecord.retryInfo); } await tx.operationRetries.put(retryRecord); return taskToTransactionNotification(ws, tx, pendingTaskId, e); @@ -383,7 +386,7 @@ export async function resetPendingTaskTimeout( if (retryRecord) { // Note that we don't reset the lastError, it should still be visible // while the retry runs. - retryRecord.retryInfo = RetryInfo.reset(); + retryRecord.retryInfo = DbRetryInfo.reset(); await tx.operationRetries.put(retryRecord); } return taskToTransactionNotification(ws, tx, pendingTaskId, undefined); @@ -403,14 +406,14 @@ async function storePendingTaskPending( if (!retryRecord) { retryRecord = { id: pendingTaskId, - retryInfo: RetryInfo.reset(), + retryInfo: DbRetryInfo.reset(), }; } else { if (retryRecord.lastError) { hadError = true; } delete retryRecord.lastError; - retryRecord.retryInfo = RetryInfo.increment(retryRecord.retryInfo); + retryRecord.retryInfo = DbRetryInfo.increment(retryRecord.retryInfo); } await tx.operationRetries.put(retryRecord); if (hadError) { @@ -736,9 +739,9 @@ export interface TaskRunLongpollResult { type: TaskRunResultType.Longpoll; } -export interface RetryInfo { - firstTry: AbsoluteTime; - nextRetry: AbsoluteTime; +export interface DbRetryInfo { + firstTry: DbPreciseTimestamp; + nextRetry: DbPreciseTimestamp; retryCounter: number; } @@ -755,7 +758,7 @@ const defaultRetryPolicy: RetryPolicy = { }; function updateTimeout( - r: RetryInfo, + r: DbRetryInfo, p: RetryPolicy = defaultRetryPolicy, ): void { const now = AbsoluteTime.now(); @@ -763,7 +766,9 @@ function updateTimeout( throw Error("assertion failed"); } if (p.backoffDelta.d_ms === "forever") { - r.nextRetry = AbsoluteTime.never(); + r.nextRetry = timestampPreciseToDb( + AbsoluteTime.toPreciseTimestamp(AbsoluteTime.never()), + ); return; } @@ -775,12 +780,12 @@ function updateTimeout( (p.maxTimeout.d_ms === "forever" ? nextIncrement : Math.min(p.maxTimeout.d_ms, nextIncrement)); - r.nextRetry = AbsoluteTime.fromMilliseconds(t); + r.nextRetry = timestampPreciseToDb(TalerPreciseTimestamp.fromMilliseconds(t)); } -export namespace RetryInfo { +export namespace DbRetryInfo { export function getDuration( - r: RetryInfo | undefined, + r: DbRetryInfo | undefined, p: RetryPolicy = defaultRetryPolicy, ): Duration { if (!r) { @@ -797,11 +802,11 @@ export namespace RetryInfo { }; } - export function reset(p: RetryPolicy = defaultRetryPolicy): RetryInfo { - const now = AbsoluteTime.now(); - const info = { - firstTry: now, - nextRetry: now, + export function reset(p: RetryPolicy = defaultRetryPolicy): DbRetryInfo { + const now = TalerPreciseTimestamp.now(); + const info: DbRetryInfo = { + firstTry: timestampPreciseToDb(now), + nextRetry: timestampPreciseToDb(now), retryCounter: 0, }; updateTimeout(info, p); @@ -809,9 +814,9 @@ export namespace RetryInfo { } export function increment( - r: RetryInfo | undefined, + r: DbRetryInfo | undefined, p: RetryPolicy = defaultRetryPolicy, - ): RetryInfo { + ): DbRetryInfo { if (!r) { return reset(p); } diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts index cb40f8f22..111d15989 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -74,6 +74,8 @@ import { createRefreshGroup, getTotalRefreshCost, timestampPreciseToDb, + timestampProtocolFromDb, + timestampProtocolToDb, } from "../index.js"; import { InternalWalletState } from "../internal-wallet-state.js"; import { assertUnreachable } from "../util/assertUnreachable.js"; @@ -800,7 +802,7 @@ async function processDepositGroupPendingTrack( amountRaw: Amounts.stringify(raw), wireFee: Amounts.stringify(wireFee), exchangePub: track.exchange_pub, - timestampExecuted: track.execution_time, + timestampExecuted: timestampProtocolToDb(track.execution_time), wireTransferId: track.wtid, }, id: track.exchange_sig, @@ -1393,7 +1395,9 @@ export async function createDepositGroup( counterpartyEffectiveDepositAmount: Amounts.stringify( counterpartyEffectiveDepositAmount, ), - wireTransferDeadline: contractTerms.wire_transfer_deadline, + wireTransferDeadline: timestampProtocolToDb( + contractTerms.wire_transfer_deadline, + ), wire: { payto_uri: req.depositPaytoUri, salt: wireSalt, diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index 60d55252a..5e966b719 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -74,7 +74,9 @@ import { ExchangeEntryDbRecordStatus, ExchangeEntryDbUpdateStatus, isWithdrawableDenom, + timestampPreciseFromDb, timestampPreciseToDb, + timestampProtocolToDb, WalletDbReadWriteTransaction, } from "../index.js"; import { InternalWalletState, TrustInfo } from "../internal-wallet-state.js"; @@ -317,8 +319,12 @@ export async function addPresetExchangeEntry( detailsPointer: undefined, lastUpdate: undefined, lastKeysEtag: undefined, - nextRefreshCheckStampMs: AbsoluteTime.getStampMsNever(), - nextUpdateStampMs: AbsoluteTime.getStampMsNever(), + nextRefreshCheckStamp: timestampPreciseToDb( + AbsoluteTime.toPreciseTimestamp(AbsoluteTime.never()), + ), + nextUpdateStamp: timestampPreciseToDb( + AbsoluteTime.toPreciseTimestamp(AbsoluteTime.never()), + ), }; await tx.exchanges.put(r); } @@ -344,8 +350,12 @@ export async function provideExchangeRecordInTx( baseUrl: baseUrl, detailsPointer: undefined, lastUpdate: undefined, - nextUpdateStampMs: AbsoluteTime.getStampMsNever(), - nextRefreshCheckStampMs: AbsoluteTime.getStampMsNever(), + nextUpdateStamp: timestampPreciseToDb( + AbsoluteTime.toPreciseTimestamp(AbsoluteTime.never()), + ), + nextRefreshCheckStamp: timestampPreciseToDb( + AbsoluteTime.toPreciseTimestamp(AbsoluteTime.never()), + ), lastKeysEtag: undefined, }; await tx.exchanges.put(r); @@ -446,13 +456,19 @@ async function downloadExchangeKeysInfo( isRevoked: false, value: Amounts.stringify(value), currency: value.currency, - stampExpireDeposit: denomIn.stamp_expire_deposit, - stampExpireLegal: denomIn.stamp_expire_legal, - stampExpireWithdraw: denomIn.stamp_expire_withdraw, - stampStart: denomIn.stamp_start, + stampExpireDeposit: timestampProtocolToDb( + denomIn.stamp_expire_deposit, + ), + stampExpireLegal: timestampProtocolToDb(denomIn.stamp_expire_legal), + stampExpireWithdraw: timestampProtocolToDb( + denomIn.stamp_expire_withdraw, + ), + stampStart: timestampProtocolToDb(denomIn.stamp_start), verificationStatus: DenominationVerificationStatus.Unverified, masterSig: denomIn.master_sig, - listIssueDate: exchangeKeysJsonUnchecked.list_issue_date, + listIssueDate: timestampProtocolToDb( + exchangeKeysJsonUnchecked.list_issue_date, + ), fees: { feeDeposit: Amounts.stringify(denomGroup.fee_deposit), feeRefresh: Amounts.stringify(denomGroup.fee_refresh), @@ -614,7 +630,9 @@ export async function updateExchangeFromUrlHandler( !forceNow && exchangeDetails !== undefined && !AbsoluteTime.isExpired( - AbsoluteTime.fromStampMs(exchange.nextUpdateStampMs), + AbsoluteTime.fromPreciseTimestamp( + timestampPreciseFromDb(exchange.nextUpdateStamp), + ), ) ) { logger.trace("using existing exchange info"); @@ -755,11 +773,15 @@ export async function updateExchangeFromUrlHandler( newDetails.rowId = existingDetails.rowId; } r.lastUpdate = timestampPreciseToDb(TalerPreciseTimestamp.now()); - r.nextUpdateStampMs = AbsoluteTime.toStampMs( - AbsoluteTime.fromProtocolTimestamp(keysInfo.expiry), + r.nextUpdateStamp = timestampPreciseToDb( + AbsoluteTime.toPreciseTimestamp( + AbsoluteTime.fromProtocolTimestamp(keysInfo.expiry), + ), ); // New denominations might be available. - r.nextRefreshCheckStampMs = AbsoluteTime.getStampMsNow(); + r.nextRefreshCheckStamp = timestampPreciseToDb( + TalerPreciseTimestamp.now(), + ); if (detailsPointerChanged) { r.detailsPointer = { currency: newDetails.currency, @@ -777,9 +799,9 @@ export async function updateExchangeFromUrlHandler( exchangeDetailsRowId: drRowId.key, masterSig: sk.master_sig, signkeyPub: sk.key, - stampEnd: sk.stamp_end, - stampExpire: sk.stamp_expire, - stampStart: sk.stamp_start, + stampEnd: timestampProtocolToDb(sk.stamp_end), + stampExpire: timestampProtocolToDb(sk.stamp_expire), + stampStart: timestampProtocolToDb(sk.stamp_start), }); } @@ -814,7 +836,7 @@ export async function updateExchangeFromUrlHandler( ); } } else { - x.listIssueDate = keysInfo.listIssueDate; + x.listIssueDate = timestampProtocolToDb(keysInfo.listIssueDate); if (!x.isOffered) { x.isOffered = true; logger.info( diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts b/packages/taler-wallet-core/src/operations/pay-merchant.ts index 97bf6e2a6..157541ed3 100644 --- a/packages/taler-wallet-core/src/operations/pay-merchant.ts +++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts @@ -104,6 +104,8 @@ import { RefundItemRecord, RefundItemStatus, timestampPreciseToDb, + timestampProtocolFromDb, + timestampProtocolToDb, } from "../index.js"; import { EXCHANGE_COINS_LOCK, @@ -115,7 +117,7 @@ import { checkDbInvariant } from "../util/invariants.js"; import { GetReadOnlyAccess } from "../util/query.js"; import { constructTaskIdentifier, - RetryInfo, + DbRetryInfo, runLongpollAsync, runTaskWithErrorReporting, spendCoins, @@ -217,11 +219,13 @@ async function failProposalPermanently( notifyTransition(ws, transactionId, transitionInfo); } -function getProposalRequestTimeout(retryInfo?: RetryInfo): Duration { +function getProposalRequestTimeout(retryInfo?: DbRetryInfo): Duration { return Duration.clamp({ lower: Duration.fromSpec({ seconds: 1 }), upper: Duration.fromSpec({ seconds: 60 }), - value: retryInfo ? RetryInfo.getDuration(retryInfo) : Duration.fromSpec({}), + value: retryInfo + ? DbRetryInfo.getDuration(retryInfo) + : Duration.fromSpec({}), }); } @@ -738,8 +742,10 @@ async function storeFirstPaySuccess( const ar = Duration.fromTalerProtocolDuration(protoAr); logger.info("auto_refund present"); purchase.purchaseStatus = PurchaseStatus.PendingQueryingAutoRefund; - purchase.autoRefundDeadline = AbsoluteTime.toProtocolTimestamp( - AbsoluteTime.addDuration(AbsoluteTime.now(), ar), + purchase.autoRefundDeadline = timestampProtocolToDb( + AbsoluteTime.toProtocolTimestamp( + AbsoluteTime.addDuration(AbsoluteTime.now(), ar), + ), ); } await tx.purchases.put(purchase); @@ -2343,7 +2349,9 @@ async function processPurchaseAutoRefund( if ( !purchase.autoRefundDeadline || AbsoluteTime.isExpired( - AbsoluteTime.fromProtocolTimestamp(purchase.autoRefundDeadline), + AbsoluteTime.fromProtocolTimestamp( + timestampProtocolFromDb(purchase.autoRefundDeadline), + ), ) ) { const transitionInfo = await ws.db @@ -2804,7 +2812,7 @@ async function storeRefunds( const status: RefundItemStatus = getItemStatus(rf); const newItem: RefundItemRecord = { coinPub: rf.coin_pub, - executionTime: rf.execution_time, + executionTime: timestampProtocolToDb(rf.execution_time), obtainedTime: timestampPreciseToDb(now), refundAmount: rf.refund_amount, refundGroupId: newGroup.refundGroupId, diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts index b3d0eb132..a7b9f79eb 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts @@ -56,10 +56,13 @@ import { RefreshOperationStatus, createRefreshGroup, timestampPreciseToDb, + timestampProtocolFromDb, + timestampProtocolToDb, } from "../index.js"; import { InternalWalletState } from "../internal-wallet-state.js"; import { PendingTaskType } from "../pending-types.js"; import { assertUnreachable } from "../util/assertUnreachable.js"; +import { PeerCoinRepair, selectPeerCoins } from "../util/coinSelection.js"; import { checkLogicInvariant } from "../util/invariants.js"; import { TaskRunResult, @@ -78,7 +81,6 @@ import { notifyTransition, stopLongpolling, } from "./transactions.js"; -import { PeerCoinRepair, selectPeerCoins } from "../util/coinSelection.js"; const logger = new Logger("pay-peer-push-debit.ts"); @@ -208,7 +210,7 @@ async function processPeerPushDebitCreateReserve( mergePub: peerPushInitiation.mergePub, minAge: 0, purseAmount: peerPushInitiation.amount, - purseExpiration, + purseExpiration: timestampProtocolFromDb(purseExpiration), pursePriv: peerPushInitiation.pursePriv, }); @@ -667,7 +669,7 @@ export async function initiatePeerPushDebit( exchangeBaseUrl: sel.exchangeBaseUrl, mergePriv: mergePair.priv, mergePub: mergePair.pub, - purseExpiration: purseExpiration, + purseExpiration: timestampProtocolToDb(purseExpiration), pursePriv: pursePair.priv, pursePub: pursePair.pub, timestampCreated: timestampPreciseToDb(TalerPreciseTimestamp.now()), diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts index 120d316ce..1819aa1b8 100644 --- a/packages/taler-wallet-core/src/operations/pending.ts +++ b/packages/taler-wallet-core/src/operations/pending.ts @@ -21,43 +21,46 @@ /** * Imports. */ +import { GlobalIDB } from "@gnu-taler/idb-bridge"; +import { AbsoluteTime, TransactionRecordFilter } from "@gnu-taler/taler-util"; import { - PurchaseStatus, - WalletStoresV1, BackupProviderStateTag, - RefreshCoinStatus, - PeerPushDebitStatus, - PeerPullDebitRecordStatus, - PeerPushCreditStatus, - PeerPullPaymentCreditStatus, - WithdrawalGroupStatus, - RewardRecordStatus, - DepositOperationStatus, - RefreshGroupRecord, - WithdrawalGroupRecord, + DepositElementStatus, DepositGroupRecord, - RewardRecord, - PurchaseRecord, + DepositOperationStatus, + ExchangeEntryDbUpdateStatus, PeerPullCreditRecord, + PeerPullDebitRecordStatus, + PeerPullPaymentCreditStatus, PeerPullPaymentIncomingRecord, + PeerPushCreditStatus, PeerPushDebitRecord, + PeerPushDebitStatus, PeerPushPaymentIncomingRecord, + PurchaseRecord, + PurchaseStatus, + RefreshCoinStatus, + RefreshGroupRecord, + RefreshOperationStatus, RefundGroupRecord, RefundGroupStatus, - ExchangeEntryDbUpdateStatus, - RefreshOperationStatus, - DepositElementStatus, + RewardRecord, + RewardRecordStatus, + WalletStoresV1, + WithdrawalGroupRecord, + WithdrawalGroupStatus, + timestampAbsoluteFromDb, + timestampOptionalAbsoluteFromDb, timestampPreciseFromDb, + timestampPreciseToDb, } from "../db.js"; +import { InternalWalletState } from "../internal-wallet-state.js"; import { PendingOperationsResponse, PendingTaskType, TaskId, } from "../pending-types.js"; -import { AbsoluteTime, TransactionRecordFilter } from "@gnu-taler/taler-util"; -import { InternalWalletState } from "../internal-wallet-state.js"; import { GetReadOnlyAccess } from "../util/query.js"; -import { GlobalIDB } from "@gnu-taler/idb-bridge"; import { TaskIdentifiers } from "./common.js"; function getPendingCommon( @@ -100,12 +103,14 @@ async function gatherExchangePending( } const opTag = TaskIdentifiers.forExchangeUpdate(exch); let opr = await tx.operationRetries.get(opTag); - const timestampDue = - opr?.retryInfo.nextRetry ?? - AbsoluteTime.fromStampMs(exch.nextUpdateStampMs); + const timestampDue = opr?.retryInfo.nextRetry ?? exch.nextRefreshCheckStamp; resp.pendingOperations.push({ type: PendingTaskType.ExchangeUpdate, - ...getPendingCommon(ws, opTag, timestampDue), + ...getPendingCommon( + ws, + opTag, + AbsoluteTime.fromPreciseTimestamp(timestampPreciseFromDb(timestampDue)), + ), givesLifeness: false, exchangeBaseUrl: exch.baseUrl, lastError: opr?.lastError, @@ -116,8 +121,16 @@ async function gatherExchangePending( if (!opr?.lastError) { resp.pendingOperations.push({ type: PendingTaskType.ExchangeCheckRefresh, - ...getPendingCommon(ws, opTag, timestampDue), - timestampDue: AbsoluteTime.fromStampMs(exch.nextRefreshCheckStampMs), + ...getPendingCommon( + ws, + opTag, + AbsoluteTime.fromPreciseTimestamp( + timestampPreciseFromDb(timestampDue), + ), + ), + timestampDue: AbsoluteTime.fromPreciseTimestamp( + timestampPreciseFromDb(exch.nextRefreshCheckStamp), + ), givesLifeness: false, exchangeBaseUrl: exch.baseUrl, }); @@ -166,7 +179,9 @@ async function gatherRefreshPending( } const opId = TaskIdentifiers.forRefresh(r); const retryRecord = await tx.operationRetries.get(opId); - const timestampDue = retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(); + const timestampDue = + timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ?? + AbsoluteTime.now(); resp.pendingOperations.push({ type: PendingTaskType.Refresh, ...getPendingCommon(ws, opId, timestampDue), @@ -223,8 +238,8 @@ async function gatherWithdrawalPending( opr = { id: opTag, retryInfo: { - firstTry: now, - nextRetry: now, + firstTry: timestampPreciseToDb(AbsoluteTime.toPreciseTimestamp(now)), + nextRetry: timestampPreciseToDb(AbsoluteTime.toPreciseTimestamp(now)), retryCounter: 0, }, }; @@ -234,7 +249,8 @@ async function gatherWithdrawalPending( ...getPendingCommon( ws, opTag, - opr.retryInfo?.nextRetry ?? AbsoluteTime.now(), + timestampOptionalAbsoluteFromDb(opr.retryInfo?.nextRetry) ?? + AbsoluteTime.now(), ), givesLifeness: true, withdrawalGroupId: wsr.withdrawalGroupId, @@ -286,7 +302,9 @@ async function gatherDepositPending( } const opId = TaskIdentifiers.forDeposit(dg); const retryRecord = await tx.operationRetries.get(opId); - const timestampDue = retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(); + const timestampDue = + timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ?? + AbsoluteTime.now(); resp.pendingOperations.push({ type: PendingTaskType.Deposit, ...getPendingCommon(ws, opId, timestampDue), @@ -331,13 +349,15 @@ async function gatherRewardPending( await iterRecordsForReward(tx, { onlyState: "nonfinal" }, async (tip) => { const opId = TaskIdentifiers.forTipPickup(tip); const retryRecord = await tx.operationRetries.get(opId); - const timestampDue = retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(); + const timestampDue = + timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ?? + AbsoluteTime.now(); if (tip.acceptedTimestamp) { resp.pendingOperations.push({ type: PendingTaskType.RewardPickup, ...getPendingCommon(ws, opId, timestampDue), givesLifeness: true, - timestampDue: retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(), + timestampDue, merchantBaseUrl: tip.merchantBaseUrl, tipId: tip.walletRewardId, merchantTipId: tip.merchantRewardId, @@ -391,7 +411,9 @@ async function gatherPurchasePending( await iterRecordsForPurchase(tx, { onlyState: "nonfinal" }, async (pr) => { const opId = TaskIdentifiers.forPay(pr); const retryRecord = await tx.operationRetries.get(opId); - const timestampDue = retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(); + const timestampDue = + timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ?? + AbsoluteTime.now(); resp.pendingOperations.push({ type: PendingTaskType.Purchase, ...getPendingCommon(ws, opId, timestampDue), @@ -420,7 +442,9 @@ async function gatherRecoupPending( } const opId = TaskIdentifiers.forRecoup(rg); const retryRecord = await tx.operationRetries.get(opId); - const timestampDue = retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(); + const timestampDue = + timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ?? + AbsoluteTime.now(); resp.pendingOperations.push({ type: PendingTaskType.Recoup, ...getPendingCommon(ws, opId, timestampDue), @@ -445,8 +469,8 @@ async function gatherBackupPending( const opId = TaskIdentifiers.forBackup(bp); const retryRecord = await tx.operationRetries.get(opId); if (bp.state.tag === BackupProviderStateTag.Ready) { - const timestampDue = AbsoluteTime.fromPreciseTimestamp( - timestampPreciseFromDb(bp.state.nextBackupTimestamp), + const timestampDue = timestampAbsoluteFromDb( + bp.state.nextBackupTimestamp, ); resp.pendingOperations.push({ type: PendingTaskType.Backup, @@ -457,7 +481,8 @@ async function gatherBackupPending( }); } else if (bp.state.tag === BackupProviderStateTag.Retrying) { const timestampDue = - retryRecord?.retryInfo?.nextRetry ?? AbsoluteTime.now(); + timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo?.nextRetry) ?? + AbsoluteTime.now(); resp.pendingOperations.push({ type: PendingTaskType.Backup, ...getPendingCommon(ws, opId, timestampDue), @@ -504,7 +529,8 @@ async function gatherPeerPullInitiationPending( const opId = TaskIdentifiers.forPeerPullPaymentInitiation(pi); const retryRecord = await tx.operationRetries.get(opId); const timestampDue = - retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(); + timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ?? + AbsoluteTime.now(); resp.pendingOperations.push({ type: PendingTaskType.PeerPullCredit, ...getPendingCommon(ws, opId, timestampDue), @@ -550,7 +576,8 @@ async function gatherPeerPullDebitPending( const opId = TaskIdentifiers.forPeerPullPaymentDebit(pi); const retryRecord = await tx.operationRetries.get(opId); const timestampDue = - retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(); + timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ?? + AbsoluteTime.now(); resp.pendingOperations.push({ type: PendingTaskType.PeerPullDebit, ...getPendingCommon(ws, opId, timestampDue), @@ -596,7 +623,8 @@ async function gatherPeerPushInitiationPending( const opId = TaskIdentifiers.forPeerPushPaymentInitiation(pi); const retryRecord = await tx.operationRetries.get(opId); const timestampDue = - retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(); + timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ?? + AbsoluteTime.now(); resp.pendingOperations.push({ type: PendingTaskType.PeerPushDebit, ...getPendingCommon(ws, opId, timestampDue), @@ -646,7 +674,8 @@ async function gatherPeerPushCreditPending( const opId = TaskIdentifiers.forPeerPushCredit(pi); const retryRecord = await tx.operationRetries.get(opId); const timestampDue = - retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(); + timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ?? + AbsoluteTime.now(); resp.pendingOperations.push({ type: PendingTaskType.PeerPushCredit, ...getPendingCommon(ws, opId, timestampDue), diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index dc1d53627..95aedbbd6 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -81,6 +81,7 @@ import { PendingTaskType, RefreshSessionRecord, timestampPreciseToDb, + timestampProtocolFromDb, } from "../index.js"; import { EXCHANGE_COINS_LOCK, @@ -1125,10 +1126,10 @@ export async function createRefreshGroup( */ function getAutoRefreshCheckThreshold(d: DenominationRecord): AbsoluteTime { const expireWithdraw = AbsoluteTime.fromProtocolTimestamp( - d.stampExpireWithdraw, + timestampProtocolFromDb(d.stampExpireWithdraw), ); const expireDeposit = AbsoluteTime.fromProtocolTimestamp( - d.stampExpireDeposit, + timestampProtocolFromDb(d.stampExpireDeposit), ); const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit); const deltaDiv = durationMul(delta, 0.75); @@ -1140,10 +1141,10 @@ function getAutoRefreshCheckThreshold(d: DenominationRecord): AbsoluteTime { */ function getAutoRefreshExecuteThreshold(d: DenominationRecord): AbsoluteTime { const expireWithdraw = AbsoluteTime.fromProtocolTimestamp( - d.stampExpireWithdraw, + timestampProtocolFromDb(d.stampExpireWithdraw), ); const expireDeposit = AbsoluteTime.fromProtocolTimestamp( - d.stampExpireDeposit, + timestampProtocolFromDb(d.stampExpireDeposit), ); const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit); const deltaDiv = durationMul(delta, 0.5); @@ -1227,8 +1228,9 @@ export async function autoRefresh( logger.trace( `next refresh check at ${AbsoluteTime.toIsoString(minCheckThreshold)}`, ); - exchange.nextRefreshCheckStampMs = - AbsoluteTime.toStampMs(minCheckThreshold); + exchange.nextRefreshCheckStamp = timestampPreciseToDb( + AbsoluteTime.toPreciseTimestamp(minCheckThreshold), + ); await tx.exchanges.put(exchange); }); return TaskRunResult.finished(); diff --git a/packages/taler-wallet-core/src/operations/reward.ts b/packages/taler-wallet-core/src/operations/reward.ts index 3681dc4f5..ddcfb20ac 100644 --- a/packages/taler-wallet-core/src/operations/reward.ts +++ b/packages/taler-wallet-core/src/operations/reward.ts @@ -52,6 +52,8 @@ import { RewardRecordStatus, timestampPreciseFromDb, timestampPreciseToDb, + timestampProtocolFromDb, + timestampProtocolToDb, } from "../db.js"; import { makeErrorDetail } from "@gnu-taler/taler-util"; import { InternalWalletState } from "../internal-wallet-state.js"; @@ -201,7 +203,7 @@ export async function prepareTip( acceptedTimestamp: undefined, status: RewardRecordStatus.DialogAccept, rewardAmountRaw: Amounts.stringify(amount), - rewardExpiration: tipPickupStatus.expiration, + rewardExpiration: timestampProtocolToDb(tipPickupStatus.expiration), exchangeBaseUrl: tipPickupStatus.exchange_url, next_url: tipPickupStatus.next_url, merchantBaseUrl: res.merchantBaseUrl, @@ -231,7 +233,7 @@ export async function prepareTip( rewardAmountRaw: Amounts.stringify(tipRecord.rewardAmountRaw), exchangeBaseUrl: tipRecord.exchangeBaseUrl, merchantBaseUrl: tipRecord.merchantBaseUrl, - expirationTimestamp: tipRecord.rewardExpiration, + expirationTimestamp: timestampProtocolFromDb(tipRecord.rewardExpiration), rewardAmountEffective: Amounts.stringify(tipRecord.rewardAmountEffective), walletRewardId: tipRecord.walletRewardId, transactionId, diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 41bdae249..cf2006406 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -20,6 +20,7 @@ import { AbsoluteTime, Amounts, + DepositTransactionTrackingState, j2s, Logger, NotificationType, @@ -69,6 +70,7 @@ import { GetReadOnlyAccess, timestampOptionalPreciseFromDb, timestampPreciseFromDb, + timestampProtocolFromDb, WalletStoresV1, } from "../index.js"; import { InternalWalletState } from "../internal-wallet-state.js"; @@ -809,6 +811,17 @@ function buildTransactionForDeposit( } } + const trackingState: DepositTransactionTrackingState[] = []; + + for (const ts of Object.values(dg.trackingState ?? {})) { + trackingState.push({ + amountRaw: ts.amountRaw, + timestampExecuted: timestampProtocolFromDb(ts.timestampExecuted), + wireFee: ts.wireFee, + wireTransferId: ts.wireTransferId, + }); + } + return { type: TransactionType.Deposit, txState: computeDepositTransactionStatus(dg), @@ -817,7 +830,7 @@ function buildTransactionForDeposit( amountEffective: Amounts.stringify(dg.totalPayCost), timestamp: timestampPreciseFromDb(dg.timestampCreated), targetPaytoUri: dg.wire.payto_uri, - wireTransferDeadline: dg.wireTransferDeadline, + wireTransferDeadline: timestampProtocolFromDb(dg.wireTransferDeadline), transactionId: constructTransactionIdentifier({ tag: TransactionType.Deposit, depositGroupId: dg.depositGroupId, @@ -830,7 +843,7 @@ function buildTransactionForDeposit( )) / dg.statusPerCoin.length, depositGroupId: dg.depositGroupId, - trackingState: Object.values(dg.trackingState ?? {}), + trackingState, deposited, ...(ort?.lastError ? { error: ort.lastError } : {}), }; diff --git a/packages/taler-wallet-core/src/operations/withdraw.test.ts b/packages/taler-wallet-core/src/operations/withdraw.test.ts index 2d9286610..cb8aa5e81 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.test.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.test.ts @@ -16,7 +16,11 @@ import { Amounts, DenomKeyType } from "@gnu-taler/taler-util"; import test from "ava"; -import { DenominationRecord, DenominationVerificationStatus } from "../db.js"; +import { + DenominationRecord, + DenominationVerificationStatus, + timestampProtocolToDb, +} from "../db.js"; import { selectWithdrawalDenominations } from "../util/coinSelection.js"; test("withdrawal selection bug repro", (t) => { @@ -64,22 +68,22 @@ test("withdrawal selection bug repro", (t) => { isRevoked: false, masterSig: "4F0P456CNNTTWK8BFJHGM3JTD6FVVNZY8EP077GYAHDJ5Y81S5RQ3SMS925NXMDVG9A88JAAP0E2GDZBC21PP5NHFFVWHAW3AVT8J3R", - stampExpireDeposit: { + stampExpireDeposit: timestampProtocolToDb({ t_s: 1742909388, - }, - stampExpireLegal: { + }), + stampExpireLegal: timestampProtocolToDb({ t_s: 1900589388, - }, - stampExpireWithdraw: { + }), + stampExpireWithdraw: timestampProtocolToDb({ t_s: 1679837388, - }, - stampStart: { + }), + stampStart: timestampProtocolToDb({ t_s: 1585229388, - }, + }), verificationStatus: DenominationVerificationStatus.Unverified, currency: "KUDOS", value: "KUDOS:1000", - listIssueDate: { t_s: 0 }, + listIssueDate: timestampProtocolToDb({ t_s: 0 }), }, { denomPub: { @@ -119,22 +123,22 @@ test("withdrawal selection bug repro", (t) => { isRevoked: false, masterSig: "P99AW82W46MZ0AKW7Z58VQPXFNTJQM9DVTYPBDF6KVYF38PPVDAZTV7JQ8TY7HGEC7JJJAY4E7AY7J3W1WV10DAZZQHHKTAVTSRAC20", - stampExpireDeposit: { + stampExpireDeposit: timestampProtocolToDb({ t_s: 1742909388, - }, - stampExpireLegal: { + }), + stampExpireLegal: timestampProtocolToDb({ t_s: 1900589388, - }, - stampExpireWithdraw: { + }), + stampExpireWithdraw: timestampProtocolToDb({ t_s: 1679837388, - }, - stampStart: { + }), + stampStart: timestampProtocolToDb({ t_s: 1585229388, - }, + }), verificationStatus: DenominationVerificationStatus.Unverified, value: "KUDOS:10", currency: "KUDOS", - listIssueDate: { t_s: 0 }, + listIssueDate: timestampProtocolToDb({ t_s: 0 }), }, { denomPub: { @@ -173,22 +177,22 @@ test("withdrawal selection bug repro", (t) => { isRevoked: false, masterSig: "8S4VZGHE5WE0N5ZVCHYW9KZZR4YAKK15S46MV1HR1QB9AAMH3NWPW4DCR4NYGJK33Q8YNFY80SWNS6XKAP5DEVK933TM894FJ2VGE3G", - stampExpireDeposit: { + stampExpireDeposit: timestampProtocolToDb({ t_s: 1742909388, - }, - stampExpireLegal: { + }), + stampExpireLegal: timestampProtocolToDb({ t_s: 1900589388, - }, - stampExpireWithdraw: { + }), + stampExpireWithdraw: timestampProtocolToDb({ t_s: 1679837388, - }, - stampStart: { + }), + stampStart: timestampProtocolToDb({ t_s: 1585229388, - }, + }), verificationStatus: DenominationVerificationStatus.Unverified, value: "KUDOS:5", currency: "KUDOS", - listIssueDate: { t_s: 0 }, + listIssueDate: timestampProtocolToDb({ t_s: 0 }), }, { denomPub: { @@ -228,22 +232,22 @@ test("withdrawal selection bug repro", (t) => { isRevoked: false, masterSig: "E3AWGAG8VB42P3KXM8B04Z6M483SX59R3Y4T53C3NXCA2NPB6C7HVCMVX05DC6S58E9X40NGEBQNYXKYMYCF3ASY2C4WP1WCZ4ME610", - stampExpireDeposit: { + stampExpireDeposit: timestampProtocolToDb({ t_s: 1742909388, - }, - stampExpireLegal: { + }), + stampExpireLegal: timestampProtocolToDb({ t_s: 1900589388, - }, - stampExpireWithdraw: { + }), + stampExpireWithdraw: timestampProtocolToDb({ t_s: 1679837388, - }, - stampStart: { + }), + stampStart: timestampProtocolToDb({ t_s: 1585229388, - }, + }), verificationStatus: DenominationVerificationStatus.Unverified, value: "KUDOS:1", currency: "KUDOS", - listIssueDate: { t_s: 0 }, + listIssueDate: timestampProtocolToDb({ t_s: 0 }), }, { denomPub: { @@ -282,18 +286,18 @@ test("withdrawal selection bug repro", (t) => { isRevoked: false, masterSig: "0ES1RKV002XB4YP21SN0QB7RSDHGYT0XAE65JYN8AVJAA6H7JZFN7JADXT521DJS89XMGPZGR8GCXF1516Y0Q9QDV00E6NMFA6CF838", - stampExpireDeposit: { + stampExpireDeposit: timestampProtocolToDb({ t_s: 1742909388, - }, - stampExpireLegal: { + }), + stampExpireLegal: timestampProtocolToDb({ t_s: 1900589388, - }, - stampExpireWithdraw: { + }), + stampExpireWithdraw: timestampProtocolToDb({ t_s: 1679837388, - }, - stampStart: { + }), + stampStart: timestampProtocolToDb({ t_s: 1585229388, - }, + }), verificationStatus: DenominationVerificationStatus.Unverified, value: Amounts.stringify({ currency: "KUDOS", @@ -301,7 +305,7 @@ test("withdrawal selection bug repro", (t) => { value: 0, }), currency: "KUDOS", - listIssueDate: { t_s: 0 }, + listIssueDate: timestampProtocolToDb({ t_s: 0 }), }, { denomPub: { @@ -340,22 +344,22 @@ test("withdrawal selection bug repro", (t) => { isRevoked: false, masterSig: "58QEB6C6N7602E572E3JYANVVJ9BRW0V9E2ZFDW940N47YVQDK9SAFPWBN5YGT3G1742AFKQ0CYR4DM2VWV0Z0T1XMEKWN6X2EZ9M0R", - stampExpireDeposit: { + stampExpireDeposit: timestampProtocolToDb({ t_s: 1742909388, - }, - stampExpireLegal: { + }), + stampExpireLegal: timestampProtocolToDb({ t_s: 1900589388, - }, - stampExpireWithdraw: { + }), + stampExpireWithdraw: timestampProtocolToDb({ t_s: 1679837388, - }, - stampStart: { + }), + stampStart: timestampProtocolToDb({ t_s: 1585229388, - }, + }), verificationStatus: DenominationVerificationStatus.Unverified, value: "KUDOS:2", currency: "KUDOS", - listIssueDate: { t_s: 0 }, + listIssueDate: timestampProtocolToDb({ t_s: 0 }), }, ]; diff --git a/packages/taler-wallet-core/src/pending-types.ts b/packages/taler-wallet-core/src/pending-types.ts index 627888b4d..e7a40e81b 100644 --- a/packages/taler-wallet-core/src/pending-types.ts +++ b/packages/taler-wallet-core/src/pending-types.ts @@ -25,7 +25,7 @@ * Imports. */ import { TalerErrorDetail, AbsoluteTime } from "@gnu-taler/taler-util"; -import { RetryInfo } from "./operations/common.js"; +import { DbRetryInfo } from "./operations/common.js"; export enum PendingTaskType { ExchangeUpdate = "exchange-update", @@ -137,7 +137,7 @@ export interface PendingRefreshTask { lastError?: TalerErrorDetail; refreshGroupId: string; finishedPerCoin: boolean[]; - retryInfo?: RetryInfo; + retryInfo?: DbRetryInfo; } /** @@ -156,7 +156,7 @@ export interface PendingTipPickupTask { export interface PendingPurchaseTask { type: PendingTaskType.Purchase; proposalId: string; - retryInfo?: RetryInfo; + retryInfo?: DbRetryInfo; /** * Status of the payment as string, used only for debugging. */ @@ -167,7 +167,7 @@ export interface PendingPurchaseTask { export interface PendingRecoupTask { type: PendingTaskType.Recoup; recoupGroupId: string; - retryInfo?: RetryInfo; + retryInfo?: DbRetryInfo; lastError: TalerErrorDetail | undefined; } @@ -177,7 +177,7 @@ export interface PendingRecoupTask { export interface PendingWithdrawTask { type: PendingTaskType.Withdraw; lastError: TalerErrorDetail | undefined; - retryInfo?: RetryInfo; + retryInfo?: DbRetryInfo; withdrawalGroupId: string; } @@ -187,7 +187,7 @@ export interface PendingWithdrawTask { export interface PendingDepositTask { type: PendingTaskType.Deposit; lastError: TalerErrorDetail | undefined; - retryInfo: RetryInfo | undefined; + retryInfo: DbRetryInfo | undefined; depositGroupId: string; } @@ -233,7 +233,7 @@ export interface PendingTaskInfoCommon { * Retry info. Currently used to stop the wallet after any operation * exceeds a number of retries. */ - retryInfo?: RetryInfo; + retryInfo?: DbRetryInfo; } /** diff --git a/packages/taler-wallet-core/src/util/denominations.ts b/packages/taler-wallet-core/src/util/denominations.ts index 76716cf7a..db6e69956 100644 --- a/packages/taler-wallet-core/src/util/denominations.ts +++ b/packages/taler-wallet-core/src/util/denominations.ts @@ -26,10 +26,9 @@ import { FeeDescriptionPair, TalerProtocolTimestamp, TimePoint, - WireFee, } from "@gnu-taler/taler-util"; import { DenominationRecord } from "../db.js"; -import { WalletConfig } from "../index.js"; +import { timestampProtocolFromDb } from "../index.js"; /** * Given a list of denominations with the same value and same period of time: @@ -457,9 +456,11 @@ export function isWithdrawableDenom( denomselAllowLate?: boolean, ): boolean { const now = AbsoluteTime.now(); - const start = AbsoluteTime.fromProtocolTimestamp(d.stampStart); + const start = AbsoluteTime.fromProtocolTimestamp( + timestampProtocolFromDb(d.stampStart), + ); const withdrawExpire = AbsoluteTime.fromProtocolTimestamp( - d.stampExpireWithdraw, + timestampProtocolFromDb(d.stampExpireWithdraw), ); const started = AbsoluteTime.cmp(now, start) >= 0; let lastPossibleWithdraw: AbsoluteTime; diff --git a/packages/taler-wallet-core/src/util/instructedAmountConversion.ts b/packages/taler-wallet-core/src/util/instructedAmountConversion.ts index 54c08eee4..a0394a687 100644 --- a/packages/taler-wallet-core/src/util/instructedAmountConversion.ts +++ b/packages/taler-wallet-core/src/util/instructedAmountConversion.ts @@ -14,6 +14,7 @@ GNU Taler; see the file COPYING. If not, see */ +import { GlobalIDB } from "@gnu-taler/idb-bridge"; import { AbsoluteTime, AgeRestriction, @@ -29,14 +30,14 @@ import { parsePaytoUri, strcmp, } from "@gnu-taler/taler-util"; -import { checkDbInvariant } from "./invariants.js"; import { DenominationRecord, InternalWalletState, getExchangeDetails, + timestampProtocolFromDb, } from "../index.js"; import { CoinInfo } from "./coinSelection.js"; -import { GlobalIDB } from "@gnu-taler/idb-bridge"; +import { checkDbInvariant } from "./invariants.js"; /** * If the operation going to be plan subtracts @@ -224,10 +225,10 @@ async function getAvailableDenoms( ); for (const denom of ds) { const expiresWithdraw = AbsoluteTime.fromProtocolTimestamp( - denom.stampExpireWithdraw, + timestampProtocolFromDb(denom.stampExpireWithdraw), ); const expiresDeposit = AbsoluteTime.fromProtocolTimestamp( - denom.stampExpireDeposit, + timestampProtocolFromDb(denom.stampExpireDeposit), ); creditDeadline = AbsoluteTime.min(deadline, expiresWithdraw); debitDeadline = AbsoluteTime.min(deadline, expiresDeposit); @@ -270,10 +271,10 @@ async function getAvailableDenoms( continue; } const expiresWithdraw = AbsoluteTime.fromProtocolTimestamp( - denom.stampExpireWithdraw, + timestampProtocolFromDb(denom.stampExpireWithdraw), ); const expiresDeposit = AbsoluteTime.fromProtocolTimestamp( - denom.stampExpireDeposit, + timestampProtocolFromDb(denom.stampExpireDeposit), ); creditDeadline = AbsoluteTime.min(deadline, expiresWithdraw); debitDeadline = AbsoluteTime.min(deadline, expiresDeposit); @@ -318,7 +319,9 @@ function buildCoinInfoFromDenom( exchangeBaseUrl: denom.exchangeBaseUrl, duration: AbsoluteTime.difference( AbsoluteTime.now(), - AbsoluteTime.fromProtocolTimestamp(denom.stampExpireDeposit), + AbsoluteTime.fromProtocolTimestamp( + timestampProtocolFromDb(denom.stampExpireDeposit), + ), ), totalAvailable: total, value: Amounts.parseOrThrow(denom.value),