wallet-core: consistently use usec timestamps in DB

This commit is contained in:
Florian Dold 2023-09-14 17:36:15 +02:00
parent f4587c44fd
commit 1ce53e1c21
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
17 changed files with 365 additions and 227 deletions

View File

@ -67,7 +67,7 @@ export interface TransactionsRequest {
*/ */
includeRefreshes?: boolean; includeRefreshes?: boolean;
filterByState?: TransactionStateFilter filterByState?: TransactionStateFilter;
} }
export interface TransactionState { export interface TransactionState {
@ -629,6 +629,17 @@ export interface TransactionRefresh extends TransactionCommon {
refreshOutputAmount: AmountString; 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 * Deposit transaction, which effectively sends
* money from this wallet somewhere else. * money from this wallet somewhere else.
@ -662,16 +673,7 @@ export interface TransactionDeposit extends TransactionCommon {
*/ */
deposited: boolean; deposited: boolean;
trackingState: Array<{ trackingState: Array<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;
}>;
} }
export interface TransactionByIdRequest { export interface TransactionByIdRequest {

View File

@ -87,7 +87,7 @@ import {
WithdrawalPlanchet, WithdrawalPlanchet,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
// FIXME: Crypto should not use DB Types! // FIXME: Crypto should not use DB Types!
import { DenominationRecord } from "../db.js"; import { DenominationRecord, timestampProtocolFromDb } from "../db.js";
import { import {
CreateRecoupRefreshReqRequest, CreateRecoupRefreshReqRequest,
CreateRecoupReqRequest, CreateRecoupReqRequest,
@ -962,10 +962,22 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
const value: AmountJson = Amounts.parseOrThrow(denom.value); const value: AmountJson = Amounts.parseOrThrow(denom.value);
const p = buildSigPS(TalerSignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY) const p = buildSigPS(TalerSignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY)
.put(decodeCrock(masterPub)) .put(decodeCrock(masterPub))
.put(timestampRoundedToBuffer(denom.stampStart)) .put(timestampRoundedToBuffer(timestampProtocolFromDb(denom.stampStart)))
.put(timestampRoundedToBuffer(denom.stampExpireWithdraw)) .put(
.put(timestampRoundedToBuffer(denom.stampExpireDeposit)) timestampRoundedToBuffer(
.put(timestampRoundedToBuffer(denom.stampExpireLegal)) timestampProtocolFromDb(denom.stampExpireWithdraw),
),
)
.put(
timestampRoundedToBuffer(
timestampProtocolFromDb(denom.stampExpireDeposit),
),
)
.put(
timestampRoundedToBuffer(
timestampProtocolFromDb(denom.stampExpireLegal),
),
)
.put(amountToBuffer(value)) .put(amountToBuffer(value))
.put(amountToBuffer(denom.fees.feeWithdraw)) .put(amountToBuffer(denom.fees.feeWithdraw))
.put(amountToBuffer(denom.fees.feeDeposit)) .put(amountToBuffer(denom.fees.feeDeposit))

View File

@ -27,6 +27,7 @@ import {
structuredEncapsulate, structuredEncapsulate,
} from "@gnu-taler/idb-bridge"; } from "@gnu-taler/idb-bridge";
import { import {
AbsoluteTime,
AgeCommitmentProof, AgeCommitmentProof,
AmountString, AmountString,
Amounts, Amounts,
@ -51,12 +52,13 @@ import {
TalerPreciseTimestamp, TalerPreciseTimestamp,
TalerProtocolDuration, TalerProtocolDuration,
TalerProtocolTimestamp, TalerProtocolTimestamp,
//TalerProtocolTimestamp,
TransactionIdStr, TransactionIdStr,
UnblindedSignature, UnblindedSignature,
WireInfo, WireInfo,
codecForAny, codecForAny,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { RetryInfo, TaskIdentifiers } from "./operations/common.js"; import { DbRetryInfo, TaskIdentifiers } from "./operations/common.js";
import { import {
DbAccess, DbAccess,
DbReadOnlyTransaction, 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 * Format of the operation status code: 0x0abc_nnnn
@ -391,22 +431,22 @@ export interface DenominationRecord {
/** /**
* Validity start date of the denomination. * Validity start date of the denomination.
*/ */
stampStart: TalerProtocolTimestamp; stampStart: DbProtocolTimestamp;
/** /**
* Date after which the currency can't be withdrawn anymore. * Date after which the currency can't be withdrawn anymore.
*/ */
stampExpireWithdraw: TalerProtocolTimestamp; stampExpireWithdraw: DbProtocolTimestamp;
/** /**
* Date after the denomination officially doesn't exist anymore. * 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. * 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 * 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 * Latest list issue date of the "/keys" response
* that includes this denomination. * that includes this denomination.
*/ */
listIssueDate: TalerProtocolTimestamp; listIssueDate: DbProtocolTimestamp;
} }
export namespace DenominationRecord { export namespace DenominationRecord {
@ -460,10 +500,10 @@ export namespace DenominationRecord {
feeRefresh: Amounts.stringify(d.fees.feeRefresh), feeRefresh: Amounts.stringify(d.fees.feeRefresh),
feeRefund: Amounts.stringify(d.fees.feeRefund), feeRefund: Amounts.stringify(d.fees.feeRefund),
feeWithdraw: Amounts.stringify(d.fees.feeWithdraw), feeWithdraw: Amounts.stringify(d.fees.feeWithdraw),
stampExpireDeposit: d.stampExpireDeposit, stampExpireDeposit: timestampProtocolFromDb(d.stampExpireDeposit),
stampExpireLegal: d.stampExpireLegal, stampExpireLegal: timestampProtocolFromDb(d.stampExpireLegal),
stampExpireWithdraw: d.stampExpireWithdraw, stampExpireWithdraw: timestampProtocolFromDb(d.stampExpireWithdraw),
stampStart: d.stampStart, stampStart: timestampProtocolFromDb(d.stampStart),
value: Amounts.stringify(d.value), value: Amounts.stringify(d.value),
exchangeBaseUrl: d.exchangeBaseUrl, exchangeBaseUrl: d.exchangeBaseUrl,
}; };
@ -471,9 +511,9 @@ export namespace DenominationRecord {
} }
export interface ExchangeSignkeysRecord { export interface ExchangeSignkeysRecord {
stampStart: TalerProtocolTimestamp; stampStart: DbProtocolTimestamp;
stampExpire: TalerProtocolTimestamp; stampExpire: DbProtocolTimestamp;
stampEnd: TalerProtocolTimestamp; stampEnd: DbProtocolTimestamp;
signkeyPub: EddsaPublicKeyString; signkeyPub: EddsaPublicKeyString;
masterSig: EddsaSignatureString; masterSig: EddsaSignatureString;
@ -590,11 +630,6 @@ export enum ExchangeEntryDbUpdateStatus {
ReadyUpdate = 7, ReadyUpdate = 7,
} }
/**
* Timestamp stored as a IEEE 754 double, in milliseconds.
*/
export type DbIndexableTimestampMs = number;
/** /**
* Exchange record as stored in the wallet's database. * Exchange record as stored in the wallet's database.
*/ */
@ -634,13 +669,8 @@ export interface ExchangeEntryRecord {
/** /**
* Next scheduled update for the exchange. * 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; lastKeysEtag: string | undefined;
@ -650,7 +680,7 @@ export interface ExchangeEntryRecord {
* Updated whenever the exchange's denominations are updated or when * Updated whenever the exchange's denominations are updated or when
* the refresh check has been done. * the refresh check has been done.
*/ */
nextRefreshCheckStampMs: DbIndexableTimestampMs; nextRefreshCheckStamp: DbPreciseTimestamp;
/** /**
* Public key of the reserve that we're currently using for * 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. * 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. * 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. * 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 * 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. // Raw wire transfer identifier of the deposit.
wireTransferId: string; wireTransferId: string;
// When was the wire transfer given to the bank. // When was the wire transfer given to the bank.
timestampExecuted: TalerProtocolTimestamp; timestampExecuted: DbProtocolTimestamp;
// Total amount transfer for this wtid (including fees) // Total amount transfer for this wtid (including fees)
amountRaw: AmountString; amountRaw: AmountString;
// Wire fee amount for this exchange // Wire fee amount for this exchange
@ -1690,7 +1720,7 @@ export interface DepositGroupRecord {
*/ */
amount: AmountString; amount: AmountString;
wireTransferDeadline: TalerProtocolTimestamp; wireTransferDeadline: DbProtocolTimestamp;
merchantPub: string; merchantPub: string;
merchantPriv: string; merchantPriv: string;
@ -1831,7 +1861,7 @@ export interface PeerPushDebitRecord {
*/ */
contractEncNonce: string; contractEncNonce: string;
purseExpiration: TalerProtocolTimestamp; purseExpiration: DbProtocolTimestamp;
timestampCreated: DbPreciseTimestamp; timestampCreated: DbPreciseTimestamp;
@ -2077,7 +2107,7 @@ export interface OperationRetryRecord {
lastError?: TalerErrorDetail; lastError?: TalerErrorDetail;
retryInfo: RetryInfo; retryInfo: DbRetryInfo;
} }
/** /**
@ -2130,9 +2160,8 @@ export interface UserAttentionRecord {
/** /**
* When the notification was created. * When the notification was created.
* FIXME: This should be a TalerPreciseTimestamp
*/ */
createdMs: number; created: DbPreciseTimestamp;
/** /**
* When the user mark this notification as read. * When the user mark this notification as read.
@ -2233,7 +2262,7 @@ export interface RefundItemRecord {
/** /**
* Execution time as claimed by the merchant * Execution time as claimed by the merchant
*/ */
executionTime: TalerProtocolTimestamp; executionTime: DbProtocolTimestamp;
/** /**
* Time when the wallet became aware of the refund. * Time when the wallet became aware of the refund.

View File

@ -31,7 +31,7 @@ import {
UserAttentionUnreadList, UserAttentionUnreadList,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { InternalWalletState } from "../internal-wallet-state.js"; 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"); const logger = new Logger("operations/attention.ts");
@ -75,7 +75,7 @@ export async function getUserAttentions(
return; return;
pending.push({ pending.push({
info: x.info, info: x.info,
when: TalerPreciseTimestamp.fromMilliseconds(x.createdMs), when: timestampPreciseFromDb(x.created),
read: x.read !== undefined, read: x.read !== undefined,
}); });
}); });
@ -118,7 +118,7 @@ export async function addAttentionRequest(
await tx.userAttention.put({ await tx.userAttention.put({
info, info,
entityId, entityId,
createdMs: AbsoluteTime.now().t_ms as number, created: timestampPreciseToDb(TalerPreciseTimestamp.now()),
read: undefined, read: undefined,
}); });
}); });

View File

@ -40,6 +40,7 @@ import {
TalerError, TalerError,
TalerErrorCode, TalerErrorCode,
TalerErrorDetail, TalerErrorDetail,
TalerPreciseTimestamp,
TombstoneIdStr, TombstoneIdStr,
TransactionIdStr, TransactionIdStr,
TransactionType, TransactionType,
@ -49,6 +50,7 @@ import { CryptoApiStoppedError } from "../crypto/workers/crypto-dispatcher.js";
import { import {
BackupProviderRecord, BackupProviderRecord,
CoinRecord, CoinRecord,
DbPreciseTimestamp,
DepositGroupRecord, DepositGroupRecord,
ExchangeDetailsRecord, ExchangeDetailsRecord,
ExchangeEntryDbRecordStatus, ExchangeEntryDbRecordStatus,
@ -62,6 +64,7 @@ import {
RecoupGroupRecord, RecoupGroupRecord,
RefreshGroupRecord, RefreshGroupRecord,
RewardRecord, RewardRecord,
timestampPreciseToDb,
WalletStoresV1, WalletStoresV1,
WithdrawalGroupRecord, WithdrawalGroupRecord,
} from "../db.js"; } from "../db.js";
@ -360,11 +363,11 @@ async function storePendingTaskError(
retryRecord = { retryRecord = {
id: pendingTaskId, id: pendingTaskId,
lastError: e, lastError: e,
retryInfo: RetryInfo.reset(), retryInfo: DbRetryInfo.reset(),
}; };
} else { } else {
retryRecord.lastError = e; retryRecord.lastError = e;
retryRecord.retryInfo = RetryInfo.increment(retryRecord.retryInfo); retryRecord.retryInfo = DbRetryInfo.increment(retryRecord.retryInfo);
} }
await tx.operationRetries.put(retryRecord); await tx.operationRetries.put(retryRecord);
return taskToTransactionNotification(ws, tx, pendingTaskId, e); return taskToTransactionNotification(ws, tx, pendingTaskId, e);
@ -383,7 +386,7 @@ export async function resetPendingTaskTimeout(
if (retryRecord) { if (retryRecord) {
// Note that we don't reset the lastError, it should still be visible // Note that we don't reset the lastError, it should still be visible
// while the retry runs. // while the retry runs.
retryRecord.retryInfo = RetryInfo.reset(); retryRecord.retryInfo = DbRetryInfo.reset();
await tx.operationRetries.put(retryRecord); await tx.operationRetries.put(retryRecord);
} }
return taskToTransactionNotification(ws, tx, pendingTaskId, undefined); return taskToTransactionNotification(ws, tx, pendingTaskId, undefined);
@ -403,14 +406,14 @@ async function storePendingTaskPending(
if (!retryRecord) { if (!retryRecord) {
retryRecord = { retryRecord = {
id: pendingTaskId, id: pendingTaskId,
retryInfo: RetryInfo.reset(), retryInfo: DbRetryInfo.reset(),
}; };
} else { } else {
if (retryRecord.lastError) { if (retryRecord.lastError) {
hadError = true; hadError = true;
} }
delete retryRecord.lastError; delete retryRecord.lastError;
retryRecord.retryInfo = RetryInfo.increment(retryRecord.retryInfo); retryRecord.retryInfo = DbRetryInfo.increment(retryRecord.retryInfo);
} }
await tx.operationRetries.put(retryRecord); await tx.operationRetries.put(retryRecord);
if (hadError) { if (hadError) {
@ -736,9 +739,9 @@ export interface TaskRunLongpollResult {
type: TaskRunResultType.Longpoll; type: TaskRunResultType.Longpoll;
} }
export interface RetryInfo { export interface DbRetryInfo {
firstTry: AbsoluteTime; firstTry: DbPreciseTimestamp;
nextRetry: AbsoluteTime; nextRetry: DbPreciseTimestamp;
retryCounter: number; retryCounter: number;
} }
@ -755,7 +758,7 @@ const defaultRetryPolicy: RetryPolicy = {
}; };
function updateTimeout( function updateTimeout(
r: RetryInfo, r: DbRetryInfo,
p: RetryPolicy = defaultRetryPolicy, p: RetryPolicy = defaultRetryPolicy,
): void { ): void {
const now = AbsoluteTime.now(); const now = AbsoluteTime.now();
@ -763,7 +766,9 @@ function updateTimeout(
throw Error("assertion failed"); throw Error("assertion failed");
} }
if (p.backoffDelta.d_ms === "forever") { if (p.backoffDelta.d_ms === "forever") {
r.nextRetry = AbsoluteTime.never(); r.nextRetry = timestampPreciseToDb(
AbsoluteTime.toPreciseTimestamp(AbsoluteTime.never()),
);
return; return;
} }
@ -775,12 +780,12 @@ function updateTimeout(
(p.maxTimeout.d_ms === "forever" (p.maxTimeout.d_ms === "forever"
? nextIncrement ? nextIncrement
: Math.min(p.maxTimeout.d_ms, 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( export function getDuration(
r: RetryInfo | undefined, r: DbRetryInfo | undefined,
p: RetryPolicy = defaultRetryPolicy, p: RetryPolicy = defaultRetryPolicy,
): Duration { ): Duration {
if (!r) { if (!r) {
@ -797,11 +802,11 @@ export namespace RetryInfo {
}; };
} }
export function reset(p: RetryPolicy = defaultRetryPolicy): RetryInfo { export function reset(p: RetryPolicy = defaultRetryPolicy): DbRetryInfo {
const now = AbsoluteTime.now(); const now = TalerPreciseTimestamp.now();
const info = { const info: DbRetryInfo = {
firstTry: now, firstTry: timestampPreciseToDb(now),
nextRetry: now, nextRetry: timestampPreciseToDb(now),
retryCounter: 0, retryCounter: 0,
}; };
updateTimeout(info, p); updateTimeout(info, p);
@ -809,9 +814,9 @@ export namespace RetryInfo {
} }
export function increment( export function increment(
r: RetryInfo | undefined, r: DbRetryInfo | undefined,
p: RetryPolicy = defaultRetryPolicy, p: RetryPolicy = defaultRetryPolicy,
): RetryInfo { ): DbRetryInfo {
if (!r) { if (!r) {
return reset(p); return reset(p);
} }

View File

@ -74,6 +74,8 @@ import {
createRefreshGroup, createRefreshGroup,
getTotalRefreshCost, getTotalRefreshCost,
timestampPreciseToDb, timestampPreciseToDb,
timestampProtocolFromDb,
timestampProtocolToDb,
} from "../index.js"; } from "../index.js";
import { InternalWalletState } from "../internal-wallet-state.js"; import { InternalWalletState } from "../internal-wallet-state.js";
import { assertUnreachable } from "../util/assertUnreachable.js"; import { assertUnreachable } from "../util/assertUnreachable.js";
@ -800,7 +802,7 @@ async function processDepositGroupPendingTrack(
amountRaw: Amounts.stringify(raw), amountRaw: Amounts.stringify(raw),
wireFee: Amounts.stringify(wireFee), wireFee: Amounts.stringify(wireFee),
exchangePub: track.exchange_pub, exchangePub: track.exchange_pub,
timestampExecuted: track.execution_time, timestampExecuted: timestampProtocolToDb(track.execution_time),
wireTransferId: track.wtid, wireTransferId: track.wtid,
}, },
id: track.exchange_sig, id: track.exchange_sig,
@ -1393,7 +1395,9 @@ export async function createDepositGroup(
counterpartyEffectiveDepositAmount: Amounts.stringify( counterpartyEffectiveDepositAmount: Amounts.stringify(
counterpartyEffectiveDepositAmount, counterpartyEffectiveDepositAmount,
), ),
wireTransferDeadline: contractTerms.wire_transfer_deadline, wireTransferDeadline: timestampProtocolToDb(
contractTerms.wire_transfer_deadline,
),
wire: { wire: {
payto_uri: req.depositPaytoUri, payto_uri: req.depositPaytoUri,
salt: wireSalt, salt: wireSalt,

View File

@ -74,7 +74,9 @@ import {
ExchangeEntryDbRecordStatus, ExchangeEntryDbRecordStatus,
ExchangeEntryDbUpdateStatus, ExchangeEntryDbUpdateStatus,
isWithdrawableDenom, isWithdrawableDenom,
timestampPreciseFromDb,
timestampPreciseToDb, timestampPreciseToDb,
timestampProtocolToDb,
WalletDbReadWriteTransaction, WalletDbReadWriteTransaction,
} from "../index.js"; } from "../index.js";
import { InternalWalletState, TrustInfo } from "../internal-wallet-state.js"; import { InternalWalletState, TrustInfo } from "../internal-wallet-state.js";
@ -317,8 +319,12 @@ export async function addPresetExchangeEntry(
detailsPointer: undefined, detailsPointer: undefined,
lastUpdate: undefined, lastUpdate: undefined,
lastKeysEtag: undefined, lastKeysEtag: undefined,
nextRefreshCheckStampMs: AbsoluteTime.getStampMsNever(), nextRefreshCheckStamp: timestampPreciseToDb(
nextUpdateStampMs: AbsoluteTime.getStampMsNever(), AbsoluteTime.toPreciseTimestamp(AbsoluteTime.never()),
),
nextUpdateStamp: timestampPreciseToDb(
AbsoluteTime.toPreciseTimestamp(AbsoluteTime.never()),
),
}; };
await tx.exchanges.put(r); await tx.exchanges.put(r);
} }
@ -344,8 +350,12 @@ export async function provideExchangeRecordInTx(
baseUrl: baseUrl, baseUrl: baseUrl,
detailsPointer: undefined, detailsPointer: undefined,
lastUpdate: undefined, lastUpdate: undefined,
nextUpdateStampMs: AbsoluteTime.getStampMsNever(), nextUpdateStamp: timestampPreciseToDb(
nextRefreshCheckStampMs: AbsoluteTime.getStampMsNever(), AbsoluteTime.toPreciseTimestamp(AbsoluteTime.never()),
),
nextRefreshCheckStamp: timestampPreciseToDb(
AbsoluteTime.toPreciseTimestamp(AbsoluteTime.never()),
),
lastKeysEtag: undefined, lastKeysEtag: undefined,
}; };
await tx.exchanges.put(r); await tx.exchanges.put(r);
@ -446,13 +456,19 @@ async function downloadExchangeKeysInfo(
isRevoked: false, isRevoked: false,
value: Amounts.stringify(value), value: Amounts.stringify(value),
currency: value.currency, currency: value.currency,
stampExpireDeposit: denomIn.stamp_expire_deposit, stampExpireDeposit: timestampProtocolToDb(
stampExpireLegal: denomIn.stamp_expire_legal, denomIn.stamp_expire_deposit,
stampExpireWithdraw: denomIn.stamp_expire_withdraw, ),
stampStart: denomIn.stamp_start, stampExpireLegal: timestampProtocolToDb(denomIn.stamp_expire_legal),
stampExpireWithdraw: timestampProtocolToDb(
denomIn.stamp_expire_withdraw,
),
stampStart: timestampProtocolToDb(denomIn.stamp_start),
verificationStatus: DenominationVerificationStatus.Unverified, verificationStatus: DenominationVerificationStatus.Unverified,
masterSig: denomIn.master_sig, masterSig: denomIn.master_sig,
listIssueDate: exchangeKeysJsonUnchecked.list_issue_date, listIssueDate: timestampProtocolToDb(
exchangeKeysJsonUnchecked.list_issue_date,
),
fees: { fees: {
feeDeposit: Amounts.stringify(denomGroup.fee_deposit), feeDeposit: Amounts.stringify(denomGroup.fee_deposit),
feeRefresh: Amounts.stringify(denomGroup.fee_refresh), feeRefresh: Amounts.stringify(denomGroup.fee_refresh),
@ -614,7 +630,9 @@ export async function updateExchangeFromUrlHandler(
!forceNow && !forceNow &&
exchangeDetails !== undefined && exchangeDetails !== undefined &&
!AbsoluteTime.isExpired( !AbsoluteTime.isExpired(
AbsoluteTime.fromStampMs(exchange.nextUpdateStampMs), AbsoluteTime.fromPreciseTimestamp(
timestampPreciseFromDb(exchange.nextUpdateStamp),
),
) )
) { ) {
logger.trace("using existing exchange info"); logger.trace("using existing exchange info");
@ -755,11 +773,15 @@ export async function updateExchangeFromUrlHandler(
newDetails.rowId = existingDetails.rowId; newDetails.rowId = existingDetails.rowId;
} }
r.lastUpdate = timestampPreciseToDb(TalerPreciseTimestamp.now()); r.lastUpdate = timestampPreciseToDb(TalerPreciseTimestamp.now());
r.nextUpdateStampMs = AbsoluteTime.toStampMs( r.nextUpdateStamp = timestampPreciseToDb(
AbsoluteTime.fromProtocolTimestamp(keysInfo.expiry), AbsoluteTime.toPreciseTimestamp(
AbsoluteTime.fromProtocolTimestamp(keysInfo.expiry),
),
); );
// New denominations might be available. // New denominations might be available.
r.nextRefreshCheckStampMs = AbsoluteTime.getStampMsNow(); r.nextRefreshCheckStamp = timestampPreciseToDb(
TalerPreciseTimestamp.now(),
);
if (detailsPointerChanged) { if (detailsPointerChanged) {
r.detailsPointer = { r.detailsPointer = {
currency: newDetails.currency, currency: newDetails.currency,
@ -777,9 +799,9 @@ export async function updateExchangeFromUrlHandler(
exchangeDetailsRowId: drRowId.key, exchangeDetailsRowId: drRowId.key,
masterSig: sk.master_sig, masterSig: sk.master_sig,
signkeyPub: sk.key, signkeyPub: sk.key,
stampEnd: sk.stamp_end, stampEnd: timestampProtocolToDb(sk.stamp_end),
stampExpire: sk.stamp_expire, stampExpire: timestampProtocolToDb(sk.stamp_expire),
stampStart: sk.stamp_start, stampStart: timestampProtocolToDb(sk.stamp_start),
}); });
} }
@ -814,7 +836,7 @@ export async function updateExchangeFromUrlHandler(
); );
} }
} else { } else {
x.listIssueDate = keysInfo.listIssueDate; x.listIssueDate = timestampProtocolToDb(keysInfo.listIssueDate);
if (!x.isOffered) { if (!x.isOffered) {
x.isOffered = true; x.isOffered = true;
logger.info( logger.info(

View File

@ -104,6 +104,8 @@ import {
RefundItemRecord, RefundItemRecord,
RefundItemStatus, RefundItemStatus,
timestampPreciseToDb, timestampPreciseToDb,
timestampProtocolFromDb,
timestampProtocolToDb,
} from "../index.js"; } from "../index.js";
import { import {
EXCHANGE_COINS_LOCK, EXCHANGE_COINS_LOCK,
@ -115,7 +117,7 @@ import { checkDbInvariant } from "../util/invariants.js";
import { GetReadOnlyAccess } from "../util/query.js"; import { GetReadOnlyAccess } from "../util/query.js";
import { import {
constructTaskIdentifier, constructTaskIdentifier,
RetryInfo, DbRetryInfo,
runLongpollAsync, runLongpollAsync,
runTaskWithErrorReporting, runTaskWithErrorReporting,
spendCoins, spendCoins,
@ -217,11 +219,13 @@ async function failProposalPermanently(
notifyTransition(ws, transactionId, transitionInfo); notifyTransition(ws, transactionId, transitionInfo);
} }
function getProposalRequestTimeout(retryInfo?: RetryInfo): Duration { function getProposalRequestTimeout(retryInfo?: DbRetryInfo): Duration {
return Duration.clamp({ return Duration.clamp({
lower: Duration.fromSpec({ seconds: 1 }), lower: Duration.fromSpec({ seconds: 1 }),
upper: Duration.fromSpec({ seconds: 60 }), 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); const ar = Duration.fromTalerProtocolDuration(protoAr);
logger.info("auto_refund present"); logger.info("auto_refund present");
purchase.purchaseStatus = PurchaseStatus.PendingQueryingAutoRefund; purchase.purchaseStatus = PurchaseStatus.PendingQueryingAutoRefund;
purchase.autoRefundDeadline = AbsoluteTime.toProtocolTimestamp( purchase.autoRefundDeadline = timestampProtocolToDb(
AbsoluteTime.addDuration(AbsoluteTime.now(), ar), AbsoluteTime.toProtocolTimestamp(
AbsoluteTime.addDuration(AbsoluteTime.now(), ar),
),
); );
} }
await tx.purchases.put(purchase); await tx.purchases.put(purchase);
@ -2343,7 +2349,9 @@ async function processPurchaseAutoRefund(
if ( if (
!purchase.autoRefundDeadline || !purchase.autoRefundDeadline ||
AbsoluteTime.isExpired( AbsoluteTime.isExpired(
AbsoluteTime.fromProtocolTimestamp(purchase.autoRefundDeadline), AbsoluteTime.fromProtocolTimestamp(
timestampProtocolFromDb(purchase.autoRefundDeadline),
),
) )
) { ) {
const transitionInfo = await ws.db const transitionInfo = await ws.db
@ -2804,7 +2812,7 @@ async function storeRefunds(
const status: RefundItemStatus = getItemStatus(rf); const status: RefundItemStatus = getItemStatus(rf);
const newItem: RefundItemRecord = { const newItem: RefundItemRecord = {
coinPub: rf.coin_pub, coinPub: rf.coin_pub,
executionTime: rf.execution_time, executionTime: timestampProtocolToDb(rf.execution_time),
obtainedTime: timestampPreciseToDb(now), obtainedTime: timestampPreciseToDb(now),
refundAmount: rf.refund_amount, refundAmount: rf.refund_amount,
refundGroupId: newGroup.refundGroupId, refundGroupId: newGroup.refundGroupId,

View File

@ -56,10 +56,13 @@ import {
RefreshOperationStatus, RefreshOperationStatus,
createRefreshGroup, createRefreshGroup,
timestampPreciseToDb, timestampPreciseToDb,
timestampProtocolFromDb,
timestampProtocolToDb,
} from "../index.js"; } from "../index.js";
import { InternalWalletState } from "../internal-wallet-state.js"; import { InternalWalletState } from "../internal-wallet-state.js";
import { PendingTaskType } from "../pending-types.js"; import { PendingTaskType } from "../pending-types.js";
import { assertUnreachable } from "../util/assertUnreachable.js"; import { assertUnreachable } from "../util/assertUnreachable.js";
import { PeerCoinRepair, selectPeerCoins } from "../util/coinSelection.js";
import { checkLogicInvariant } from "../util/invariants.js"; import { checkLogicInvariant } from "../util/invariants.js";
import { import {
TaskRunResult, TaskRunResult,
@ -78,7 +81,6 @@ import {
notifyTransition, notifyTransition,
stopLongpolling, stopLongpolling,
} from "./transactions.js"; } from "./transactions.js";
import { PeerCoinRepair, selectPeerCoins } from "../util/coinSelection.js";
const logger = new Logger("pay-peer-push-debit.ts"); const logger = new Logger("pay-peer-push-debit.ts");
@ -208,7 +210,7 @@ async function processPeerPushDebitCreateReserve(
mergePub: peerPushInitiation.mergePub, mergePub: peerPushInitiation.mergePub,
minAge: 0, minAge: 0,
purseAmount: peerPushInitiation.amount, purseAmount: peerPushInitiation.amount,
purseExpiration, purseExpiration: timestampProtocolFromDb(purseExpiration),
pursePriv: peerPushInitiation.pursePriv, pursePriv: peerPushInitiation.pursePriv,
}); });
@ -667,7 +669,7 @@ export async function initiatePeerPushDebit(
exchangeBaseUrl: sel.exchangeBaseUrl, exchangeBaseUrl: sel.exchangeBaseUrl,
mergePriv: mergePair.priv, mergePriv: mergePair.priv,
mergePub: mergePair.pub, mergePub: mergePair.pub,
purseExpiration: purseExpiration, purseExpiration: timestampProtocolToDb(purseExpiration),
pursePriv: pursePair.priv, pursePriv: pursePair.priv,
pursePub: pursePair.pub, pursePub: pursePair.pub,
timestampCreated: timestampPreciseToDb(TalerPreciseTimestamp.now()), timestampCreated: timestampPreciseToDb(TalerPreciseTimestamp.now()),

View File

@ -21,43 +21,46 @@
/** /**
* Imports. * Imports.
*/ */
import { GlobalIDB } from "@gnu-taler/idb-bridge";
import { AbsoluteTime, TransactionRecordFilter } from "@gnu-taler/taler-util";
import { import {
PurchaseStatus,
WalletStoresV1,
BackupProviderStateTag, BackupProviderStateTag,
RefreshCoinStatus, DepositElementStatus,
PeerPushDebitStatus,
PeerPullDebitRecordStatus,
PeerPushCreditStatus,
PeerPullPaymentCreditStatus,
WithdrawalGroupStatus,
RewardRecordStatus,
DepositOperationStatus,
RefreshGroupRecord,
WithdrawalGroupRecord,
DepositGroupRecord, DepositGroupRecord,
RewardRecord, DepositOperationStatus,
PurchaseRecord, ExchangeEntryDbUpdateStatus,
PeerPullCreditRecord, PeerPullCreditRecord,
PeerPullDebitRecordStatus,
PeerPullPaymentCreditStatus,
PeerPullPaymentIncomingRecord, PeerPullPaymentIncomingRecord,
PeerPushCreditStatus,
PeerPushDebitRecord, PeerPushDebitRecord,
PeerPushDebitStatus,
PeerPushPaymentIncomingRecord, PeerPushPaymentIncomingRecord,
PurchaseRecord,
PurchaseStatus,
RefreshCoinStatus,
RefreshGroupRecord,
RefreshOperationStatus,
RefundGroupRecord, RefundGroupRecord,
RefundGroupStatus, RefundGroupStatus,
ExchangeEntryDbUpdateStatus, RewardRecord,
RefreshOperationStatus, RewardRecordStatus,
DepositElementStatus, WalletStoresV1,
WithdrawalGroupRecord,
WithdrawalGroupStatus,
timestampAbsoluteFromDb,
timestampOptionalAbsoluteFromDb,
timestampPreciseFromDb, timestampPreciseFromDb,
timestampPreciseToDb,
} from "../db.js"; } from "../db.js";
import { InternalWalletState } from "../internal-wallet-state.js";
import { import {
PendingOperationsResponse, PendingOperationsResponse,
PendingTaskType, PendingTaskType,
TaskId, TaskId,
} from "../pending-types.js"; } 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 { GetReadOnlyAccess } from "../util/query.js";
import { GlobalIDB } from "@gnu-taler/idb-bridge";
import { TaskIdentifiers } from "./common.js"; import { TaskIdentifiers } from "./common.js";
function getPendingCommon( function getPendingCommon(
@ -100,12 +103,14 @@ async function gatherExchangePending(
} }
const opTag = TaskIdentifiers.forExchangeUpdate(exch); const opTag = TaskIdentifiers.forExchangeUpdate(exch);
let opr = await tx.operationRetries.get(opTag); let opr = await tx.operationRetries.get(opTag);
const timestampDue = const timestampDue = opr?.retryInfo.nextRetry ?? exch.nextRefreshCheckStamp;
opr?.retryInfo.nextRetry ??
AbsoluteTime.fromStampMs(exch.nextUpdateStampMs);
resp.pendingOperations.push({ resp.pendingOperations.push({
type: PendingTaskType.ExchangeUpdate, type: PendingTaskType.ExchangeUpdate,
...getPendingCommon(ws, opTag, timestampDue), ...getPendingCommon(
ws,
opTag,
AbsoluteTime.fromPreciseTimestamp(timestampPreciseFromDb(timestampDue)),
),
givesLifeness: false, givesLifeness: false,
exchangeBaseUrl: exch.baseUrl, exchangeBaseUrl: exch.baseUrl,
lastError: opr?.lastError, lastError: opr?.lastError,
@ -116,8 +121,16 @@ async function gatherExchangePending(
if (!opr?.lastError) { if (!opr?.lastError) {
resp.pendingOperations.push({ resp.pendingOperations.push({
type: PendingTaskType.ExchangeCheckRefresh, type: PendingTaskType.ExchangeCheckRefresh,
...getPendingCommon(ws, opTag, timestampDue), ...getPendingCommon(
timestampDue: AbsoluteTime.fromStampMs(exch.nextRefreshCheckStampMs), ws,
opTag,
AbsoluteTime.fromPreciseTimestamp(
timestampPreciseFromDb(timestampDue),
),
),
timestampDue: AbsoluteTime.fromPreciseTimestamp(
timestampPreciseFromDb(exch.nextRefreshCheckStamp),
),
givesLifeness: false, givesLifeness: false,
exchangeBaseUrl: exch.baseUrl, exchangeBaseUrl: exch.baseUrl,
}); });
@ -166,7 +179,9 @@ async function gatherRefreshPending(
} }
const opId = TaskIdentifiers.forRefresh(r); const opId = TaskIdentifiers.forRefresh(r);
const retryRecord = await tx.operationRetries.get(opId); 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({ resp.pendingOperations.push({
type: PendingTaskType.Refresh, type: PendingTaskType.Refresh,
...getPendingCommon(ws, opId, timestampDue), ...getPendingCommon(ws, opId, timestampDue),
@ -223,8 +238,8 @@ async function gatherWithdrawalPending(
opr = { opr = {
id: opTag, id: opTag,
retryInfo: { retryInfo: {
firstTry: now, firstTry: timestampPreciseToDb(AbsoluteTime.toPreciseTimestamp(now)),
nextRetry: now, nextRetry: timestampPreciseToDb(AbsoluteTime.toPreciseTimestamp(now)),
retryCounter: 0, retryCounter: 0,
}, },
}; };
@ -234,7 +249,8 @@ async function gatherWithdrawalPending(
...getPendingCommon( ...getPendingCommon(
ws, ws,
opTag, opTag,
opr.retryInfo?.nextRetry ?? AbsoluteTime.now(), timestampOptionalAbsoluteFromDb(opr.retryInfo?.nextRetry) ??
AbsoluteTime.now(),
), ),
givesLifeness: true, givesLifeness: true,
withdrawalGroupId: wsr.withdrawalGroupId, withdrawalGroupId: wsr.withdrawalGroupId,
@ -286,7 +302,9 @@ async function gatherDepositPending(
} }
const opId = TaskIdentifiers.forDeposit(dg); const opId = TaskIdentifiers.forDeposit(dg);
const retryRecord = await tx.operationRetries.get(opId); 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({ resp.pendingOperations.push({
type: PendingTaskType.Deposit, type: PendingTaskType.Deposit,
...getPendingCommon(ws, opId, timestampDue), ...getPendingCommon(ws, opId, timestampDue),
@ -331,13 +349,15 @@ async function gatherRewardPending(
await iterRecordsForReward(tx, { onlyState: "nonfinal" }, async (tip) => { await iterRecordsForReward(tx, { onlyState: "nonfinal" }, async (tip) => {
const opId = TaskIdentifiers.forTipPickup(tip); const opId = TaskIdentifiers.forTipPickup(tip);
const retryRecord = await tx.operationRetries.get(opId); 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) { if (tip.acceptedTimestamp) {
resp.pendingOperations.push({ resp.pendingOperations.push({
type: PendingTaskType.RewardPickup, type: PendingTaskType.RewardPickup,
...getPendingCommon(ws, opId, timestampDue), ...getPendingCommon(ws, opId, timestampDue),
givesLifeness: true, givesLifeness: true,
timestampDue: retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(), timestampDue,
merchantBaseUrl: tip.merchantBaseUrl, merchantBaseUrl: tip.merchantBaseUrl,
tipId: tip.walletRewardId, tipId: tip.walletRewardId,
merchantTipId: tip.merchantRewardId, merchantTipId: tip.merchantRewardId,
@ -391,7 +411,9 @@ async function gatherPurchasePending(
await iterRecordsForPurchase(tx, { onlyState: "nonfinal" }, async (pr) => { await iterRecordsForPurchase(tx, { onlyState: "nonfinal" }, async (pr) => {
const opId = TaskIdentifiers.forPay(pr); const opId = TaskIdentifiers.forPay(pr);
const retryRecord = await tx.operationRetries.get(opId); 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({ resp.pendingOperations.push({
type: PendingTaskType.Purchase, type: PendingTaskType.Purchase,
...getPendingCommon(ws, opId, timestampDue), ...getPendingCommon(ws, opId, timestampDue),
@ -420,7 +442,9 @@ async function gatherRecoupPending(
} }
const opId = TaskIdentifiers.forRecoup(rg); const opId = TaskIdentifiers.forRecoup(rg);
const retryRecord = await tx.operationRetries.get(opId); 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({ resp.pendingOperations.push({
type: PendingTaskType.Recoup, type: PendingTaskType.Recoup,
...getPendingCommon(ws, opId, timestampDue), ...getPendingCommon(ws, opId, timestampDue),
@ -445,8 +469,8 @@ async function gatherBackupPending(
const opId = TaskIdentifiers.forBackup(bp); const opId = TaskIdentifiers.forBackup(bp);
const retryRecord = await tx.operationRetries.get(opId); const retryRecord = await tx.operationRetries.get(opId);
if (bp.state.tag === BackupProviderStateTag.Ready) { if (bp.state.tag === BackupProviderStateTag.Ready) {
const timestampDue = AbsoluteTime.fromPreciseTimestamp( const timestampDue = timestampAbsoluteFromDb(
timestampPreciseFromDb(bp.state.nextBackupTimestamp), bp.state.nextBackupTimestamp,
); );
resp.pendingOperations.push({ resp.pendingOperations.push({
type: PendingTaskType.Backup, type: PendingTaskType.Backup,
@ -457,7 +481,8 @@ async function gatherBackupPending(
}); });
} else if (bp.state.tag === BackupProviderStateTag.Retrying) { } else if (bp.state.tag === BackupProviderStateTag.Retrying) {
const timestampDue = const timestampDue =
retryRecord?.retryInfo?.nextRetry ?? AbsoluteTime.now(); timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo?.nextRetry) ??
AbsoluteTime.now();
resp.pendingOperations.push({ resp.pendingOperations.push({
type: PendingTaskType.Backup, type: PendingTaskType.Backup,
...getPendingCommon(ws, opId, timestampDue), ...getPendingCommon(ws, opId, timestampDue),
@ -504,7 +529,8 @@ async function gatherPeerPullInitiationPending(
const opId = TaskIdentifiers.forPeerPullPaymentInitiation(pi); const opId = TaskIdentifiers.forPeerPullPaymentInitiation(pi);
const retryRecord = await tx.operationRetries.get(opId); const retryRecord = await tx.operationRetries.get(opId);
const timestampDue = const timestampDue =
retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(); timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ??
AbsoluteTime.now();
resp.pendingOperations.push({ resp.pendingOperations.push({
type: PendingTaskType.PeerPullCredit, type: PendingTaskType.PeerPullCredit,
...getPendingCommon(ws, opId, timestampDue), ...getPendingCommon(ws, opId, timestampDue),
@ -550,7 +576,8 @@ async function gatherPeerPullDebitPending(
const opId = TaskIdentifiers.forPeerPullPaymentDebit(pi); const opId = TaskIdentifiers.forPeerPullPaymentDebit(pi);
const retryRecord = await tx.operationRetries.get(opId); const retryRecord = await tx.operationRetries.get(opId);
const timestampDue = const timestampDue =
retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(); timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ??
AbsoluteTime.now();
resp.pendingOperations.push({ resp.pendingOperations.push({
type: PendingTaskType.PeerPullDebit, type: PendingTaskType.PeerPullDebit,
...getPendingCommon(ws, opId, timestampDue), ...getPendingCommon(ws, opId, timestampDue),
@ -596,7 +623,8 @@ async function gatherPeerPushInitiationPending(
const opId = TaskIdentifiers.forPeerPushPaymentInitiation(pi); const opId = TaskIdentifiers.forPeerPushPaymentInitiation(pi);
const retryRecord = await tx.operationRetries.get(opId); const retryRecord = await tx.operationRetries.get(opId);
const timestampDue = const timestampDue =
retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(); timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ??
AbsoluteTime.now();
resp.pendingOperations.push({ resp.pendingOperations.push({
type: PendingTaskType.PeerPushDebit, type: PendingTaskType.PeerPushDebit,
...getPendingCommon(ws, opId, timestampDue), ...getPendingCommon(ws, opId, timestampDue),
@ -646,7 +674,8 @@ async function gatherPeerPushCreditPending(
const opId = TaskIdentifiers.forPeerPushCredit(pi); const opId = TaskIdentifiers.forPeerPushCredit(pi);
const retryRecord = await tx.operationRetries.get(opId); const retryRecord = await tx.operationRetries.get(opId);
const timestampDue = const timestampDue =
retryRecord?.retryInfo.nextRetry ?? AbsoluteTime.now(); timestampOptionalAbsoluteFromDb(retryRecord?.retryInfo.nextRetry) ??
AbsoluteTime.now();
resp.pendingOperations.push({ resp.pendingOperations.push({
type: PendingTaskType.PeerPushCredit, type: PendingTaskType.PeerPushCredit,
...getPendingCommon(ws, opId, timestampDue), ...getPendingCommon(ws, opId, timestampDue),

View File

@ -81,6 +81,7 @@ import {
PendingTaskType, PendingTaskType,
RefreshSessionRecord, RefreshSessionRecord,
timestampPreciseToDb, timestampPreciseToDb,
timestampProtocolFromDb,
} from "../index.js"; } from "../index.js";
import { import {
EXCHANGE_COINS_LOCK, EXCHANGE_COINS_LOCK,
@ -1125,10 +1126,10 @@ export async function createRefreshGroup(
*/ */
function getAutoRefreshCheckThreshold(d: DenominationRecord): AbsoluteTime { function getAutoRefreshCheckThreshold(d: DenominationRecord): AbsoluteTime {
const expireWithdraw = AbsoluteTime.fromProtocolTimestamp( const expireWithdraw = AbsoluteTime.fromProtocolTimestamp(
d.stampExpireWithdraw, timestampProtocolFromDb(d.stampExpireWithdraw),
); );
const expireDeposit = AbsoluteTime.fromProtocolTimestamp( const expireDeposit = AbsoluteTime.fromProtocolTimestamp(
d.stampExpireDeposit, timestampProtocolFromDb(d.stampExpireDeposit),
); );
const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit); const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit);
const deltaDiv = durationMul(delta, 0.75); const deltaDiv = durationMul(delta, 0.75);
@ -1140,10 +1141,10 @@ function getAutoRefreshCheckThreshold(d: DenominationRecord): AbsoluteTime {
*/ */
function getAutoRefreshExecuteThreshold(d: DenominationRecord): AbsoluteTime { function getAutoRefreshExecuteThreshold(d: DenominationRecord): AbsoluteTime {
const expireWithdraw = AbsoluteTime.fromProtocolTimestamp( const expireWithdraw = AbsoluteTime.fromProtocolTimestamp(
d.stampExpireWithdraw, timestampProtocolFromDb(d.stampExpireWithdraw),
); );
const expireDeposit = AbsoluteTime.fromProtocolTimestamp( const expireDeposit = AbsoluteTime.fromProtocolTimestamp(
d.stampExpireDeposit, timestampProtocolFromDb(d.stampExpireDeposit),
); );
const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit); const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit);
const deltaDiv = durationMul(delta, 0.5); const deltaDiv = durationMul(delta, 0.5);
@ -1227,8 +1228,9 @@ export async function autoRefresh(
logger.trace( logger.trace(
`next refresh check at ${AbsoluteTime.toIsoString(minCheckThreshold)}`, `next refresh check at ${AbsoluteTime.toIsoString(minCheckThreshold)}`,
); );
exchange.nextRefreshCheckStampMs = exchange.nextRefreshCheckStamp = timestampPreciseToDb(
AbsoluteTime.toStampMs(minCheckThreshold); AbsoluteTime.toPreciseTimestamp(minCheckThreshold),
);
await tx.exchanges.put(exchange); await tx.exchanges.put(exchange);
}); });
return TaskRunResult.finished(); return TaskRunResult.finished();

View File

@ -52,6 +52,8 @@ import {
RewardRecordStatus, RewardRecordStatus,
timestampPreciseFromDb, timestampPreciseFromDb,
timestampPreciseToDb, timestampPreciseToDb,
timestampProtocolFromDb,
timestampProtocolToDb,
} from "../db.js"; } from "../db.js";
import { makeErrorDetail } from "@gnu-taler/taler-util"; import { makeErrorDetail } from "@gnu-taler/taler-util";
import { InternalWalletState } from "../internal-wallet-state.js"; import { InternalWalletState } from "../internal-wallet-state.js";
@ -201,7 +203,7 @@ export async function prepareTip(
acceptedTimestamp: undefined, acceptedTimestamp: undefined,
status: RewardRecordStatus.DialogAccept, status: RewardRecordStatus.DialogAccept,
rewardAmountRaw: Amounts.stringify(amount), rewardAmountRaw: Amounts.stringify(amount),
rewardExpiration: tipPickupStatus.expiration, rewardExpiration: timestampProtocolToDb(tipPickupStatus.expiration),
exchangeBaseUrl: tipPickupStatus.exchange_url, exchangeBaseUrl: tipPickupStatus.exchange_url,
next_url: tipPickupStatus.next_url, next_url: tipPickupStatus.next_url,
merchantBaseUrl: res.merchantBaseUrl, merchantBaseUrl: res.merchantBaseUrl,
@ -231,7 +233,7 @@ export async function prepareTip(
rewardAmountRaw: Amounts.stringify(tipRecord.rewardAmountRaw), rewardAmountRaw: Amounts.stringify(tipRecord.rewardAmountRaw),
exchangeBaseUrl: tipRecord.exchangeBaseUrl, exchangeBaseUrl: tipRecord.exchangeBaseUrl,
merchantBaseUrl: tipRecord.merchantBaseUrl, merchantBaseUrl: tipRecord.merchantBaseUrl,
expirationTimestamp: tipRecord.rewardExpiration, expirationTimestamp: timestampProtocolFromDb(tipRecord.rewardExpiration),
rewardAmountEffective: Amounts.stringify(tipRecord.rewardAmountEffective), rewardAmountEffective: Amounts.stringify(tipRecord.rewardAmountEffective),
walletRewardId: tipRecord.walletRewardId, walletRewardId: tipRecord.walletRewardId,
transactionId, transactionId,

View File

@ -20,6 +20,7 @@
import { import {
AbsoluteTime, AbsoluteTime,
Amounts, Amounts,
DepositTransactionTrackingState,
j2s, j2s,
Logger, Logger,
NotificationType, NotificationType,
@ -69,6 +70,7 @@ import {
GetReadOnlyAccess, GetReadOnlyAccess,
timestampOptionalPreciseFromDb, timestampOptionalPreciseFromDb,
timestampPreciseFromDb, timestampPreciseFromDb,
timestampProtocolFromDb,
WalletStoresV1, WalletStoresV1,
} from "../index.js"; } from "../index.js";
import { InternalWalletState } from "../internal-wallet-state.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 { return {
type: TransactionType.Deposit, type: TransactionType.Deposit,
txState: computeDepositTransactionStatus(dg), txState: computeDepositTransactionStatus(dg),
@ -817,7 +830,7 @@ function buildTransactionForDeposit(
amountEffective: Amounts.stringify(dg.totalPayCost), amountEffective: Amounts.stringify(dg.totalPayCost),
timestamp: timestampPreciseFromDb(dg.timestampCreated), timestamp: timestampPreciseFromDb(dg.timestampCreated),
targetPaytoUri: dg.wire.payto_uri, targetPaytoUri: dg.wire.payto_uri,
wireTransferDeadline: dg.wireTransferDeadline, wireTransferDeadline: timestampProtocolFromDb(dg.wireTransferDeadline),
transactionId: constructTransactionIdentifier({ transactionId: constructTransactionIdentifier({
tag: TransactionType.Deposit, tag: TransactionType.Deposit,
depositGroupId: dg.depositGroupId, depositGroupId: dg.depositGroupId,
@ -830,7 +843,7 @@ function buildTransactionForDeposit(
)) / )) /
dg.statusPerCoin.length, dg.statusPerCoin.length,
depositGroupId: dg.depositGroupId, depositGroupId: dg.depositGroupId,
trackingState: Object.values(dg.trackingState ?? {}), trackingState,
deposited, deposited,
...(ort?.lastError ? { error: ort.lastError } : {}), ...(ort?.lastError ? { error: ort.lastError } : {}),
}; };

View File

@ -16,7 +16,11 @@
import { Amounts, DenomKeyType } from "@gnu-taler/taler-util"; import { Amounts, DenomKeyType } from "@gnu-taler/taler-util";
import test from "ava"; import test from "ava";
import { DenominationRecord, DenominationVerificationStatus } from "../db.js"; import {
DenominationRecord,
DenominationVerificationStatus,
timestampProtocolToDb,
} from "../db.js";
import { selectWithdrawalDenominations } from "../util/coinSelection.js"; import { selectWithdrawalDenominations } from "../util/coinSelection.js";
test("withdrawal selection bug repro", (t) => { test("withdrawal selection bug repro", (t) => {
@ -64,22 +68,22 @@ test("withdrawal selection bug repro", (t) => {
isRevoked: false, isRevoked: false,
masterSig: masterSig:
"4F0P456CNNTTWK8BFJHGM3JTD6FVVNZY8EP077GYAHDJ5Y81S5RQ3SMS925NXMDVG9A88JAAP0E2GDZBC21PP5NHFFVWHAW3AVT8J3R", "4F0P456CNNTTWK8BFJHGM3JTD6FVVNZY8EP077GYAHDJ5Y81S5RQ3SMS925NXMDVG9A88JAAP0E2GDZBC21PP5NHFFVWHAW3AVT8J3R",
stampExpireDeposit: { stampExpireDeposit: timestampProtocolToDb({
t_s: 1742909388, t_s: 1742909388,
}, }),
stampExpireLegal: { stampExpireLegal: timestampProtocolToDb({
t_s: 1900589388, t_s: 1900589388,
}, }),
stampExpireWithdraw: { stampExpireWithdraw: timestampProtocolToDb({
t_s: 1679837388, t_s: 1679837388,
}, }),
stampStart: { stampStart: timestampProtocolToDb({
t_s: 1585229388, t_s: 1585229388,
}, }),
verificationStatus: DenominationVerificationStatus.Unverified, verificationStatus: DenominationVerificationStatus.Unverified,
currency: "KUDOS", currency: "KUDOS",
value: "KUDOS:1000", value: "KUDOS:1000",
listIssueDate: { t_s: 0 }, listIssueDate: timestampProtocolToDb({ t_s: 0 }),
}, },
{ {
denomPub: { denomPub: {
@ -119,22 +123,22 @@ test("withdrawal selection bug repro", (t) => {
isRevoked: false, isRevoked: false,
masterSig: masterSig:
"P99AW82W46MZ0AKW7Z58VQPXFNTJQM9DVTYPBDF6KVYF38PPVDAZTV7JQ8TY7HGEC7JJJAY4E7AY7J3W1WV10DAZZQHHKTAVTSRAC20", "P99AW82W46MZ0AKW7Z58VQPXFNTJQM9DVTYPBDF6KVYF38PPVDAZTV7JQ8TY7HGEC7JJJAY4E7AY7J3W1WV10DAZZQHHKTAVTSRAC20",
stampExpireDeposit: { stampExpireDeposit: timestampProtocolToDb({
t_s: 1742909388, t_s: 1742909388,
}, }),
stampExpireLegal: { stampExpireLegal: timestampProtocolToDb({
t_s: 1900589388, t_s: 1900589388,
}, }),
stampExpireWithdraw: { stampExpireWithdraw: timestampProtocolToDb({
t_s: 1679837388, t_s: 1679837388,
}, }),
stampStart: { stampStart: timestampProtocolToDb({
t_s: 1585229388, t_s: 1585229388,
}, }),
verificationStatus: DenominationVerificationStatus.Unverified, verificationStatus: DenominationVerificationStatus.Unverified,
value: "KUDOS:10", value: "KUDOS:10",
currency: "KUDOS", currency: "KUDOS",
listIssueDate: { t_s: 0 }, listIssueDate: timestampProtocolToDb({ t_s: 0 }),
}, },
{ {
denomPub: { denomPub: {
@ -173,22 +177,22 @@ test("withdrawal selection bug repro", (t) => {
isRevoked: false, isRevoked: false,
masterSig: masterSig:
"8S4VZGHE5WE0N5ZVCHYW9KZZR4YAKK15S46MV1HR1QB9AAMH3NWPW4DCR4NYGJK33Q8YNFY80SWNS6XKAP5DEVK933TM894FJ2VGE3G", "8S4VZGHE5WE0N5ZVCHYW9KZZR4YAKK15S46MV1HR1QB9AAMH3NWPW4DCR4NYGJK33Q8YNFY80SWNS6XKAP5DEVK933TM894FJ2VGE3G",
stampExpireDeposit: { stampExpireDeposit: timestampProtocolToDb({
t_s: 1742909388, t_s: 1742909388,
}, }),
stampExpireLegal: { stampExpireLegal: timestampProtocolToDb({
t_s: 1900589388, t_s: 1900589388,
}, }),
stampExpireWithdraw: { stampExpireWithdraw: timestampProtocolToDb({
t_s: 1679837388, t_s: 1679837388,
}, }),
stampStart: { stampStart: timestampProtocolToDb({
t_s: 1585229388, t_s: 1585229388,
}, }),
verificationStatus: DenominationVerificationStatus.Unverified, verificationStatus: DenominationVerificationStatus.Unverified,
value: "KUDOS:5", value: "KUDOS:5",
currency: "KUDOS", currency: "KUDOS",
listIssueDate: { t_s: 0 }, listIssueDate: timestampProtocolToDb({ t_s: 0 }),
}, },
{ {
denomPub: { denomPub: {
@ -228,22 +232,22 @@ test("withdrawal selection bug repro", (t) => {
isRevoked: false, isRevoked: false,
masterSig: masterSig:
"E3AWGAG8VB42P3KXM8B04Z6M483SX59R3Y4T53C3NXCA2NPB6C7HVCMVX05DC6S58E9X40NGEBQNYXKYMYCF3ASY2C4WP1WCZ4ME610", "E3AWGAG8VB42P3KXM8B04Z6M483SX59R3Y4T53C3NXCA2NPB6C7HVCMVX05DC6S58E9X40NGEBQNYXKYMYCF3ASY2C4WP1WCZ4ME610",
stampExpireDeposit: { stampExpireDeposit: timestampProtocolToDb({
t_s: 1742909388, t_s: 1742909388,
}, }),
stampExpireLegal: { stampExpireLegal: timestampProtocolToDb({
t_s: 1900589388, t_s: 1900589388,
}, }),
stampExpireWithdraw: { stampExpireWithdraw: timestampProtocolToDb({
t_s: 1679837388, t_s: 1679837388,
}, }),
stampStart: { stampStart: timestampProtocolToDb({
t_s: 1585229388, t_s: 1585229388,
}, }),
verificationStatus: DenominationVerificationStatus.Unverified, verificationStatus: DenominationVerificationStatus.Unverified,
value: "KUDOS:1", value: "KUDOS:1",
currency: "KUDOS", currency: "KUDOS",
listIssueDate: { t_s: 0 }, listIssueDate: timestampProtocolToDb({ t_s: 0 }),
}, },
{ {
denomPub: { denomPub: {
@ -282,18 +286,18 @@ test("withdrawal selection bug repro", (t) => {
isRevoked: false, isRevoked: false,
masterSig: masterSig:
"0ES1RKV002XB4YP21SN0QB7RSDHGYT0XAE65JYN8AVJAA6H7JZFN7JADXT521DJS89XMGPZGR8GCXF1516Y0Q9QDV00E6NMFA6CF838", "0ES1RKV002XB4YP21SN0QB7RSDHGYT0XAE65JYN8AVJAA6H7JZFN7JADXT521DJS89XMGPZGR8GCXF1516Y0Q9QDV00E6NMFA6CF838",
stampExpireDeposit: { stampExpireDeposit: timestampProtocolToDb({
t_s: 1742909388, t_s: 1742909388,
}, }),
stampExpireLegal: { stampExpireLegal: timestampProtocolToDb({
t_s: 1900589388, t_s: 1900589388,
}, }),
stampExpireWithdraw: { stampExpireWithdraw: timestampProtocolToDb({
t_s: 1679837388, t_s: 1679837388,
}, }),
stampStart: { stampStart: timestampProtocolToDb({
t_s: 1585229388, t_s: 1585229388,
}, }),
verificationStatus: DenominationVerificationStatus.Unverified, verificationStatus: DenominationVerificationStatus.Unverified,
value: Amounts.stringify({ value: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
@ -301,7 +305,7 @@ test("withdrawal selection bug repro", (t) => {
value: 0, value: 0,
}), }),
currency: "KUDOS", currency: "KUDOS",
listIssueDate: { t_s: 0 }, listIssueDate: timestampProtocolToDb({ t_s: 0 }),
}, },
{ {
denomPub: { denomPub: {
@ -340,22 +344,22 @@ test("withdrawal selection bug repro", (t) => {
isRevoked: false, isRevoked: false,
masterSig: masterSig:
"58QEB6C6N7602E572E3JYANVVJ9BRW0V9E2ZFDW940N47YVQDK9SAFPWBN5YGT3G1742AFKQ0CYR4DM2VWV0Z0T1XMEKWN6X2EZ9M0R", "58QEB6C6N7602E572E3JYANVVJ9BRW0V9E2ZFDW940N47YVQDK9SAFPWBN5YGT3G1742AFKQ0CYR4DM2VWV0Z0T1XMEKWN6X2EZ9M0R",
stampExpireDeposit: { stampExpireDeposit: timestampProtocolToDb({
t_s: 1742909388, t_s: 1742909388,
}, }),
stampExpireLegal: { stampExpireLegal: timestampProtocolToDb({
t_s: 1900589388, t_s: 1900589388,
}, }),
stampExpireWithdraw: { stampExpireWithdraw: timestampProtocolToDb({
t_s: 1679837388, t_s: 1679837388,
}, }),
stampStart: { stampStart: timestampProtocolToDb({
t_s: 1585229388, t_s: 1585229388,
}, }),
verificationStatus: DenominationVerificationStatus.Unverified, verificationStatus: DenominationVerificationStatus.Unverified,
value: "KUDOS:2", value: "KUDOS:2",
currency: "KUDOS", currency: "KUDOS",
listIssueDate: { t_s: 0 }, listIssueDate: timestampProtocolToDb({ t_s: 0 }),
}, },
]; ];

View File

@ -25,7 +25,7 @@
* Imports. * Imports.
*/ */
import { TalerErrorDetail, AbsoluteTime } from "@gnu-taler/taler-util"; import { TalerErrorDetail, AbsoluteTime } from "@gnu-taler/taler-util";
import { RetryInfo } from "./operations/common.js"; import { DbRetryInfo } from "./operations/common.js";
export enum PendingTaskType { export enum PendingTaskType {
ExchangeUpdate = "exchange-update", ExchangeUpdate = "exchange-update",
@ -137,7 +137,7 @@ export interface PendingRefreshTask {
lastError?: TalerErrorDetail; lastError?: TalerErrorDetail;
refreshGroupId: string; refreshGroupId: string;
finishedPerCoin: boolean[]; finishedPerCoin: boolean[];
retryInfo?: RetryInfo; retryInfo?: DbRetryInfo;
} }
/** /**
@ -156,7 +156,7 @@ export interface PendingTipPickupTask {
export interface PendingPurchaseTask { export interface PendingPurchaseTask {
type: PendingTaskType.Purchase; type: PendingTaskType.Purchase;
proposalId: string; proposalId: string;
retryInfo?: RetryInfo; retryInfo?: DbRetryInfo;
/** /**
* Status of the payment as string, used only for debugging. * Status of the payment as string, used only for debugging.
*/ */
@ -167,7 +167,7 @@ export interface PendingPurchaseTask {
export interface PendingRecoupTask { export interface PendingRecoupTask {
type: PendingTaskType.Recoup; type: PendingTaskType.Recoup;
recoupGroupId: string; recoupGroupId: string;
retryInfo?: RetryInfo; retryInfo?: DbRetryInfo;
lastError: TalerErrorDetail | undefined; lastError: TalerErrorDetail | undefined;
} }
@ -177,7 +177,7 @@ export interface PendingRecoupTask {
export interface PendingWithdrawTask { export interface PendingWithdrawTask {
type: PendingTaskType.Withdraw; type: PendingTaskType.Withdraw;
lastError: TalerErrorDetail | undefined; lastError: TalerErrorDetail | undefined;
retryInfo?: RetryInfo; retryInfo?: DbRetryInfo;
withdrawalGroupId: string; withdrawalGroupId: string;
} }
@ -187,7 +187,7 @@ export interface PendingWithdrawTask {
export interface PendingDepositTask { export interface PendingDepositTask {
type: PendingTaskType.Deposit; type: PendingTaskType.Deposit;
lastError: TalerErrorDetail | undefined; lastError: TalerErrorDetail | undefined;
retryInfo: RetryInfo | undefined; retryInfo: DbRetryInfo | undefined;
depositGroupId: string; depositGroupId: string;
} }
@ -233,7 +233,7 @@ export interface PendingTaskInfoCommon {
* Retry info. Currently used to stop the wallet after any operation * Retry info. Currently used to stop the wallet after any operation
* exceeds a number of retries. * exceeds a number of retries.
*/ */
retryInfo?: RetryInfo; retryInfo?: DbRetryInfo;
} }
/** /**

View File

@ -26,10 +26,9 @@ import {
FeeDescriptionPair, FeeDescriptionPair,
TalerProtocolTimestamp, TalerProtocolTimestamp,
TimePoint, TimePoint,
WireFee,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { DenominationRecord } from "../db.js"; 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: * Given a list of denominations with the same value and same period of time:
@ -457,9 +456,11 @@ export function isWithdrawableDenom(
denomselAllowLate?: boolean, denomselAllowLate?: boolean,
): boolean { ): boolean {
const now = AbsoluteTime.now(); const now = AbsoluteTime.now();
const start = AbsoluteTime.fromProtocolTimestamp(d.stampStart); const start = AbsoluteTime.fromProtocolTimestamp(
timestampProtocolFromDb(d.stampStart),
);
const withdrawExpire = AbsoluteTime.fromProtocolTimestamp( const withdrawExpire = AbsoluteTime.fromProtocolTimestamp(
d.stampExpireWithdraw, timestampProtocolFromDb(d.stampExpireWithdraw),
); );
const started = AbsoluteTime.cmp(now, start) >= 0; const started = AbsoluteTime.cmp(now, start) >= 0;
let lastPossibleWithdraw: AbsoluteTime; let lastPossibleWithdraw: AbsoluteTime;

View File

@ -14,6 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { GlobalIDB } from "@gnu-taler/idb-bridge";
import { import {
AbsoluteTime, AbsoluteTime,
AgeRestriction, AgeRestriction,
@ -29,14 +30,14 @@ import {
parsePaytoUri, parsePaytoUri,
strcmp, strcmp,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { checkDbInvariant } from "./invariants.js";
import { import {
DenominationRecord, DenominationRecord,
InternalWalletState, InternalWalletState,
getExchangeDetails, getExchangeDetails,
timestampProtocolFromDb,
} from "../index.js"; } from "../index.js";
import { CoinInfo } from "./coinSelection.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 * If the operation going to be plan subtracts
@ -224,10 +225,10 @@ async function getAvailableDenoms(
); );
for (const denom of ds) { for (const denom of ds) {
const expiresWithdraw = AbsoluteTime.fromProtocolTimestamp( const expiresWithdraw = AbsoluteTime.fromProtocolTimestamp(
denom.stampExpireWithdraw, timestampProtocolFromDb(denom.stampExpireWithdraw),
); );
const expiresDeposit = AbsoluteTime.fromProtocolTimestamp( const expiresDeposit = AbsoluteTime.fromProtocolTimestamp(
denom.stampExpireDeposit, timestampProtocolFromDb(denom.stampExpireDeposit),
); );
creditDeadline = AbsoluteTime.min(deadline, expiresWithdraw); creditDeadline = AbsoluteTime.min(deadline, expiresWithdraw);
debitDeadline = AbsoluteTime.min(deadline, expiresDeposit); debitDeadline = AbsoluteTime.min(deadline, expiresDeposit);
@ -270,10 +271,10 @@ async function getAvailableDenoms(
continue; continue;
} }
const expiresWithdraw = AbsoluteTime.fromProtocolTimestamp( const expiresWithdraw = AbsoluteTime.fromProtocolTimestamp(
denom.stampExpireWithdraw, timestampProtocolFromDb(denom.stampExpireWithdraw),
); );
const expiresDeposit = AbsoluteTime.fromProtocolTimestamp( const expiresDeposit = AbsoluteTime.fromProtocolTimestamp(
denom.stampExpireDeposit, timestampProtocolFromDb(denom.stampExpireDeposit),
); );
creditDeadline = AbsoluteTime.min(deadline, expiresWithdraw); creditDeadline = AbsoluteTime.min(deadline, expiresWithdraw);
debitDeadline = AbsoluteTime.min(deadline, expiresDeposit); debitDeadline = AbsoluteTime.min(deadline, expiresDeposit);
@ -318,7 +319,9 @@ function buildCoinInfoFromDenom(
exchangeBaseUrl: denom.exchangeBaseUrl, exchangeBaseUrl: denom.exchangeBaseUrl,
duration: AbsoluteTime.difference( duration: AbsoluteTime.difference(
AbsoluteTime.now(), AbsoluteTime.now(),
AbsoluteTime.fromProtocolTimestamp(denom.stampExpireDeposit), AbsoluteTime.fromProtocolTimestamp(
timestampProtocolFromDb(denom.stampExpireDeposit),
),
), ),
totalAvailable: total, totalAvailable: total,
value: Amounts.parseOrThrow(denom.value), value: Amounts.parseOrThrow(denom.value),