diff options
Diffstat (limited to 'packages/taler-wallet-core')
26 files changed, 388 insertions, 377 deletions
diff --git a/packages/taler-wallet-core/src/bank-api-client.ts b/packages/taler-wallet-core/src/bank-api-client.ts index a61ea2eef..128e9a7a7 100644 --- a/packages/taler-wallet-core/src/bank-api-client.ts +++ b/packages/taler-wallet-core/src/bank-api-client.ts @@ -25,6 +25,7 @@ import {    AmountString,    buildCodecForObject,    Codec, +  codecForAny,    codecForString,    encodeCrock,    getRandomBytes, @@ -102,15 +103,16 @@ export namespace BankApi {      const resp = await bank.http.postJson(url.href, { username, password });      let paytoUri = `payto://x-taler-bank/localhost/${username}`;      if (resp.status !== 200 && resp.status !== 202) { -      logger.error(`${j2s(await resp.json())}`) +      logger.error(`${j2s(await resp.json())}`);        throw new Error();      } +    const respJson = await readSuccessResponseJsonOrThrow(resp, codecForAny()); +    // LibEuFin demobank returns payto URI in response +    if (respJson.paytoUri) { +      paytoUri = respJson.paytoUri; +    }      try {        const respJson = await resp.json(); -      // LibEuFin demobank returns payto URI in response -      if (respJson.paytoUri) { -        paytoUri = respJson.paytoUri; -      }      } catch (e) {}      return {        password, diff --git a/packages/taler-wallet-core/src/common.ts b/packages/taler-wallet-core/src/common.ts index 957ba1ca1..d37bbe6eb 100644 --- a/packages/taler-wallet-core/src/common.ts +++ b/packages/taler-wallet-core/src/common.ts @@ -34,7 +34,8 @@ import {    BalancesResponse,    AmountJson,    DenominationPubKey, -  Timestamp, +  AbsoluteTime, +  TalerProtocolTimestamp,  } from "@gnu-taler/taler-util";  import { CryptoApi } from "./crypto/workers/cryptoApi.js";  import { ExchangeDetailsRecord, ExchangeRecord, WalletStoresV1 } from "./db.js"; @@ -165,22 +166,22 @@ export interface DenomInfo {    /**     * Validity start date of the denomination.     */ -  stampStart: Timestamp; +  stampStart: TalerProtocolTimestamp;    /**     * Date after which the currency can't be withdrawn anymore.     */ -  stampExpireWithdraw: Timestamp; +  stampExpireWithdraw: TalerProtocolTimestamp;    /**     * Date after the denomination officially doesn't exist anymore.     */ -  stampExpireLegal: Timestamp; +  stampExpireLegal: TalerProtocolTimestamp;    /**     * Data after which coins of this denomination can't be deposited anymore.     */ -  stampExpireDeposit: Timestamp; +  stampExpireDeposit: TalerProtocolTimestamp;  }  export type NotificationListener = (n: WalletNotification) => void; diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts index 820397346..d91aa08a2 100644 --- a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts +++ b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts @@ -207,7 +207,7 @@ export class CryptoApi {        }      };      ws.terminationTimerHandle = timer.after(15 * 1000, destroy); -    ws.terminationTimerHandle.unref(); +    //ws.terminationTimerHandle.unref();    }    handleWorkerError(ws: WorkerState, e: any): void { diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts index b51d499d5..b27067885 100644 --- a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts +++ b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts @@ -67,13 +67,11 @@ import {    setupWithdrawPlanchet,    stringToBytes,    TalerSignaturePurpose, -  Timestamp, -  timestampTruncateToSecond, -  typedArrayConcat, +  AbsoluteTime,    BlindedDenominationSignature, -  RsaUnblindedSignature,    UnblindedSignature,    PlanchetUnblindInfo, +  TalerProtocolTimestamp,  } from "@gnu-taler/taler-util";  import bigint from "big-integer";  import { DenominationRecord, WireFee } from "../../db.js"; @@ -110,18 +108,16 @@ function amountToBuffer(amount: AmountJson): Uint8Array {    return u8buf;  } -function timestampRoundedToBuffer(ts: Timestamp): Uint8Array { +function timestampRoundedToBuffer(ts: TalerProtocolTimestamp): Uint8Array {    const b = new ArrayBuffer(8);    const v = new DataView(b); -  const tsRounded = timestampTruncateToSecond(ts); +  // The buffer we sign over represents the timestamp in microseconds.    if (typeof v.setBigUint64 !== "undefined") { -    const s = BigInt(tsRounded.t_ms) * BigInt(1000); +    const s = BigInt(ts.t_s) * BigInt(1000 * 1000);      v.setBigUint64(0, s);    } else {      const s = -      tsRounded.t_ms === "never" -        ? bigint.zero -        : bigint(tsRounded.t_ms).times(1000); +      ts.t_s === "never" ? bigint.zero : bigint(ts.t_s).multiply(1000 * 1000);      const arr = s.toArray(2 ** 8).value;      let offset = 8 - arr.length;      for (let i = 0; i < arr.length; i++) { diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 2e76ab523..e9fe6a47b 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -36,9 +36,10 @@ import {    Product,    RefreshReason,    TalerErrorDetails, -  Timestamp,    UnblindedSignature,    CoinEnvelope, +  TalerProtocolTimestamp, +  TalerProtocolDuration,  } from "@gnu-taler/taler-util";  import { RetryInfo } from "./util/retries.js";  import { PayCoinSelection } from "./util/coinSelection.js"; @@ -152,7 +153,7 @@ export interface ReserveRecord {    /**     * Time when the reserve was created.     */ -  timestampCreated: Timestamp; +  timestampCreated: TalerProtocolTimestamp;    /**     * Time when the information about this reserve was posted to the bank. @@ -161,14 +162,14 @@ export interface ReserveRecord {     *     * Set to undefined if that hasn't happened yet.     */ -  timestampReserveInfoPosted: Timestamp | undefined; +  timestampReserveInfoPosted: TalerProtocolTimestamp | undefined;    /**     * Time when the reserve was confirmed by the bank.     *     * Set to undefined if not confirmed yet.     */ -  timestampBankConfirmed: Timestamp | undefined; +  timestampBankConfirmed: TalerProtocolTimestamp | undefined;    /**     * Wire information (as payto URI) for the bank account that @@ -218,11 +219,6 @@ export interface ReserveRecord {    operationStatus: OperationStatus;    /** -   * Time of the last successful status query. -   */ -  lastSuccessfulStatusQuery: Timestamp | undefined; - -  /**     * Retry info, in case the reserve needs to be processed again     * later, either due to an error or because the wallet needs to     * wait for something. @@ -350,22 +346,22 @@ export interface DenominationRecord {    /**     * Validity start date of the denomination.     */ -  stampStart: Timestamp; +  stampStart: TalerProtocolTimestamp;    /**     * Date after which the currency can't be withdrawn anymore.     */ -  stampExpireWithdraw: Timestamp; +  stampExpireWithdraw: TalerProtocolTimestamp;    /**     * Date after the denomination officially doesn't exist anymore.     */ -  stampExpireLegal: Timestamp; +  stampExpireLegal: TalerProtocolTimestamp;    /**     * Data after which coins of this denomination can't be deposited anymore.     */ -  stampExpireDeposit: Timestamp; +  stampExpireDeposit: TalerProtocolTimestamp;    /**     * Signature by the exchange's master key over the denomination @@ -407,7 +403,7 @@ export interface DenominationRecord {     * Latest list issue date of the "/keys" response     * that includes this denomination.     */ -  listIssueDate: Timestamp; +  listIssueDate: TalerProtocolTimestamp;  }  /** @@ -441,7 +437,7 @@ export interface ExchangeDetailsRecord {     */    protocolVersion: string; -  reserveClosingDelay: Duration; +  reserveClosingDelay: TalerProtocolDuration;    /**     * Signing keys we got from the exchange, can also contain @@ -478,7 +474,7 @@ export interface ExchangeDetailsRecord {     *     * Used during backup merging.     */ -  termsOfServiceAcceptedTimestamp: Timestamp | undefined; +  termsOfServiceAcceptedTimestamp: TalerProtocolTimestamp | undefined;    wireInfo: WireInfo;  } @@ -503,7 +499,7 @@ export interface ExchangeDetailsPointer {     * Timestamp when the (masterPublicKey, currency) pointer     * has been updated.     */ -  updateClock: Timestamp; +  updateClock: TalerProtocolTimestamp;  }  /** @@ -528,14 +524,14 @@ export interface ExchangeRecord {    /**     * Last time when the exchange was updated.     */ -  lastUpdate: Timestamp | undefined; +  lastUpdate: TalerProtocolTimestamp | undefined;    /**     * Next scheduled update for the exchange.     *     * (This field must always be present, so we can index on the timestamp.)     */ -  nextUpdate: Timestamp; +  nextUpdate: TalerProtocolTimestamp;    /**     * Next time that we should check if coins need to be refreshed. @@ -543,7 +539,7 @@ export interface ExchangeRecord {     * Updated whenever the exchange's denominations are updated or when     * the refresh check has been done.     */ -  nextRefreshCheck: Timestamp; +  nextRefreshCheck: TalerProtocolTimestamp;    /**     * Last error (if any) for fetching updated information about the @@ -793,7 +789,7 @@ export interface ProposalRecord {     * Timestamp (in ms) of when the record     * was created.     */ -  timestamp: Timestamp; +  timestamp: TalerProtocolTimestamp;    /**     * Private key for the nonce. @@ -837,7 +833,7 @@ export interface TipRecord {     * Has the user accepted the tip?  Only after the tip has been accepted coins     * withdrawn from the tip may be used.     */ -  acceptedTimestamp: Timestamp | undefined; +  acceptedTimestamp: TalerProtocolTimestamp | undefined;    /**     * The tipped amount. @@ -849,7 +845,7 @@ export interface TipRecord {    /**     * Timestamp, the tip can't be picked up anymore after this deadline.     */ -  tipExpiration: Timestamp; +  tipExpiration: TalerProtocolTimestamp;    /**     * The exchange that will sign our coins, chosen by the merchant. @@ -884,13 +880,13 @@ export interface TipRecord {     */    merchantTipId: string; -  createdTimestamp: Timestamp; +  createdTimestamp: TalerProtocolTimestamp;    /**     * Timestamp for when the wallet finished picking up the tip     * from the merchant.     */ -  pickedUpTimestamp: Timestamp | undefined; +  pickedUpTimestamp: TalerProtocolTimestamp | undefined;    /**     * Retry info, even present when the operation isn't active to allow indexing @@ -959,12 +955,12 @@ export interface RefreshGroupRecord {     */    statusPerCoin: RefreshCoinStatus[]; -  timestampCreated: Timestamp; +  timestampCreated: TalerProtocolTimestamp;    /**     * Timestamp when the refresh session finished.     */ -  timestampFinished: Timestamp | undefined; +  timestampFinished: TalerProtocolTimestamp | undefined;    /**     * No coins are pending, but at least one is frozen. @@ -1025,12 +1021,12 @@ export interface WireFee {    /**     * Start date of the fee.     */ -  startStamp: Timestamp; +  startStamp: TalerProtocolTimestamp;    /**     * End date of the fee.     */ -  endStamp: Timestamp; +  endStamp: TalerProtocolTimestamp;    /**     * Signature made by the exchange master key. @@ -1054,12 +1050,12 @@ export type WalletRefundItem =  export interface WalletRefundItemCommon {    // Execution time as claimed by the merchant -  executionTime: Timestamp; +  executionTime: TalerProtocolTimestamp;    /**     * Time when the wallet became aware of the refund.     */ -  obtainedTime: Timestamp; +  obtainedTime: TalerProtocolTimestamp;    refundAmount: AmountJson; @@ -1141,14 +1137,14 @@ export interface WalletContractData {    orderId: string;    merchantBaseUrl: string;    summary: string; -  autoRefund: Duration | undefined; +  autoRefund: TalerProtocolDuration | undefined;    maxWireFee: AmountJson;    wireFeeAmortization: number; -  payDeadline: Timestamp; -  refundDeadline: Timestamp; +  payDeadline: TalerProtocolTimestamp; +  refundDeadline: TalerProtocolTimestamp;    allowedAuditors: AllowedAuditorInfo[];    allowedExchanges: AllowedExchangeInfo[]; -  timestamp: Timestamp; +  timestamp: TalerProtocolTimestamp;    wireMethod: string;    wireInfoHash: string;    maxDepositFee: AmountJson; @@ -1215,8 +1211,10 @@ export interface PurchaseRecord {    /**     * Timestamp of the first time that sending a payment to the merchant     * for this purchase was successful. +   * +   * FIXME: Does this need to be a timestamp, doensn't boolean suffice?     */ -  timestampFirstSuccessfulPay: Timestamp | undefined; +  timestampFirstSuccessfulPay: TalerProtocolTimestamp | undefined;    merchantPaySig: string | undefined; @@ -1224,7 +1222,7 @@ export interface PurchaseRecord {     * When was the purchase made?     * Refers to the time that the user accepted.     */ -  timestampAccept: Timestamp; +  timestampAccept: TalerProtocolTimestamp;    /**     * Pending refunds for the purchase.  A refund is pending @@ -1236,7 +1234,7 @@ export interface PurchaseRecord {     * When was the last refund made?     * Set to 0 if no refund was made on the purchase.     */ -  timestampLastRefundStatus: Timestamp | undefined; +  timestampLastRefundStatus: TalerProtocolTimestamp | undefined;    /**     * Last session signature that we submitted to /pay (if any). @@ -1273,7 +1271,7 @@ export interface PurchaseRecord {    /**     * Continue querying the refund status until this deadline has expired.     */ -  autoRefundDeadline: Timestamp | undefined; +  autoRefundDeadline: TalerProtocolTimestamp | undefined;    /**     * Is the payment frozen?  I.e. did we encounter @@ -1308,12 +1306,12 @@ export interface WalletBackupConfState {    /**     * Timestamp stored in the last backup.     */ -  lastBackupTimestamp?: Timestamp; +  lastBackupTimestamp?: TalerProtocolTimestamp;    /**     * Last time we tried to do a backup.     */ -  lastBackupCheckTimestamp?: Timestamp; +  lastBackupCheckTimestamp?: TalerProtocolTimestamp;    lastBackupNonce?: string;  } @@ -1362,14 +1360,14 @@ export interface WithdrawalGroupRecord {     * When was the withdrawal operation started started?     * Timestamp in milliseconds.     */ -  timestampStart: Timestamp; +  timestampStart: TalerProtocolTimestamp;    /**     * When was the withdrawal operation completed?     *     * FIXME: We should probably drop this and introduce an OperationStatus field.     */ -  timestampFinish?: Timestamp; +  timestampFinish?: TalerProtocolTimestamp;    /**     * Operation status of the withdrawal group. @@ -1429,9 +1427,9 @@ export interface RecoupGroupRecord {     */    recoupGroupId: string; -  timestampStarted: Timestamp; +  timestampStarted: TalerProtocolTimestamp; -  timestampFinished: Timestamp | undefined; +  timestampFinished: TalerProtocolTimestamp | undefined;    /**     * Public keys that identify the coins being recouped @@ -1482,7 +1480,7 @@ export type BackupProviderState =      }    | {        tag: BackupProviderStateTag.Ready; -      nextBackupTimestamp: Timestamp; +      nextBackupTimestamp: TalerProtocolTimestamp;      }    | {        tag: BackupProviderStateTag.Retrying; @@ -1529,7 +1527,7 @@ export interface BackupProviderRecord {     * Does NOT correspond to the timestamp of the backup,     * which only changes when the backup content changes.     */ -  lastBackupCycleTimestamp?: Timestamp; +  lastBackupCycleTimestamp?: TalerProtocolTimestamp;    /**     * Proposal that we're currently trying to pay for. @@ -1594,9 +1592,9 @@ export interface DepositGroupRecord {    depositedPerCoin: boolean[]; -  timestampCreated: Timestamp; +  timestampCreated: TalerProtocolTimestamp; -  timestampFinished: Timestamp | undefined; +  timestampFinished: TalerProtocolTimestamp | undefined;    operationStatus: OperationStatus; @@ -1618,14 +1616,14 @@ export interface GhostDepositGroupRecord {     * When multiple deposits for the same contract terms hash     * have a different timestamp, we choose the earliest one.     */ -  timestamp: Timestamp; +  timestamp: TalerProtocolTimestamp;    contractTermsHash: string;    deposits: {      coinPub: string;      amount: AmountString; -    timestamp: Timestamp; +    timestamp: TalerProtocolTimestamp;      depositFee: AmountString;      merchantPub: string;      coinSig: string; diff --git a/packages/taler-wallet-core/src/dbless.ts b/packages/taler-wallet-core/src/dbless.ts index 169b6ae0a..854a3ea09 100644 --- a/packages/taler-wallet-core/src/dbless.ts +++ b/packages/taler-wallet-core/src/dbless.ts @@ -44,7 +44,7 @@ import {    hashWire,    Logger,    parsePaytoUri, -  Timestamp, +  AbsoluteTime,    UnblindedSignature,  } from "@gnu-taler/taler-util";  import { DenominationRecord } from "./db.js"; @@ -222,10 +222,11 @@ export async function depositCoin(args: {    const depositPayto =      args.depositPayto ?? "payto://x-taler-bank/localhost/foo";    const wireSalt = encodeCrock(getRandomBytes(16)); +  const timestampNow = AbsoluteTime.toTimestamp(AbsoluteTime.now());    const contractTermsHash = encodeCrock(getRandomBytes(64)); -  const depositTimestamp = Timestamp.truncateToSecond(Timestamp.now()); -  const refundDeadline = Timestamp.truncateToSecond(Timestamp.now()); -  const wireTransferDeadline = Timestamp.truncateToSecond(Timestamp.now()); +  const depositTimestamp = timestampNow; +  const refundDeadline = timestampNow; +  const wireTransferDeadline = timestampNow;    const merchantPub = encodeCrock(getRandomBytes(32));    const dp = await cryptoApi.signDepositPermission({      coinPriv: coin.coinPriv, diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts b/packages/taler-wallet-core/src/operations/backup/export.ts index 12b309418..35306da63 100644 --- a/packages/taler-wallet-core/src/operations/backup/export.ts +++ b/packages/taler-wallet-core/src/operations/backup/export.ts @@ -49,14 +49,13 @@ import {    BackupWithdrawalGroup,    canonicalizeBaseUrl,    canonicalJson, -  getTimestampNow,    Logger, -  timestampToIsoString,    WalletBackupContentV1,    hash,    encodeCrock,    getRandomBytes,    stringToBytes, +  AbsoluteTime,  } from "@gnu-taler/taler-util";  import { InternalWalletState } from "../../common.js";  import { @@ -455,7 +454,7 @@ export async function exportBackup(          });        }); -      const ts = getTimestampNow(); +      const ts = AbsoluteTime.toTimestamp(AbsoluteTime.now());        if (!bs.lastBackupTimestamp) {          bs.lastBackupTimestamp = ts; @@ -496,9 +495,9 @@ export async function exportBackup(          );          bs.lastBackupNonce = encodeCrock(getRandomBytes(32));          logger.trace( -          `setting timestamp to ${timestampToIsoString(ts)} and nonce to ${ -            bs.lastBackupNonce -          }`, +          `setting timestamp to ${AbsoluteTime.toIsoString( +            AbsoluteTime.fromTimestamp(ts), +          )} and nonce to ${bs.lastBackupNonce}`,          );          await tx.config.put({            key: WALLET_BACKUP_STATE_KEY, diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index 35b62c2e4..4b17a5f33 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -20,7 +20,6 @@ import {    Amounts,    BackupDenomSel,    WalletBackupContentV1, -  getTimestampNow,    BackupCoinSourceType,    BackupProposalStatus,    codecForContractTerms, @@ -28,6 +27,8 @@ import {    RefreshReason,    BackupRefreshReason,    DenomKeyType, +  AbsoluteTime, +  TalerProtocolTimestamp,  } from "@gnu-taler/taler-util";  import {    WalletContractData, @@ -277,8 +278,8 @@ export async function importBackup(            permanent: true,            retryInfo: initRetryInfo(),            lastUpdate: undefined, -          nextUpdate: getTimestampNow(), -          nextRefreshCheck: getTimestampNow(), +          nextUpdate: TalerProtocolTimestamp.now(), +          nextRefreshCheck: TalerProtocolTimestamp.now(),          });        } @@ -465,7 +466,6 @@ export async function importBackup(                senderWire: backupReserve.sender_wire,                retryInfo: initRetryInfo(),                lastError: undefined, -              lastSuccessfulStatusQuery: { t_ms: "never" },                initialWithdrawalGroupId:                  backupReserve.initial_withdrawal_group_id,                initialWithdrawalStarted: @@ -752,7 +752,7 @@ export async function importBackup(              noncePub:                cryptoComp.proposalNoncePrivToPub[backupPurchase.nonce_priv],              lastPayError: undefined, -            autoRefundDeadline: { t_ms: "never" }, +            autoRefundDeadline: TalerProtocolTimestamp.never(),              refundStatusRetryInfo: initRetryInfo(),              lastRefundStatusError: undefined,              timestampAccept: backupPurchase.timestamp_accept, diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts b/packages/taler-wallet-core/src/operations/backup/index.ts index 2a1a774f1..48eea56ad 100644 --- a/packages/taler-wallet-core/src/operations/backup/index.ts +++ b/packages/taler-wallet-core/src/operations/backup/index.ts @@ -40,21 +40,19 @@ import {    ConfirmPayResultType,    DenomKeyType,    durationFromSpec, -  getTimestampNow,    hashDenomPub,    HttpStatusCode,    j2s, -  LibtoolVersion,    Logger,    notEmpty,    PreparePayResultType,    RecoveryLoadRequest,    RecoveryMergeStrategy,    TalerErrorDetails, -  Timestamp, -  timestampAddDuration, +  AbsoluteTime,    URL,    WalletBackupContentV1, +  TalerProtocolTimestamp,  } from "@gnu-taler/taler-util";  import { gunzipSync, gzipSync } from "fflate";  import { InternalWalletState } from "../../common.js"; @@ -250,11 +248,13 @@ interface BackupForProviderArgs {    retryAfterPayment: boolean;  } -function getNextBackupTimestamp(): Timestamp { +function getNextBackupTimestamp(): TalerProtocolTimestamp {    // FIXME:  Randomize! -  return timestampAddDuration( -    getTimestampNow(), -    durationFromSpec({ minutes: 5 }), +  return AbsoluteTime.toTimestamp( +    AbsoluteTime.addDuration( +      AbsoluteTime.now(), +      durationFromSpec({ minutes: 5 }), +    ),    );  } @@ -324,7 +324,7 @@ async function runBackupCycleForProvider(          if (!prov) {            return;          } -        prov.lastBackupCycleTimestamp = getTimestampNow(); +        prov.lastBackupCycleTimestamp = TalerProtocolTimestamp.now();          prov.state = {            tag: BackupProviderStateTag.Ready,            nextBackupTimestamp: getNextBackupTimestamp(), @@ -404,7 +404,7 @@ async function runBackupCycleForProvider(            return;          }          prov.lastBackupHash = encodeCrock(currentBackupHash); -        prov.lastBackupCycleTimestamp = getTimestampNow(); +        prov.lastBackupCycleTimestamp = TalerProtocolTimestamp.now();          prov.state = {            tag: BackupProviderStateTag.Ready,            nextBackupTimestamp: getNextBackupTimestamp(), @@ -641,7 +641,7 @@ export async function addBackupProvider(          if (req.activate) {            oldProv.state = {              tag: BackupProviderStateTag.Ready, -            nextBackupTimestamp: getTimestampNow(), +            nextBackupTimestamp: TalerProtocolTimestamp.now(),            };            logger.info("setting existing backup provider to active");            await tx.backupProviders.put(oldProv); @@ -662,7 +662,7 @@ export async function addBackupProvider(        if (req.activate) {          state = {            tag: BackupProviderStateTag.Ready, -          nextBackupTimestamp: getTimestampNow(), +          nextBackupTimestamp: TalerProtocolTimestamp.now(),          };        } else {          state = { @@ -701,8 +701,8 @@ export interface ProviderInfo {     * Last communication issue with the provider.     */    lastError?: TalerErrorDetails; -  lastSuccessfulBackupTimestamp?: Timestamp; -  lastAttemptedBackupTimestamp?: Timestamp; +  lastSuccessfulBackupTimestamp?: TalerProtocolTimestamp; +  lastAttemptedBackupTimestamp?: TalerProtocolTimestamp;    paymentProposalIds: string[];    backupProblem?: BackupProblem;    paymentStatus: ProviderPaymentStatus; @@ -724,7 +724,7 @@ export interface BackupConflictingDeviceProblem {    type: "backup-conflicting-device";    otherDeviceId: string;    myDeviceId: string; -  backupTimestamp: Timestamp; +  backupTimestamp: AbsoluteTime;  }  export type ProviderPaymentStatus = @@ -774,12 +774,12 @@ export interface ProviderPaymentPending {  export interface ProviderPaymentPaid {    type: ProviderPaymentType.Paid; -  paidUntil: Timestamp; +  paidUntil: AbsoluteTime;  }  export interface ProviderPaymentTermsChanged {    type: ProviderPaymentType.TermsChanged; -  paidUntil: Timestamp; +  paidUntil: AbsoluteTime;    oldTerms: BackupProviderTerms;    newTerms: BackupProviderTerms;  } @@ -811,8 +811,8 @@ async function getProviderPaymentInfo(      if (status.paid) {        return {          type: ProviderPaymentType.Paid, -        paidUntil: timestampAddDuration( -          status.contractTerms.timestamp, +        paidUntil: AbsoluteTime.addDuration( +          AbsoluteTime.fromTimestamp(status.contractTerms.timestamp),            durationFromSpec({ years: 1 }),          ),        }; @@ -915,7 +915,7 @@ async function backupRecoveryTheirs(              paymentProposalIds: [],              state: {                tag: BackupProviderStateTag.Ready, -              nextBackupTimestamp: getTimestampNow(), +              nextBackupTimestamp: TalerProtocolTimestamp.now(),              },              uids: [encodeCrock(getRandomBytes(32))],            }); diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts index a5d6c93cf..4b976011b 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -15,6 +15,7 @@   */  import { +  AbsoluteTime,    AmountJson,    Amounts,    buildCodecForObject, @@ -27,21 +28,16 @@ import {    ContractTerms,    CreateDepositGroupRequest,    CreateDepositGroupResponse, -  DenomKeyType,    durationFromSpec,    encodeCrock,    GetFeeForDepositRequest,    getRandomBytes, -  getTimestampNow,    hashWire,    Logger,    NotificationType,    parsePaytoUri,    TalerErrorDetails, -  Timestamp, -  timestampAddDuration, -  timestampIsBetween, -  timestampTruncateToSecond, +  TalerProtocolTimestamp,    TrackDepositGroupRequest,    TrackDepositGroupResponse,    URL, @@ -212,7 +208,7 @@ async function processDepositGroupImpl(          }        }        if (allDeposited) { -        dg.timestampFinished = getTimestampNow(); +        dg.timestampFinished = TalerProtocolTimestamp.now();          dg.operationStatus = OperationStatus.Finished;          delete dg.lastError;          delete dg.retryInfo; @@ -310,13 +306,8 @@ export async function getFeeForDeposit(        }      }); -  const timestamp = getTimestampNow(); -  const timestampRound = timestampTruncateToSecond(timestamp); -  // const noncePair = await ws.cryptoApi.createEddsaKeypair(); -  // const merchantPair = await ws.cryptoApi.createEddsaKeypair(); -  // const wireSalt = encodeCrock(getRandomBytes(16)); -  // const wireHash = hashWire(req.depositPaytoUri, wireSalt); -  // const wireHashLegacy = hashWireLegacy(req.depositPaytoUri, wireSalt); +  const timestamp = AbsoluteTime.now(); +  const timestampRound = AbsoluteTime.toTimestamp(timestamp);    const contractTerms: ContractTerms = {      auditors: [],      exchanges: exchangeInfos, @@ -331,15 +322,14 @@ export async function getFeeForDeposit(      wire_transfer_deadline: timestampRound,      order_id: "",      h_wire: "", -    pay_deadline: timestampAddDuration( -      timestampRound, -      durationFromSpec({ hours: 1 }), +    pay_deadline: AbsoluteTime.toTimestamp( +      AbsoluteTime.addDuration(timestamp, durationFromSpec({ hours: 1 })),      ),      merchant: {        name: "",      },      merchant_pub: "", -    refund_deadline: { t_ms: 0 }, +    refund_deadline: TalerProtocolTimestamp.zero(),    };    const contractData = extractContractData(contractTerms, "", ""); @@ -399,8 +389,8 @@ export async function createDepositGroup(        }      }); -  const timestamp = getTimestampNow(); -  const timestampRound = timestampTruncateToSecond(timestamp); +  const now = AbsoluteTime.now(); +  const nowRounded = AbsoluteTime.toTimestamp(now);    const noncePair = await ws.cryptoApi.createEddsaKeypair();    const merchantPair = await ws.cryptoApi.createEddsaKeypair();    const wireSalt = encodeCrock(getRandomBytes(16)); @@ -412,24 +402,23 @@ export async function createDepositGroup(      max_fee: Amounts.stringify(amount),      max_wire_fee: Amounts.stringify(amount),      wire_method: p.targetType, -    timestamp: timestampRound, +    timestamp: nowRounded,      merchant_base_url: "",      summary: "",      nonce: noncePair.pub, -    wire_transfer_deadline: timestampRound, +    wire_transfer_deadline: nowRounded,      order_id: "",      // This is always the v2 wire hash, as we're the "merchant" and support v2.      h_wire: wireHash,      // Required for older exchanges. -    pay_deadline: timestampAddDuration( -      timestampRound, -      durationFromSpec({ hours: 1 }), +    pay_deadline: AbsoluteTime.toTimestamp( +      AbsoluteTime.addDuration(now, durationFromSpec({ hours: 1 })),      ),      merchant: {        name: "",      },      merchant_pub: merchantPair.pub, -    refund_deadline: { t_ms: 0 }, +    refund_deadline: TalerProtocolTimestamp.zero(),    };    const contractTermsHash = await ws.cryptoApi.hashString( @@ -482,7 +471,7 @@ export async function createDepositGroup(      depositGroupId,      noncePriv: noncePair.priv,      noncePub: noncePair.pub, -    timestampCreated: timestamp, +    timestampCreated: AbsoluteTime.toTimestamp(now),      timestampFinished: undefined,      payCoinSelection: payCoinSel,      payCoinSelectionUid: encodeCrock(getRandomBytes(32)), @@ -570,10 +559,10 @@ export async function getEffectiveDepositAmount(          // about "find method not found on undefined" when the wireType          // is not supported by the Exchange.          const fee = exchangeDetails.wireInfo.feesForType[wireType].find((x) => { -          return timestampIsBetween( -            getTimestampNow(), -            x.startStamp, -            x.endStamp, +          return AbsoluteTime.isBetween( +            AbsoluteTime.now(), +            AbsoluteTime.fromTimestamp(x.startStamp), +            AbsoluteTime.fromTimestamp(x.endStamp),            );          })?.wireFee;          if (fee) { @@ -656,10 +645,10 @@ export async function getTotalFeeForDepositAmount(          // about "find method not found on undefined" when the wireType          // is not supported by the Exchange.          const fee = exchangeDetails.wireInfo.feesForType[wireType].find((x) => { -          return timestampIsBetween( -            getTimestampNow(), -            x.startStamp, -            x.endStamp, +          return AbsoluteTime.isBetween( +            AbsoluteTime.now(), +            AbsoluteTime.fromTimestamp(x.startStamp), +            AbsoluteTime.fromTimestamp(x.endStamp),            );          })?.wireFee;          if (fee) { diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index 536d4e3bf..df7eee76d 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -28,8 +28,6 @@ import {    durationFromSpec,    ExchangeSignKeyJson,    ExchangeWireJson, -  getTimestampNow, -  isTimestampExpired,    Logger,    NotificationType,    parsePaytoUri, @@ -37,13 +35,15 @@ import {    TalerErrorCode,    URL,    TalerErrorDetails, -  Timestamp, +  AbsoluteTime,    hashDenomPub,    LibtoolVersion,    codecForAny,    DenominationPubKey,    DenomKeyType,    ExchangeKeysJson, +  TalerProtocolTimestamp, +  TalerProtocolDuration,  } from "@gnu-taler/taler-util";  import { decodeCrock, encodeCrock, hash } from "@gnu-taler/taler-util";  import { CryptoApi } from "../crypto/workers/cryptoApi.js"; @@ -57,7 +57,7 @@ import {    WireInfo,  } from "../db.js";  import { -  getExpiryTimestamp, +  getExpiry,    HttpRequestLibrary,    readSuccessResponseJsonOrThrow,    readSuccessResponseTextOrThrow, @@ -80,7 +80,7 @@ const logger = new Logger("exchanges.ts");  function denominationRecordFromKeys(    exchangeBaseUrl: string,    exchangeMasterPub: string, -  listIssueDate: Timestamp, +  listIssueDate: TalerProtocolTimestamp,    denomIn: ExchangeDenomination,  ): DenominationRecord {    let denomPub: DenominationPubKey; @@ -132,7 +132,9 @@ async function handleExchangeUpdateError(  }  export function getExchangeRequestTimeout(): Duration { -  return { d_ms: 5000 }; +  return Duration.fromSpec({ +    seconds: 5, +  });  }  export interface ExchangeTosDownloadResult { @@ -362,12 +364,11 @@ export async function updateExchangeFromUrl(  async function provideExchangeRecord(    ws: InternalWalletState,    baseUrl: string, -  now: Timestamp, +  now: AbsoluteTime,  ): Promise<{    exchange: ExchangeRecord;    exchangeDetails: ExchangeDetailsRecord | undefined;  }> { -    return await ws.db      .mktx((x) => ({        exchanges: x.exchanges, @@ -376,14 +377,14 @@ async function provideExchangeRecord(      .runReadWrite(async (tx) => {        let exchange = await tx.exchanges.get(baseUrl);        if (!exchange) { -        const r = { +        const r: ExchangeRecord = {            permanent: true,            baseUrl: baseUrl,            retryInfo: initRetryInfo(),            detailsPointer: undefined,            lastUpdate: undefined, -          nextUpdate: now, -          nextRefreshCheck: now, +          nextUpdate: AbsoluteTime.toTimestamp(now), +          nextRefreshCheck: AbsoluteTime.toTimestamp(now),          };          await tx.exchanges.put(r);          exchange = r; @@ -400,10 +401,10 @@ interface ExchangeKeysDownloadResult {    currentDenominations: DenominationRecord[];    protocolVersion: string;    signingKeys: ExchangeSignKeyJson[]; -  reserveClosingDelay: Duration; -  expiry: Timestamp; +  reserveClosingDelay: TalerProtocolDuration; +  expiry: TalerProtocolTimestamp;    recoup: Recoup[]; -  listIssueDate: Timestamp; +  listIssueDate: TalerProtocolTimestamp;  }  /** @@ -475,9 +476,11 @@ async function downloadExchangeKeysInfo(      protocolVersion: exchangeKeysJsonUnchecked.version,      signingKeys: exchangeKeysJsonUnchecked.signkeys,      reserveClosingDelay: exchangeKeysJsonUnchecked.reserve_closing_delay, -    expiry: getExpiryTimestamp(resp, { -      minDuration: durationFromSpec({ hours: 1 }), -    }), +    expiry: AbsoluteTime.toTimestamp( +      getExpiry(resp, { +        minDuration: durationFromSpec({ hours: 1 }), +      }), +    ),      recoup: exchangeKeysJsonUnchecked.recoup ?? [],      listIssueDate: exchangeKeysJsonUnchecked.list_issue_date,    }; @@ -529,12 +532,20 @@ async function updateExchangeFromUrlImpl(    exchangeDetails: ExchangeDetailsRecord;  }> {    logger.info(`updating exchange info for ${baseUrl}, forced: ${forceNow}`); -  const now = getTimestampNow(); +  const now = AbsoluteTime.now();    baseUrl = canonicalizeBaseUrl(baseUrl); -  const { exchange, exchangeDetails } = await provideExchangeRecord(ws, baseUrl, now); +  const { exchange, exchangeDetails } = await provideExchangeRecord( +    ws, +    baseUrl, +    now, +  ); -  if (!forceNow && exchangeDetails !== undefined && !isTimestampExpired(exchange.nextUpdate)) { +  if ( +    !forceNow && +    exchangeDetails !== undefined && +    !AbsoluteTime.isExpired(AbsoluteTime.fromTimestamp(exchange.nextUpdate)) +  ) {      logger.info("using existing exchange info");      return { exchange, exchangeDetails };    } @@ -575,7 +586,8 @@ async function updateExchangeFromUrlImpl(      timeout,      acceptedFormat,    ); -  const tosHasBeenAccepted = exchangeDetails?.termsOfServiceAcceptedEtag === tosDownload.tosEtag +  const tosHasBeenAccepted = +    exchangeDetails?.termsOfServiceAcceptedEtag === tosDownload.tosEtag;    let recoupGroupId: string | undefined; @@ -611,23 +623,25 @@ async function updateExchangeFromUrlImpl(          exchangeBaseUrl: r.baseUrl,          wireInfo,          termsOfServiceText: tosDownload.tosText, -        termsOfServiceAcceptedEtag: tosHasBeenAccepted ? tosDownload.tosEtag : undefined, +        termsOfServiceAcceptedEtag: tosHasBeenAccepted +          ? tosDownload.tosEtag +          : undefined,          termsOfServiceContentType: tosDownload.tosContentType,          termsOfServiceLastEtag: tosDownload.tosEtag, -        termsOfServiceAcceptedTimestamp: getTimestampNow(), +        termsOfServiceAcceptedTimestamp: TalerProtocolTimestamp.now(),        };        // FIXME: only update if pointer got updated        r.lastError = undefined;        r.retryInfo = initRetryInfo(); -      r.lastUpdate = getTimestampNow(); +      r.lastUpdate = TalerProtocolTimestamp.now();        r.nextUpdate = keysInfo.expiry;        // New denominations might be available. -      r.nextRefreshCheck = getTimestampNow(); +      r.nextRefreshCheck = TalerProtocolTimestamp.now();        r.detailsPointer = {          currency: details.currency,          masterPublicKey: details.masterPublicKey,          // FIXME: only change if pointer really changed -        updateClock: getTimestampNow(), +        updateClock: TalerProtocolTimestamp.now(),          protocolVersionRange: keysInfo.protocolVersion,        };        await tx.exchanges.put(r); diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts index 9844dc52e..9521d544f 100644 --- a/packages/taler-wallet-core/src/operations/pay.ts +++ b/packages/taler-wallet-core/src/operations/pay.ts @@ -27,7 +27,6 @@  import {    AmountJson,    Amounts, -  CheckPaymentResponse,    codecForContractTerms,    codecForMerchantPayResponse,    codecForProposal, @@ -41,11 +40,8 @@ import {    durationMin,    durationMul,    encodeCrock, -  getDurationRemaining,    getRandomBytes, -  getTimestampNow,    HttpStatusCode, -  isTimestampExpired,    j2s,    kdf,    Logger, @@ -57,9 +53,9 @@ import {    stringToBytes,    TalerErrorCode,    TalerErrorDetails, -  Timestamp, -  timestampAddDuration, +  AbsoluteTime,    URL, +  TalerProtocolTimestamp,  } from "@gnu-taler/taler-util";  import { EXCHANGE_COINS_LOCK, InternalWalletState } from "../common.js";  import { @@ -172,7 +168,9 @@ function isSpendableCoin(coin: CoinRecord, denom: DenominationRecord): boolean {    if (coin.status !== CoinStatus.Fresh) {      return false;    } -  if (isTimestampExpired(denom.stampExpireDeposit)) { +  if ( +    AbsoluteTime.isExpired(AbsoluteTime.fromTimestamp(denom.stampExpireDeposit)) +  ) {      return false;    }    return true; @@ -187,7 +185,7 @@ export interface CoinSelectionRequest {    /**     * Timestamp of the contract.     */ -  timestamp: Timestamp; +  timestamp: TalerProtocolTimestamp;    wireMethod: string; @@ -422,7 +420,7 @@ async function recordConfirmPay(      payCoinSelectionUid: encodeCrock(getRandomBytes(32)),      totalPayCost: payCostInfo,      coinDepositPermissions, -    timestampAccept: getTimestampNow(), +    timestampAccept: AbsoluteTime.toTimestamp(AbsoluteTime.now()),      timestampLastRefundStatus: undefined,      proposalId: proposal.proposalId,      lastPayError: undefined, @@ -784,7 +782,7 @@ async function processDownloadProposalImpl(    } catch (e) {      const err = makeErrorDetails(        TalerErrorCode.WALLET_CONTRACT_TERMS_MALFORMED, -      "schema validation failed", +      `schema validation failed: ${e}`,        {},      );      await failProposalPermanently(ws, proposalId, err); @@ -921,7 +919,7 @@ async function startDownloadProposal(      noncePriv: priv,      noncePub: pub,      claimToken, -    timestamp: getTimestampNow(), +    timestamp: AbsoluteTime.toTimestamp(AbsoluteTime.now()),      merchantBaseUrl,      orderId,      proposalId: proposalId, @@ -956,7 +954,7 @@ async function storeFirstPaySuccess(    sessionId: string | undefined,    paySig: string,  ): Promise<void> { -  const now = getTimestampNow(); +  const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());    await ws.db      .mktx((x) => ({ purchases: x.purchases }))      .runReadWrite(async (tx) => { @@ -978,13 +976,16 @@ async function storeFirstPaySuccess(        purchase.payRetryInfo = initRetryInfo();        purchase.merchantPaySig = paySig;        if (isFirst) { -        const ar = purchase.download.contractData.autoRefund; -        if (ar) { +        const protoAr = purchase.download.contractData.autoRefund; +        if (protoAr) { +          const ar = Duration.fromTalerProtocolDuration(protoAr);            logger.info("auto_refund present");            purchase.refundQueryRequested = true;            purchase.refundStatusRetryInfo = initRetryInfo();            purchase.lastRefundStatusError = undefined; -          purchase.autoRefundDeadline = timestampAddDuration(now, ar); +          purchase.autoRefundDeadline = AbsoluteTime.toTimestamp( +            AbsoluteTime.addDuration(AbsoluteTime.now(), ar), +          );          }        }        await tx.purchases.put(purchase); @@ -1150,7 +1151,7 @@ async function unblockBackup(            if (bp.state.tag === BackupProviderStateTag.Retrying) {              bp.state = {                tag: BackupProviderStateTag.Ready, -              nextBackupTimestamp: getTimestampNow(), +              nextBackupTimestamp: TalerProtocolTimestamp.now(),              };            }          }); diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts index 6d686fb3a..fc76eeb19 100644 --- a/packages/taler-wallet-core/src/operations/pending.ts +++ b/packages/taler-wallet-core/src/operations/pending.ts @@ -35,7 +35,7 @@ import {    PendingTaskType,    ReserveType,  } from "../pending-types.js"; -import { getTimestampNow, Timestamp } from "@gnu-taler/taler-util"; +import { AbsoluteTime } from "@gnu-taler/taler-util";  import { InternalWalletState } from "../common.js";  import { GetReadOnlyAccess } from "../util/query.js"; @@ -44,21 +44,25 @@ async function gatherExchangePending(      exchanges: typeof WalletStoresV1.exchanges;      exchangeDetails: typeof WalletStoresV1.exchangeDetails;    }>, -  now: Timestamp, +  now: AbsoluteTime,    resp: PendingOperationsResponse,  ): Promise<void> {    await tx.exchanges.iter().forEachAsync(async (e) => {      resp.pendingOperations.push({        type: PendingTaskType.ExchangeUpdate,        givesLifeness: false, -      timestampDue: e.lastError ? e.retryInfo.nextRetry : e.nextUpdate, +      timestampDue: e.lastError +        ? e.retryInfo.nextRetry +        : AbsoluteTime.fromTimestamp(e.nextUpdate),        exchangeBaseUrl: e.baseUrl,        lastError: e.lastError,      });      resp.pendingOperations.push({        type: PendingTaskType.ExchangeCheckRefresh, -      timestampDue: e.lastError ? e.retryInfo.nextRetry : e.nextRefreshCheck, +      timestampDue: e.lastError +        ? e.retryInfo.nextRetry +        : AbsoluteTime.fromTimestamp(e.nextRefreshCheck),        givesLifeness: false,        exchangeBaseUrl: e.baseUrl,      }); @@ -67,7 +71,7 @@ async function gatherExchangePending(  async function gatherReservePending(    tx: GetReadOnlyAccess<{ reserves: typeof WalletStoresV1.reserves }>, -  now: Timestamp, +  now: AbsoluteTime,    resp: PendingOperationsResponse,  ): Promise<void> {    const reserves = await tx.reserves.indexes.byStatus.getAll( @@ -87,7 +91,7 @@ async function gatherReservePending(          resp.pendingOperations.push({            type: PendingTaskType.Reserve,            givesLifeness: true, -          timestampDue: reserve.retryInfo?.nextRetry ?? Timestamp.now(), +          timestampDue: reserve.retryInfo?.nextRetry ?? AbsoluteTime.now(),            stage: reserve.reserveStatus,            timestampCreated: reserve.timestampCreated,            reserveType, @@ -105,7 +109,7 @@ async function gatherReservePending(  async function gatherRefreshPending(    tx: GetReadOnlyAccess<{ refreshGroups: typeof WalletStoresV1.refreshGroups }>, -  now: Timestamp, +  now: AbsoluteTime,    resp: PendingOperationsResponse,  ): Promise<void> {    const refreshGroups = await tx.refreshGroups.indexes.byStatus.getAll( @@ -136,7 +140,7 @@ async function gatherWithdrawalPending(      withdrawalGroups: typeof WalletStoresV1.withdrawalGroups;      planchets: typeof WalletStoresV1.planchets;    }>, -  now: Timestamp, +  now: AbsoluteTime,    resp: PendingOperationsResponse,  ): Promise<void> {    const wsrs = await tx.withdrawalGroups.indexes.byStatus.getAll( @@ -169,14 +173,14 @@ async function gatherWithdrawalPending(  async function gatherProposalPending(    tx: GetReadOnlyAccess<{ proposals: typeof WalletStoresV1.proposals }>, -  now: Timestamp, +  now: AbsoluteTime,    resp: PendingOperationsResponse,  ): Promise<void> {    await tx.proposals.iter().forEach((proposal) => {      if (proposal.proposalStatus == ProposalStatus.Proposed) {        // Nothing to do, user needs to choose.      } else if (proposal.proposalStatus == ProposalStatus.Downloading) { -      const timestampDue = proposal.retryInfo?.nextRetry ?? getTimestampNow(); +      const timestampDue = proposal.retryInfo?.nextRetry ?? AbsoluteTime.now();        resp.pendingOperations.push({          type: PendingTaskType.ProposalDownload,          givesLifeness: true, @@ -194,7 +198,7 @@ async function gatherProposalPending(  async function gatherDepositPending(    tx: GetReadOnlyAccess<{ depositGroups: typeof WalletStoresV1.depositGroups }>, -  now: Timestamp, +  now: AbsoluteTime,    resp: PendingOperationsResponse,  ): Promise<void> {    const dgs = await tx.depositGroups.indexes.byStatus.getAll( @@ -204,7 +208,7 @@ async function gatherDepositPending(      if (dg.timestampFinished) {        return;      } -    const timestampDue = dg.retryInfo?.nextRetry ?? getTimestampNow(); +    const timestampDue = dg.retryInfo?.nextRetry ?? AbsoluteTime.now();      resp.pendingOperations.push({        type: PendingTaskType.Deposit,        givesLifeness: true, @@ -218,7 +222,7 @@ async function gatherDepositPending(  async function gatherTipPending(    tx: GetReadOnlyAccess<{ tips: typeof WalletStoresV1.tips }>, -  now: Timestamp, +  now: AbsoluteTime,    resp: PendingOperationsResponse,  ): Promise<void> {    await tx.tips.iter().forEach((tip) => { @@ -240,7 +244,7 @@ async function gatherTipPending(  async function gatherPurchasePending(    tx: GetReadOnlyAccess<{ purchases: typeof WalletStoresV1.purchases }>, -  now: Timestamp, +  now: AbsoluteTime,    resp: PendingOperationsResponse,  ): Promise<void> {    await tx.purchases.iter().forEach((pr) => { @@ -249,7 +253,7 @@ async function gatherPurchasePending(        pr.abortStatus === AbortStatus.None &&        !pr.payFrozen      ) { -      const timestampDue = pr.payRetryInfo?.nextRetry ?? getTimestampNow(); +      const timestampDue = pr.payRetryInfo?.nextRetry ?? AbsoluteTime.now();        resp.pendingOperations.push({          type: PendingTaskType.Pay,          givesLifeness: true, @@ -275,7 +279,7 @@ async function gatherPurchasePending(  async function gatherRecoupPending(    tx: GetReadOnlyAccess<{ recoupGroups: typeof WalletStoresV1.recoupGroups }>, -  now: Timestamp, +  now: AbsoluteTime,    resp: PendingOperationsResponse,  ): Promise<void> {    await tx.recoupGroups.iter().forEach((rg) => { @@ -297,7 +301,7 @@ async function gatherBackupPending(    tx: GetReadOnlyAccess<{      backupProviders: typeof WalletStoresV1.backupProviders;    }>, -  now: Timestamp, +  now: AbsoluteTime,    resp: PendingOperationsResponse,  ): Promise<void> {    await tx.backupProviders.iter().forEach((bp) => { @@ -305,7 +309,7 @@ async function gatherBackupPending(        resp.pendingOperations.push({          type: PendingTaskType.Backup,          givesLifeness: false, -        timestampDue: bp.state.nextBackupTimestamp, +        timestampDue: AbsoluteTime.fromTimestamp(bp.state.nextBackupTimestamp),          backupProviderBaseUrl: bp.baseUrl,          lastError: undefined,        }); @@ -325,7 +329,7 @@ async function gatherBackupPending(  export async function getPendingOperations(    ws: InternalWalletState,  ): Promise<PendingOperationsResponse> { -  const now = getTimestampNow(); +  const now = AbsoluteTime.now();    return await ws.db      .mktx((x) => ({        backupProviders: x.backupProviders, diff --git a/packages/taler-wallet-core/src/operations/recoup.ts b/packages/taler-wallet-core/src/operations/recoup.ts index 23d14f212..84a27966d 100644 --- a/packages/taler-wallet-core/src/operations/recoup.ts +++ b/packages/taler-wallet-core/src/operations/recoup.ts @@ -27,11 +27,11 @@  import {    Amounts,    codecForRecoupConfirmation, -  getTimestampNow,    j2s,    NotificationType,    RefreshReason,    TalerErrorDetails, +  TalerProtocolTimestamp,  } from "@gnu-taler/taler-util";  import { encodeCrock, getRandomBytes } from "@gnu-taler/taler-util";  import { @@ -110,7 +110,7 @@ async function putGroupAsFinished(    }    if (allFinished) {      logger.info("all recoups of recoup group are finished"); -    recoupGroup.timestampFinished = getTimestampNow(); +    recoupGroup.timestampFinished = TalerProtocolTimestamp.now();      recoupGroup.retryInfo = initRetryInfo();      recoupGroup.lastError = undefined;      if (recoupGroup.scheduleRefreshCoins.length > 0) { @@ -467,7 +467,7 @@ export async function createRecoupGroup(      coinPubs: coinPubs,      lastError: undefined,      timestampFinished: undefined, -    timestampStarted: getTimestampNow(), +    timestampStarted: TalerProtocolTimestamp.now(),      retryInfo: initRetryInfo(),      recoupFinishedPerCoin: coinPubs.map(() => false),      // Will be populated later diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index 8b6d8b2e4..11f0f6c51 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -23,7 +23,7 @@ import {    ExchangeRefreshRevealRequest,    getRandomBytes,    HttpStatusCode, -  j2s, +  TalerProtocolTimestamp,  } from "@gnu-taler/taler-util";  import {    CoinRecord, @@ -42,11 +42,8 @@ import {    fnutil,    NotificationType,    RefreshGroupId, -  RefreshPlanchetInfo,    RefreshReason, -  stringifyTimestamp,    TalerErrorDetails, -  timestampToIsoString,  } from "@gnu-taler/taler-util";  import { AmountJson, Amounts } from "@gnu-taler/taler-util";  import { amountToPretty } from "@gnu-taler/taler-util"; @@ -61,12 +58,7 @@ import {    Duration,    durationFromSpec,    durationMul, -  getTimestampNow, -  isTimestampExpired, -  Timestamp, -  timestampAddDuration, -  timestampDifference, -  timestampMin, +  AbsoluteTime,    URL,  } from "@gnu-taler/taler-util";  import { guardOperationException } from "../errors.js"; @@ -139,7 +131,7 @@ function updateGroupStatus(rg: RefreshGroupRecord): void {        rg.frozen = true;        rg.retryInfo = initRetryInfo();      } else { -      rg.timestampFinished = getTimestampNow(); +      rg.timestampFinished = AbsoluteTime.toTimestamp(AbsoluteTime.now());        rg.operationStatus = OperationStatus.Finished;        rg.retryInfo = initRetryInfo();      } @@ -234,19 +226,6 @@ async function refreshCreateSession(      availableDenoms,    ); -  if (logger.shouldLogTrace()) { -    logger.trace(`printing selected denominations for refresh`); -    logger.trace(`current time: ${stringifyTimestamp(getTimestampNow())}`); -    for (const denom of newCoinDenoms.selectedDenoms) { -      logger.trace(`denom ${denom.denom}, count ${denom.count}`); -      logger.trace( -        `withdrawal expiration ${stringifyTimestamp( -          denom.denom.stampExpireWithdraw, -        )}`, -      ); -    } -  } -    if (newCoinDenoms.selectedDenoms.length === 0) {      logger.trace(        `not refreshing, available amount ${amountToPretty( @@ -306,7 +285,9 @@ async function refreshCreateSession(  }  function getRefreshRequestTimeout(rg: RefreshGroupRecord): Duration { -  return { d_ms: 5000 }; +  return Duration.fromSpec({ +    seconds: 5, +  });  }  async function refreshMelt( @@ -949,12 +930,12 @@ export async function createRefreshGroup(      retryInfo: initRetryInfo(),      inputPerCoin,      estimatedOutputPerCoin, -    timestampCreated: getTimestampNow(), +    timestampCreated: TalerProtocolTimestamp.now(),    };    if (oldCoinPubs.length == 0) {      logger.warn("created refresh group with zero coins"); -    refreshGroup.timestampFinished = getTimestampNow(); +    refreshGroup.timestampFinished = TalerProtocolTimestamp.now();      refreshGroup.operationStatus = OperationStatus.Finished;    } @@ -974,25 +955,23 @@ export async function createRefreshGroup(  /**   * Timestamp after which the wallet would do the next check for an auto-refresh.   */ -function getAutoRefreshCheckThreshold(d: DenominationRecord): Timestamp { -  const delta = timestampDifference( -    d.stampExpireWithdraw, -    d.stampExpireDeposit, -  ); +function getAutoRefreshCheckThreshold(d: DenominationRecord): AbsoluteTime { +  const expireWithdraw = AbsoluteTime.fromTimestamp(d.stampExpireWithdraw); +  const expireDeposit = AbsoluteTime.fromTimestamp(d.stampExpireDeposit); +  const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit);    const deltaDiv = durationMul(delta, 0.75); -  return timestampAddDuration(d.stampExpireWithdraw, deltaDiv); +  return AbsoluteTime.addDuration(expireWithdraw, deltaDiv);  }  /**   * Timestamp after which the wallet would do an auto-refresh.   */ -function getAutoRefreshExecuteThreshold(d: DenominationRecord): Timestamp { -  const delta = timestampDifference( -    d.stampExpireWithdraw, -    d.stampExpireDeposit, -  ); +function getAutoRefreshExecuteThreshold(d: DenominationRecord): AbsoluteTime { +  const expireWithdraw = AbsoluteTime.fromTimestamp(d.stampExpireWithdraw); +  const expireDeposit = AbsoluteTime.fromTimestamp(d.stampExpireDeposit); +  const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit);    const deltaDiv = durationMul(delta, 0.5); -  return timestampAddDuration(d.stampExpireWithdraw, deltaDiv); +  return AbsoluteTime.addDuration(expireWithdraw, deltaDiv);  }  export async function autoRefresh( @@ -1001,8 +980,8 @@ export async function autoRefresh(  ): Promise<void> {    logger.info(`doing auto-refresh check for '${exchangeBaseUrl}'`);    await updateExchangeFromUrl(ws, exchangeBaseUrl, undefined, true); -  let minCheckThreshold = timestampAddDuration( -    getTimestampNow(), +  let minCheckThreshold = AbsoluteTime.addDuration( +    AbsoluteTime.now(),      durationFromSpec({ days: 1 }),    );    await ws.db @@ -1037,11 +1016,14 @@ export async function autoRefresh(            continue;          }          const executeThreshold = getAutoRefreshExecuteThreshold(denom); -        if (isTimestampExpired(executeThreshold)) { +        if (AbsoluteTime.isExpired(executeThreshold)) {            refreshCoins.push(coin);          } else {            const checkThreshold = getAutoRefreshCheckThreshold(denom); -          minCheckThreshold = timestampMin(minCheckThreshold, checkThreshold); +          minCheckThreshold = AbsoluteTime.min( +            minCheckThreshold, +            checkThreshold, +          );          }        }        if (refreshCoins.length > 0) { @@ -1056,12 +1038,12 @@ export async function autoRefresh(          );        }        logger.info( -        `current wallet time: ${timestampToIsoString(getTimestampNow())}`, +        `current wallet time: ${AbsoluteTime.toIsoString(AbsoluteTime.now())}`,        );        logger.info( -        `next refresh check at ${timestampToIsoString(minCheckThreshold)}`, +        `next refresh check at ${AbsoluteTime.toIsoString(minCheckThreshold)}`,        ); -      exchange.nextRefreshCheck = minCheckThreshold; +      exchange.nextRefreshCheck = AbsoluteTime.toTimestamp(minCheckThreshold);        await tx.exchanges.put(exchange);      });  } diff --git a/packages/taler-wallet-core/src/operations/refund.ts b/packages/taler-wallet-core/src/operations/refund.ts index 106c79365..686d545df 100644 --- a/packages/taler-wallet-core/src/operations/refund.ts +++ b/packages/taler-wallet-core/src/operations/refund.ts @@ -32,7 +32,6 @@ import {    codecForAbortResponse,    codecForMerchantOrderRefundPickupResponse,    CoinPublicKey, -  getTimestampNow,    Logger,    MerchantCoinRefundFailureStatus,    MerchantCoinRefundStatus, @@ -43,9 +42,10 @@ import {    TalerErrorCode,    TalerErrorDetails,    URL, -  timestampAddDuration,    codecForMerchantOrderStatusPaid, -  isTimestampExpired, +  AbsoluteTime, +  TalerProtocolTimestamp, +  Duration,  } from "@gnu-taler/taler-util";  import {    AbortStatus, @@ -170,7 +170,7 @@ async function applySuccessfulRefund(    p.refunds[refundKey] = {      type: RefundState.Applied, -    obtainedTime: getTimestampNow(), +    obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()),      executionTime: r.execution_time,      refundAmount: Amounts.parseOrThrow(r.refund_amount),      refundFee: denom.feeRefund, @@ -222,7 +222,7 @@ async function storePendingRefund(    p.refunds[refundKey] = {      type: RefundState.Pending, -    obtainedTime: getTimestampNow(), +    obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()),      executionTime: r.execution_time,      refundAmount: Amounts.parseOrThrow(r.refund_amount),      refundFee: denom.feeRefund, @@ -275,7 +275,7 @@ async function storeFailedRefund(    p.refunds[refundKey] = {      type: RefundState.Failed, -    obtainedTime: getTimestampNow(), +    obtainedTime: TalerProtocolTimestamp.now(),      executionTime: r.execution_time,      refundAmount: Amounts.parseOrThrow(r.refund_amount),      refundFee: denom.feeRefund, @@ -327,7 +327,7 @@ async function acceptRefunds(    reason: RefundReason,  ): Promise<void> {    logger.trace("handling refunds", refunds); -  const now = getTimestampNow(); +  const now = TalerProtocolTimestamp.now();    await ws.db      .mktx((x) => ({ @@ -401,7 +401,10 @@ async function acceptRefunds(        if (          p.timestampFirstSuccessfulPay &&          p.autoRefundDeadline && -        p.autoRefundDeadline.t_ms > now.t_ms +        AbsoluteTime.cmp( +          AbsoluteTime.fromTimestamp(p.autoRefundDeadline), +          AbsoluteTime.fromTimestamp(now), +        ) > 0        ) {          queryDone = false;        } @@ -556,8 +559,10 @@ export async function applyRefund(          ).amount,        ).amount;      } else { -      amountRefundGone = Amounts.add(amountRefundGone, refund.refundAmount) -        .amount; +      amountRefundGone = Amounts.add( +        amountRefundGone, +        refund.refundAmount, +      ).amount;      }    }); @@ -623,7 +628,9 @@ async function processPurchaseQueryRefundImpl(      if (        waitForAutoRefund &&        purchase.autoRefundDeadline && -      !isTimestampExpired(purchase.autoRefundDeadline) +      !AbsoluteTime.isExpired( +        AbsoluteTime.fromTimestamp(purchase.autoRefundDeadline), +      )      ) {        const requestUrl = new URL(          `orders/${purchase.download.contractData.orderId}`, @@ -731,11 +738,13 @@ async function processPurchaseQueryRefundImpl(            purchase.payCoinSelection.coinContributions[i],          ),          rtransaction_id: 0, -        execution_time: timestampAddDuration( -          purchase.download.contractData.timestamp, -          { -            d_ms: 1000, -          }, +        execution_time: AbsoluteTime.toTimestamp( +          AbsoluteTime.addDuration( +            AbsoluteTime.fromTimestamp( +              purchase.download.contractData.timestamp, +            ), +            Duration.fromSpec({ seconds: 1 }), +          ),          ),        });      } diff --git a/packages/taler-wallet-core/src/operations/reserves.ts b/packages/taler-wallet-core/src/operations/reserves.ts index d91ce89f1..ac9483631 100644 --- a/packages/taler-wallet-core/src/operations/reserves.ts +++ b/packages/taler-wallet-core/src/operations/reserves.ts @@ -29,15 +29,13 @@ import {    durationMin,    encodeCrock,    getRandomBytes, -  getTimestampNow,    j2s,    Logger,    NotificationType,    randomBytes, -  ReserveTransactionType,    TalerErrorCode,    TalerErrorDetails, -  Timestamp, +  AbsoluteTime,    URL,  } from "@gnu-taler/taler-util";  import { InternalWalletState } from "../common.js"; @@ -172,7 +170,7 @@ export async function createReserve(    req: CreateReserveRequest,  ): Promise<CreateReserveResponse> {    const keypair = await ws.cryptoApi.createEddsaKeypair(); -  const now = getTimestampNow(); +  const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());    const canonExchange = canonicalizeBaseUrl(req.exchange);    let reserveStatus; @@ -217,7 +215,6 @@ export async function createReserve(      timestampReserveInfoPosted: undefined,      bankInfo,      reserveStatus, -    lastSuccessfulStatusQuery: undefined,      retryInfo: initRetryInfo(),      lastError: undefined,      currency: req.amount.currency, @@ -403,7 +400,9 @@ async function registerReserveWithBank(          default:            return;        } -      r.timestampReserveInfoPosted = getTimestampNow(); +      r.timestampReserveInfoPosted = AbsoluteTime.toTimestamp( +        AbsoluteTime.now(), +      );        r.reserveStatus = ReserveRecordStatus.WaitConfirmBank;        r.operationStatus = OperationStatus.Pending;        if (!r.bankInfo) { @@ -472,7 +471,7 @@ async function processReserveBankStatus(            default:              return;          } -        const now = getTimestampNow(); +        const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());          r.timestampBankConfirmed = now;          r.reserveStatus = ReserveRecordStatus.BankAborted;          r.operationStatus = OperationStatus.Finished; @@ -509,7 +508,7 @@ async function processReserveBankStatus(            default:              return;          } -        const now = getTimestampNow(); +        const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());          r.timestampBankConfirmed = now;          r.reserveStatus = ReserveRecordStatus.QueryingStatus;          r.operationStatus = OperationStatus.Pending; @@ -683,7 +682,7 @@ async function updateReserve(          exchangeBaseUrl: reserve.exchangeBaseUrl,          reservePub: reserve.reservePub,          rawWithdrawalAmount: remainingAmount, -        timestampStart: getTimestampNow(), +        timestampStart: AbsoluteTime.toTimestamp(AbsoluteTime.now()),          retryInfo: initRetryInfo(),          lastError: undefined,          denomsSel: denomSelectionInfoToState(denomSelInfo), @@ -736,7 +735,7 @@ async function processReserveImpl(      await resetReserveRetry(ws, reservePub);    } else if (      reserve.retryInfo && -    !Timestamp.isExpired(reserve.retryInfo.nextRetry) +    !AbsoluteTime.isExpired(reserve.retryInfo.nextRetry)    ) {      logger.trace("processReserve retry not due yet");      return; diff --git a/packages/taler-wallet-core/src/operations/testing.ts b/packages/taler-wallet-core/src/operations/testing.ts index 93f48fb83..23fee56c1 100644 --- a/packages/taler-wallet-core/src/operations/testing.ts +++ b/packages/taler-wallet-core/src/operations/testing.ts @@ -229,8 +229,8 @@ async function createOrder(        amount,        summary,        fulfillment_url: fulfillmentUrl, -      refund_deadline: { t_ms: t * 1000 }, -      wire_transfer_deadline: { t_ms: t * 1000 }, +      refund_deadline: { t_s: t }, +      wire_transfer_deadline: { t_s: t },      },    };    const resp = await http.postJson(reqUrl, orderReq, { diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts index a2a4e6f49..765120294 100644 --- a/packages/taler-wallet-core/src/operations/tip.ts +++ b/packages/taler-wallet-core/src/operations/tip.ts @@ -22,7 +22,6 @@ import {    parseTipUri,    codecForTipPickupGetResponse,    Amounts, -  getTimestampNow,    TalerErrorDetails,    NotificationType,    TipPlanchetDetail, @@ -32,6 +31,7 @@ import {    DenomKeyType,    BlindedDenominationSignature,    codecForMerchantTipResponseV2, +  TalerProtocolTimestamp,  } from "@gnu-taler/taler-util";  import { DerivedTipPlanchet } from "../crypto/cryptoTypes.js";  import { @@ -39,6 +39,7 @@ import {    CoinRecord,    CoinSourceType,    CoinStatus, +  TipRecord,  } from "../db.js";  import { j2s } from "@gnu-taler/taler-util";  import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js"; @@ -115,14 +116,14 @@ export async function prepareTip(      const secretSeed = encodeCrock(getRandomBytes(64));      const denomSelUid = encodeCrock(getRandomBytes(32)); -    const newTipRecord = { +    const newTipRecord: TipRecord = {        walletTipId: walletTipId,        acceptedTimestamp: undefined,        tipAmountRaw: amount,        tipExpiration: tipPickupStatus.expiration,        exchangeBaseUrl: tipPickupStatus.exchange_url,        merchantBaseUrl: res.merchantBaseUrl, -      createdTimestamp: getTimestampNow(), +      createdTimestamp: TalerProtocolTimestamp.now(),        merchantTipId: res.merchantTipId,        tipAmountEffective: Amounts.sub(          amount, @@ -397,7 +398,7 @@ async function processTipImpl(        if (tr.pickedUpTimestamp) {          return;        } -      tr.pickedUpTimestamp = getTimestampNow(); +      tr.pickedUpTimestamp = TalerProtocolTimestamp.now();        tr.lastError = undefined;        tr.retryInfo = initRetryInfo();        await tx.tips.put(tr); @@ -421,7 +422,7 @@ export async function acceptTip(          logger.error("tip not found");          return false;        } -      tipRecord.acceptedTimestamp = getTimestampNow(); +      tipRecord.acceptedTimestamp = TalerProtocolTimestamp.now();        await tx.tips.put(tipRecord);        return true;      }); diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 23ab39052..bc466f5a0 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -18,12 +18,12 @@   * Imports.   */  import { +  AbsoluteTime,    AmountJson,    Amounts,    Logger,    OrderShortInfo,    PaymentStatus, -  timestampCmp,    Transaction,    TransactionsRequest,    TransactionsResponse, @@ -309,7 +309,7 @@ export async function getTransactions(            for (const rk of Object.keys(pr.refunds)) {              const refund = pr.refunds[rk]; -            const groupKey = `${refund.executionTime.t_ms}`; +            const groupKey = `${refund.executionTime.t_s}`;              refundGroupKeys.add(groupKey);            } @@ -333,7 +333,7 @@ export async function getTransactions(              let amountEffective = Amounts.getZero(contractData.amount.currency);              for (const rk of Object.keys(pr.refunds)) {                const refund = pr.refunds[rk]; -              const myGroupKey = `${refund.executionTime.t_ms}`; +              const myGroupKey = `${refund.executionTime.t_s}`;                if (myGroupKey !== groupKey) {                  continue;                } @@ -403,8 +403,18 @@ export async function getTransactions(    const txPending = transactions.filter((x) => x.pending);    const txNotPending = transactions.filter((x) => !x.pending); -  txPending.sort((h1, h2) => timestampCmp(h1.timestamp, h2.timestamp)); -  txNotPending.sort((h1, h2) => timestampCmp(h1.timestamp, h2.timestamp)); +  txPending.sort((h1, h2) => +    AbsoluteTime.cmp( +      AbsoluteTime.fromTimestamp(h1.timestamp), +      AbsoluteTime.fromTimestamp(h2.timestamp), +    ), +  ); +  txNotPending.sort((h1, h2) => +    AbsoluteTime.cmp( +      AbsoluteTime.fromTimestamp(h1.timestamp), +      AbsoluteTime.fromTimestamp(h2.timestamp), +    ), +  );    return { transactions: [...txNotPending, ...txPending] };  } @@ -485,11 +495,10 @@ export async function deleteTransaction(            });            return;          } -        const reserveRecord: -          | ReserveRecord -          | undefined = await tx.reserves.indexes.byInitialWithdrawalGroupId.get( -          withdrawalGroupId, -        ); +        const reserveRecord: ReserveRecord | undefined = +          await tx.reserves.indexes.byInitialWithdrawalGroupId.get( +            withdrawalGroupId, +          );          if (reserveRecord && !reserveRecord.initialWithdrawalStarted) {            const reservePub = reserveRecord.reservePub;            await tx.reserves.delete(reservePub); diff --git a/packages/taler-wallet-core/src/operations/withdraw.test.ts b/packages/taler-wallet-core/src/operations/withdraw.test.ts index 02540848a..e5894a3e7 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.test.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.test.ts @@ -62,16 +62,16 @@ test("withdrawal selection bug repro", (t) => {        masterSig:          "4F0P456CNNTTWK8BFJHGM3JTD6FVVNZY8EP077GYAHDJ5Y81S5RQ3SMS925NXMDVG9A88JAAP0E2GDZBC21PP5NHFFVWHAW3AVT8J3R",        stampExpireDeposit: { -        t_ms: 1742909388000, +        t_s: 1742909388,        },        stampExpireLegal: { -        t_ms: 1900589388000, +        t_s: 1900589388,        },        stampExpireWithdraw: { -        t_ms: 1679837388000, +        t_s: 1679837388,        },        stampStart: { -        t_ms: 1585229388000, +        t_s: 1585229388,        },        verificationStatus: DenominationVerificationStatus.Unverified,        value: { @@ -79,7 +79,7 @@ test("withdrawal selection bug repro", (t) => {          fraction: 0,          value: 1000,        }, -      listIssueDate: { t_ms: 0 }, +      listIssueDate: { t_s: 0 },      },      {        denomPub: { @@ -117,16 +117,16 @@ test("withdrawal selection bug repro", (t) => {        masterSig:          "P99AW82W46MZ0AKW7Z58VQPXFNTJQM9DVTYPBDF6KVYF38PPVDAZTV7JQ8TY7HGEC7JJJAY4E7AY7J3W1WV10DAZZQHHKTAVTSRAC20",        stampExpireDeposit: { -        t_ms: 1742909388000, +        t_s: 1742909388,        },        stampExpireLegal: { -        t_ms: 1900589388000, +        t_s: 1900589388,        },        stampExpireWithdraw: { -        t_ms: 1679837388000, +        t_s: 1679837388,        },        stampStart: { -        t_ms: 1585229388000, +        t_s: 1585229388,        },        verificationStatus: DenominationVerificationStatus.Unverified,        value: { @@ -134,7 +134,7 @@ test("withdrawal selection bug repro", (t) => {          fraction: 0,          value: 10,        }, -      listIssueDate: { t_ms: 0 }, +      listIssueDate: { t_s: 0 },      },      {        denomPub: { @@ -171,16 +171,16 @@ test("withdrawal selection bug repro", (t) => {        masterSig:          "8S4VZGHE5WE0N5ZVCHYW9KZZR4YAKK15S46MV1HR1QB9AAMH3NWPW4DCR4NYGJK33Q8YNFY80SWNS6XKAP5DEVK933TM894FJ2VGE3G",        stampExpireDeposit: { -        t_ms: 1742909388000, +        t_s: 1742909388,        },        stampExpireLegal: { -        t_ms: 1900589388000, +        t_s: 1900589388,        },        stampExpireWithdraw: { -        t_ms: 1679837388000, +        t_s: 1679837388,        },        stampStart: { -        t_ms: 1585229388000, +        t_s: 1585229388,        },        verificationStatus: DenominationVerificationStatus.Unverified,        value: { @@ -188,7 +188,7 @@ test("withdrawal selection bug repro", (t) => {          fraction: 0,          value: 5,        }, -      listIssueDate: { t_ms: 0 }, +      listIssueDate: { t_s: 0 },      },      {        denomPub: { @@ -226,16 +226,16 @@ test("withdrawal selection bug repro", (t) => {        masterSig:          "E3AWGAG8VB42P3KXM8B04Z6M483SX59R3Y4T53C3NXCA2NPB6C7HVCMVX05DC6S58E9X40NGEBQNYXKYMYCF3ASY2C4WP1WCZ4ME610",        stampExpireDeposit: { -        t_ms: 1742909388000, +        t_s: 1742909388,        },        stampExpireLegal: { -        t_ms: 1900589388000, +        t_s: 1900589388,        },        stampExpireWithdraw: { -        t_ms: 1679837388000, +        t_s: 1679837388,        },        stampStart: { -        t_ms: 1585229388000, +        t_s: 1585229388,        },        verificationStatus: DenominationVerificationStatus.Unverified,        value: { @@ -243,7 +243,7 @@ test("withdrawal selection bug repro", (t) => {          fraction: 0,          value: 1,        }, -      listIssueDate: { t_ms: 0 }, +      listIssueDate: { t_s: 0 },      },      {        denomPub: { @@ -280,16 +280,16 @@ test("withdrawal selection bug repro", (t) => {        masterSig:          "0ES1RKV002XB4YP21SN0QB7RSDHGYT0XAE65JYN8AVJAA6H7JZFN7JADXT521DJS89XMGPZGR8GCXF1516Y0Q9QDV00E6NMFA6CF838",        stampExpireDeposit: { -        t_ms: 1742909388000, +        t_s: 1742909388,        },        stampExpireLegal: { -        t_ms: 1900589388000, +        t_s: 1900589388,        },        stampExpireWithdraw: { -        t_ms: 1679837388000, +        t_s: 1679837388,        },        stampStart: { -        t_ms: 1585229388000, +        t_s: 1585229388,        },        verificationStatus: DenominationVerificationStatus.Unverified,        value: { @@ -297,7 +297,7 @@ test("withdrawal selection bug repro", (t) => {          fraction: 10000000,          value: 0,        }, -      listIssueDate: { t_ms: 0 }, +      listIssueDate: { t_s: 0 },      },      {        denomPub: { @@ -334,16 +334,16 @@ test("withdrawal selection bug repro", (t) => {        masterSig:          "58QEB6C6N7602E572E3JYANVVJ9BRW0V9E2ZFDW940N47YVQDK9SAFPWBN5YGT3G1742AFKQ0CYR4DM2VWV0Z0T1XMEKWN6X2EZ9M0R",        stampExpireDeposit: { -        t_ms: 1742909388000, +        t_s: 1742909388,        },        stampExpireLegal: { -        t_ms: 1900589388000, +        t_s: 1900589388,        },        stampExpireWithdraw: { -        t_ms: 1679837388000, +        t_s: 1679837388,        },        stampStart: { -        t_ms: 1585229388000, +        t_s: 1585229388,        },        verificationStatus: DenominationVerificationStatus.Unverified,        value: { @@ -351,7 +351,7 @@ test("withdrawal selection bug repro", (t) => {          fraction: 0,          value: 2,        }, -      listIssueDate: { t_ms: 0 }, +      listIssueDate: { t_s: 0 },      },    ]; diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 392cecf0b..e4c6f2a6a 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -26,16 +26,12 @@ import {    codecForWithdrawResponse,    durationFromSpec,    ExchangeListItem, -  getDurationRemaining, -  getTimestampNow,    Logger,    NotificationType,    parseWithdrawUri,    TalerErrorCode,    TalerErrorDetails, -  Timestamp, -  timestampCmp, -  timestampSubtractDuraction, +  AbsoluteTime,    WithdrawResponse,    URL,    WithdrawUriInfoResponse, @@ -44,6 +40,8 @@ import {    LibtoolVersion,    UnblindedSignature,    ExchangeWithdrawRequest, +  Duration, +  TalerProtocolTimestamp,  } from "@gnu-taler/taler-util";  import {    CoinRecord, @@ -147,7 +145,7 @@ export interface ExchangeWithdrawDetails {    /**     * The earliest deposit expiration of the selected coins.     */ -  earliestDepositExpiration: Timestamp; +  earliestDepositExpiration: TalerProtocolTimestamp;    /**     * Number of currently offered denominations. @@ -184,18 +182,20 @@ export interface ExchangeWithdrawDetails {   * revocation and offered state.   */  export function isWithdrawableDenom(d: DenominationRecord): boolean { -  const now = getTimestampNow(); -  const started = timestampCmp(now, d.stampStart) >= 0; -  let lastPossibleWithdraw: Timestamp; +  const now = AbsoluteTime.now(); +  const start = AbsoluteTime.fromTimestamp(d.stampStart); +  const withdrawExpire = AbsoluteTime.fromTimestamp(d.stampExpireWithdraw); +  const started = AbsoluteTime.cmp(now, start) >= 0; +  let lastPossibleWithdraw: AbsoluteTime;    if (walletCoreDebugFlags.denomselAllowLate) { -    lastPossibleWithdraw = d.stampExpireWithdraw; +    lastPossibleWithdraw = start;    } else { -    lastPossibleWithdraw = timestampSubtractDuraction( -      d.stampExpireWithdraw, +    lastPossibleWithdraw = AbsoluteTime.subtractDuraction( +      withdrawExpire,        durationFromSpec({ minutes: 5 }),      );    } -  const remaining = getDurationRemaining(lastPossibleWithdraw, now); +  const remaining = Duration.getRemaining(lastPossibleWithdraw, now);    const stillOkay = remaining.d_ms !== 0;    return started && stillOkay && !d.isRevoked && d.isOffered;  } @@ -274,7 +274,7 @@ export function selectWithdrawalDenominations(  /**   * Get information about a withdrawal from   * a taler://withdraw URI by asking the bank. - *  + *   * FIXME: Move into bank client.   */  export async function getBankWithdrawalInfo( @@ -947,7 +947,7 @@ async function processWithdrawGroupImpl(        logger.trace(`now withdrawn ${numFinished} of ${numTotalCoins} coins`);        if (wg.timestampFinish === undefined && numFinished === numTotalCoins) {          finishedForFirstTime = true; -        wg.timestampFinish = getTimestampNow(); +        wg.timestampFinish = TalerProtocolTimestamp.now();          wg.operationStatus = OperationStatus.Finished;          delete wg.lastError;          wg.retryInfo = initRetryInfo(); @@ -999,7 +999,12 @@ export async function getExchangeWithdrawalInfo(    for (let i = 1; i < selectedDenoms.selectedDenoms.length; i++) {      const expireDeposit =        selectedDenoms.selectedDenoms[i].denom.stampExpireDeposit; -    if (expireDeposit.t_ms < earliestDepositExpiration.t_ms) { +    if ( +      AbsoluteTime.cmp( +        AbsoluteTime.fromTimestamp(expireDeposit), +        AbsoluteTime.fromTimestamp(earliestDepositExpiration), +      ) < 0 +    ) {        earliestDepositExpiration = expireDeposit;      }    } diff --git a/packages/taler-wallet-core/src/pending-types.ts b/packages/taler-wallet-core/src/pending-types.ts index 911d0d8bd..4b1434bb5 100644 --- a/packages/taler-wallet-core/src/pending-types.ts +++ b/packages/taler-wallet-core/src/pending-types.ts @@ -27,7 +27,8 @@  import {    TalerErrorDetails,    BalancesResponse, -  Timestamp, +  AbsoluteTime, +  TalerProtocolTimestamp,  } from "@gnu-taler/taler-util";  import { ReserveRecordStatus } from "./db.js";  import { RetryInfo } from "./util/retries.js"; @@ -112,7 +113,7 @@ export interface PendingReserveTask {    type: PendingTaskType.Reserve;    retryInfo: RetryInfo | undefined;    stage: ReserveRecordStatus; -  timestampCreated: Timestamp; +  timestampCreated: TalerProtocolTimestamp;    reserveType: ReserveType;    reservePub: string;    bankWithdrawConfirmUrl?: string; @@ -135,7 +136,7 @@ export interface PendingRefreshTask {  export interface PendingProposalDownloadTask {    type: PendingTaskType.ProposalDownload;    merchantBaseUrl: string; -  proposalTimestamp: Timestamp; +  proposalTimestamp: TalerProtocolTimestamp;    proposalId: string;    orderId: string;    lastError?: TalerErrorDetails; @@ -149,7 +150,7 @@ export interface PendingProposalDownloadTask {  export interface PendingProposalChoiceOperation {    type: PendingTaskType.ProposalChoice;    merchantBaseUrl: string; -  proposalTimestamp: Timestamp; +  proposalTimestamp: AbsoluteTime;    proposalId: string;  } @@ -231,7 +232,7 @@ export interface PendingTaskInfoCommon {    /**     * Timestamp when the pending operation should be executed next.     */ -  timestampDue: Timestamp; +  timestampDue: AbsoluteTime;    /**     * Retry info.  Currently used to stop the wallet after any operation diff --git a/packages/taler-wallet-core/src/util/http.ts b/packages/taler-wallet-core/src/util/http.ts index 43fe29bba..79afd5707 100644 --- a/packages/taler-wallet-core/src/util/http.ts +++ b/packages/taler-wallet-core/src/util/http.ts @@ -28,10 +28,7 @@ import { OperationFailedError, makeErrorDetails } from "../errors.js";  import {    Logger,    Duration, -  Timestamp, -  getTimestampNow, -  timestampAddDuration, -  timestampMax, +  AbsoluteTime,    TalerErrorDetails,    Codec,    j2s, @@ -314,24 +311,24 @@ export async function readSuccessResponseTextOrThrow<T>(  /**   * Get the timestamp at which the response's content is considered expired.   */ -export function getExpiryTimestamp( +export function getExpiry(    httpResponse: HttpResponse,    opt: { minDuration?: Duration }, -): Timestamp { +): AbsoluteTime {    const expiryDateMs = new Date(      httpResponse.headers.get("expiry") ?? "",    ).getTime(); -  let t: Timestamp; +  let t: AbsoluteTime;    if (Number.isNaN(expiryDateMs)) { -    t = getTimestampNow(); +    t = AbsoluteTime.now();    } else {      t = {        t_ms: expiryDateMs,      };    }    if (opt.minDuration) { -    const t2 = timestampAddDuration(getTimestampNow(), opt.minDuration); -    return timestampMax(t, t2); +    const t2 = AbsoluteTime.addDuration(AbsoluteTime.now(), opt.minDuration); +    return AbsoluteTime.max(t, t2);    }    return t;  } diff --git a/packages/taler-wallet-core/src/util/retries.ts b/packages/taler-wallet-core/src/util/retries.ts index 8dec22bed..4b78d38ef 100644 --- a/packages/taler-wallet-core/src/util/retries.ts +++ b/packages/taler-wallet-core/src/util/retries.ts @@ -21,11 +21,11 @@  /**   * Imports.   */ -import { Timestamp, Duration, getTimestampNow } from "@gnu-taler/taler-util"; +import { AbsoluteTime, Duration } from "@gnu-taler/taler-util";  export interface RetryInfo { -  firstTry: Timestamp; -  nextRetry: Timestamp; +  firstTry: AbsoluteTime; +  nextRetry: AbsoluteTime;    retryCounter: number;  } @@ -45,7 +45,7 @@ export function updateRetryInfoTimeout(    r: RetryInfo,    p: RetryPolicy = defaultRetryPolicy,  ): void { -  const now = getTimestampNow(); +  const now = AbsoluteTime.now();    if (now.t_ms === "never") {      throw Error("assertion failed");    } @@ -54,10 +54,14 @@ export function updateRetryInfoTimeout(      return;    } -  const nextIncrement = p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter) +  const nextIncrement = +    p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter);    const t = -    now.t_ms + (p.maxTimeout.d_ms === "forever" ? nextIncrement : Math.min(p.maxTimeout.d_ms, nextIncrement)); +    now.t_ms + +    (p.maxTimeout.d_ms === "forever" +      ? nextIncrement +      : Math.min(p.maxTimeout.d_ms, nextIncrement));    r.nextRetry = { t_ms: t };  } @@ -73,13 +77,13 @@ export function getRetryDuration(      return { d_ms: "forever" };    }    const t = p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter); -  return { d_ms: p.maxTimeout.d_ms === "forever" ? t : Math.min(p.maxTimeout.d_ms, t) }; +  return { +    d_ms: p.maxTimeout.d_ms === "forever" ? t : Math.min(p.maxTimeout.d_ms, t), +  };  } -export function initRetryInfo( -  p: RetryPolicy = defaultRetryPolicy, -): RetryInfo { -  const now = getTimestampNow(); +export function initRetryInfo(p: RetryPolicy = defaultRetryPolicy): RetryInfo { +  const now = AbsoluteTime.now();    const info = {      firstTry: now,      nextRetry: now, diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 329417562..bbff465a8 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -64,9 +64,7 @@ import {    durationMin,    ExchangeListItem,    ExchangesListRespose, -  getDurationRemaining,    GetExchangeTosResult, -  isTimestampExpired,    j2s,    KnownBankAccounts,    Logger, @@ -76,11 +74,12 @@ import {    PaytoUri,    RefreshReason,    TalerErrorCode, -  Timestamp, -  timestampMin, +  AbsoluteTime,    URL,    WalletNotification, +  Duration,  } from "@gnu-taler/taler-util"; +import { timeStamp } from "console";  import {    DenomInfo,    ExchangeOperations, @@ -292,7 +291,7 @@ export async function runPending(  ): Promise<void> {    const pendingOpsResponse = await getPendingOperations(ws);    for (const p of pendingOpsResponse.pendingOperations) { -    if (!forceNow && !isTimestampExpired(p.timestampDue)) { +    if (!forceNow && !AbsoluteTime.isExpired(p.timestampDue)) {        continue;      }      try { @@ -340,10 +339,10 @@ async function runTaskLoop(      logger.trace(`pending operations: ${j2s(pending)}`);      let numGivingLiveness = 0;      let numDue = 0; -    let minDue: Timestamp = { t_ms: "never" }; +    let minDue: AbsoluteTime = AbsoluteTime.never();      for (const p of pending.pendingOperations) { -      minDue = timestampMin(minDue, p.timestampDue); -      if (isTimestampExpired(p.timestampDue)) { +      minDue = AbsoluteTime.min(minDue, p.timestampDue); +      if (AbsoluteTime.isExpired(p.timestampDue)) {          numDue++;        }        if (p.givesLifeness) { @@ -377,7 +376,7 @@ async function runTaskLoop(          durationFromSpec({            seconds: 5,          }), -        getDurationRemaining(minDue), +        Duration.getRemaining(minDue),        );        logger.trace(`waiting for at most ${dt.d_ms} ms`);        const timeout = ws.timerGroup.resolveAfter(dt); @@ -394,7 +393,7 @@ async function runTaskLoop(          `running ${pending.pendingOperations.length} pending operations`,        );        for (const p of pending.pendingOperations) { -        if (!isTimestampExpired(p.timestampDue)) { +        if (!AbsoluteTime.isExpired(p.timestampDue)) {            continue;          }          try {  | 
