parent
557dcec30d
commit
cd8f76db61
@ -1473,7 +1473,7 @@ async function updateUploadFees(
|
||||
x,
|
||||
).amount;
|
||||
};
|
||||
const expirationTime = AbsoluteTime.fromTimestamp(expiration);
|
||||
const expirationTime = AbsoluteTime.fromProtocolTimestamp(expiration);
|
||||
const years = Duration.toIntegerYears(Duration.getRemaining(expirationTime));
|
||||
logger.info(`computing fees for ${years} years`);
|
||||
// For now, we compute fees for *all* available providers.
|
||||
|
@ -59,7 +59,7 @@ export async function runAgeRestrictionsPeerTest(t: GlobalTestState) {
|
||||
restrictAge: 13,
|
||||
});
|
||||
|
||||
const purse_expiration = AbsoluteTime.toTimestamp(
|
||||
const purse_expiration = AbsoluteTime.toProtocolTimestamp(
|
||||
AbsoluteTime.addDuration(
|
||||
AbsoluteTime.now(),
|
||||
Duration.fromSpec({ days: 2 }),
|
||||
|
@ -177,7 +177,7 @@ export async function runExchangeTimetravelTest(t: GlobalTestState) {
|
||||
return {
|
||||
denomPub: x.denom_pub,
|
||||
expireDeposit: AbsoluteTime.stringify(
|
||||
AbsoluteTime.fromTimestamp(x.stamp_expire_deposit),
|
||||
AbsoluteTime.fromProtocolTimestamp(x.stamp_expire_deposit),
|
||||
),
|
||||
};
|
||||
});
|
||||
@ -186,7 +186,7 @@ export async function runExchangeTimetravelTest(t: GlobalTestState) {
|
||||
return {
|
||||
denomPub: x.denom_pub,
|
||||
expireDeposit: AbsoluteTime.stringify(
|
||||
AbsoluteTime.fromTimestamp(x.stamp_expire_deposit),
|
||||
AbsoluteTime.fromProtocolTimestamp(x.stamp_expire_deposit),
|
||||
),
|
||||
};
|
||||
});
|
||||
@ -196,7 +196,7 @@ export async function runExchangeTimetravelTest(t: GlobalTestState) {
|
||||
|
||||
console.log(
|
||||
"list issue date",
|
||||
AbsoluteTime.stringify(AbsoluteTime.fromTimestamp(keys1.list_issue_date)),
|
||||
AbsoluteTime.stringify(AbsoluteTime.fromProtocolTimestamp(keys1.list_issue_date)),
|
||||
);
|
||||
console.log("num denoms", keys1.denoms.length);
|
||||
console.log("denoms", JSON.stringify(denomPubs1, undefined, 2));
|
||||
@ -205,7 +205,7 @@ export async function runExchangeTimetravelTest(t: GlobalTestState) {
|
||||
|
||||
console.log(
|
||||
"list issue date",
|
||||
AbsoluteTime.stringify(AbsoluteTime.fromTimestamp(keys2.list_issue_date)),
|
||||
AbsoluteTime.stringify(AbsoluteTime.fromProtocolTimestamp(keys2.list_issue_date)),
|
||||
);
|
||||
console.log("num denoms", keys2.denoms.length);
|
||||
console.log("denoms", JSON.stringify(denomPubs2, undefined, 2));
|
||||
@ -227,7 +227,7 @@ export async function runExchangeTimetravelTest(t: GlobalTestState) {
|
||||
);
|
||||
console.log(
|
||||
`the new /keys response was issued ${AbsoluteTime.stringify(
|
||||
AbsoluteTime.fromTimestamp(keys2.list_issue_date),
|
||||
AbsoluteTime.fromProtocolTimestamp(keys2.list_issue_date),
|
||||
)}`,
|
||||
);
|
||||
console.log(
|
||||
|
@ -275,7 +275,7 @@ export async function runLibeufinBasicTest(t: GlobalTestState) {
|
||||
summary: "Buy me!",
|
||||
amount: "EUR:5",
|
||||
fulfillment_url: "taler://fulfillment-success/thx",
|
||||
wire_transfer_deadline: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
|
||||
wire_transfer_deadline: AbsoluteTime.toProtocolTimestamp(AbsoluteTime.now()),
|
||||
};
|
||||
|
||||
await makeTestPayment(t, { wallet, merchant, order });
|
||||
|
@ -75,7 +75,7 @@ export async function runPeerToPeerPullTest(t: GlobalTestState) {
|
||||
|
||||
await withdrawalDoneCond;
|
||||
|
||||
const purse_expiration = AbsoluteTime.toTimestamp(
|
||||
const purse_expiration = AbsoluteTime.toProtocolTimestamp(
|
||||
AbsoluteTime.addDuration(
|
||||
AbsoluteTime.now(),
|
||||
Duration.fromSpec({ days: 2 }),
|
||||
|
@ -47,7 +47,7 @@ export async function runPeerToPeerPushTest(t: GlobalTestState) {
|
||||
|
||||
await wallet1.runUntilDone();
|
||||
|
||||
const purse_expiration = AbsoluteTime.toTimestamp(
|
||||
const purse_expiration = AbsoluteTime.toProtocolTimestamp(
|
||||
AbsoluteTime.addDuration(
|
||||
AbsoluteTime.now(),
|
||||
Duration.fromSpec({ days: 2 }),
|
||||
|
@ -50,7 +50,7 @@ export async function runRefundGoneTest(t: GlobalTestState) {
|
||||
summary: "Buy me!",
|
||||
amount: "TESTKUDOS:5",
|
||||
fulfillment_url: "taler://fulfillment-success/thx",
|
||||
pay_deadline: AbsoluteTime.toTimestamp(
|
||||
pay_deadline: AbsoluteTime.toProtocolTimestamp(
|
||||
AbsoluteTime.addDuration(
|
||||
AbsoluteTime.now(),
|
||||
durationFromSpec({
|
||||
|
@ -63,7 +63,7 @@
|
||||
* Imports.
|
||||
*/
|
||||
import { DenominationPubKey, UnblindedSignature } from "./taler-types.js";
|
||||
import { TalerProtocolDuration, TalerProtocolTimestamp } from "./time.js";
|
||||
import { TalerProtocolDuration, TalerProtocolTimestamp, TalerPreciseTimestamp } from "./time.js";
|
||||
|
||||
export const BACKUP_TAG = "gnu-taler-wallet-backup-content" as const;
|
||||
/**
|
||||
@ -148,7 +148,7 @@ export interface WalletBackupContentV1 {
|
||||
* This timestamp should only be advanced if the content
|
||||
* of the backup changes.
|
||||
*/
|
||||
timestamp: TalerProtocolTimestamp;
|
||||
timestamp: TalerPreciseTimestamp;
|
||||
|
||||
/**
|
||||
* Per-exchange data sorted by exchange master public key.
|
||||
@ -274,14 +274,14 @@ export type BackupWgInfo =
|
||||
*
|
||||
* Set to undefined if that hasn't happened yet.
|
||||
*/
|
||||
timestamp_reserve_info_posted?: TalerProtocolTimestamp;
|
||||
timestamp_reserve_info_posted?: TalerPreciseTimestamp;
|
||||
|
||||
/**
|
||||
* Time when the reserve was confirmed by the bank.
|
||||
*
|
||||
* Set to undefined if not confirmed yet.
|
||||
*/
|
||||
timestamp_bank_confirmed?: TalerProtocolTimestamp;
|
||||
timestamp_bank_confirmed?: TalerPreciseTimestamp;
|
||||
}
|
||||
| {
|
||||
type: BackupWgType.PeerPullCredit;
|
||||
@ -315,9 +315,9 @@ export interface BackupWithdrawalGroup {
|
||||
|
||||
exchange_base_url: string;
|
||||
|
||||
timestamp_created: TalerProtocolTimestamp;
|
||||
timestamp_created: TalerPreciseTimestamp;
|
||||
|
||||
timestamp_finish?: TalerProtocolTimestamp;
|
||||
timestamp_finish?: TalerPreciseTimestamp;
|
||||
|
||||
operation_status: BackupOperationStatus;
|
||||
|
||||
@ -473,9 +473,9 @@ export interface BackupRecoupGroup {
|
||||
/**
|
||||
* Timestamp when the recoup was started.
|
||||
*/
|
||||
timestamp_created: TalerProtocolTimestamp;
|
||||
timestamp_created: TalerPreciseTimestamp;
|
||||
|
||||
timestamp_finish?: TalerProtocolTimestamp;
|
||||
timestamp_finish?: TalerPreciseTimestamp;
|
||||
finish_clock?: TalerProtocolTimestamp;
|
||||
// FIXME: Use some enum here!
|
||||
finish_is_failure?: boolean;
|
||||
@ -633,14 +633,14 @@ export interface BackupTip {
|
||||
* Has the user accepted the tip? Only after the tip has been accepted coins
|
||||
* withdrawn from the tip may be used.
|
||||
*/
|
||||
timestamp_accepted: TalerProtocolTimestamp | undefined;
|
||||
timestamp_accepted: TalerPreciseTimestamp | undefined;
|
||||
|
||||
/**
|
||||
* When was the tip first scanned by the wallet?
|
||||
*/
|
||||
timestamp_created: TalerProtocolTimestamp;
|
||||
timestamp_created: TalerPreciseTimestamp;
|
||||
|
||||
timestamp_finished?: TalerProtocolTimestamp;
|
||||
timestamp_finished?: TalerPreciseTimestamp;
|
||||
finish_is_failure?: boolean;
|
||||
|
||||
/**
|
||||
@ -765,9 +765,9 @@ export interface BackupRefreshGroup {
|
||||
*/
|
||||
old_coins: BackupRefreshOldCoin[];
|
||||
|
||||
timestamp_created: TalerProtocolTimestamp;
|
||||
timestamp_created: TalerPreciseTimestamp;
|
||||
|
||||
timestamp_finish?: TalerProtocolTimestamp;
|
||||
timestamp_finish?: TalerPreciseTimestamp;
|
||||
finish_is_failure?: boolean;
|
||||
}
|
||||
|
||||
@ -940,7 +940,7 @@ export interface BackupPurchase {
|
||||
* Timestamp of the first time that sending a payment to the merchant
|
||||
* for this purchase was successful.
|
||||
*/
|
||||
timestamp_first_successful_pay: TalerProtocolTimestamp | undefined;
|
||||
timestamp_first_successful_pay: TalerPreciseTimestamp | undefined;
|
||||
|
||||
/**
|
||||
* Signature by the merchant confirming the payment.
|
||||
@ -952,13 +952,13 @@ export interface BackupPurchase {
|
||||
*/
|
||||
pos_confirmation: string | undefined;
|
||||
|
||||
timestamp_proposed: TalerProtocolTimestamp;
|
||||
timestamp_proposed: TalerPreciseTimestamp;
|
||||
|
||||
/**
|
||||
* When was the purchase made?
|
||||
* Refers to the time that the user accepted.
|
||||
*/
|
||||
timestamp_accepted: TalerProtocolTimestamp | undefined;
|
||||
timestamp_accepted: TalerPreciseTimestamp | undefined;
|
||||
|
||||
/**
|
||||
* Pending refunds for the purchase. A refund is pending
|
||||
@ -1186,7 +1186,7 @@ export interface BackupExchange {
|
||||
*
|
||||
* Used to facilitate automatic merging.
|
||||
*/
|
||||
update_clock: TalerProtocolTimestamp;
|
||||
update_clock: TalerPreciseTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1258,7 +1258,7 @@ export interface BackupExchangeDetails {
|
||||
/**
|
||||
* Timestamp when the ToS has been accepted.
|
||||
*/
|
||||
tos_accepted_timestamp: TalerProtocolTimestamp | undefined;
|
||||
tos_accepted_timestamp: TalerPreciseTimestamp | undefined;
|
||||
}
|
||||
|
||||
export enum BackupProposalStatus {
|
||||
|
@ -25,8 +25,9 @@ import { Codec, renderContext, Context } from "./codec.js";
|
||||
|
||||
declare const flavor_AbsoluteTime: unique symbol;
|
||||
declare const flavor_TalerProtocolTimestamp: unique symbol;
|
||||
declare const flavor_TalerWalletDbTimestamp: unique symbol;
|
||||
declare const flavor_TalerPreciseTimestamp: unique symbol;
|
||||
|
||||
// FIXME: Make this opaque!
|
||||
export interface AbsoluteTime {
|
||||
/**
|
||||
* Timestamp in milliseconds.
|
||||
@ -45,7 +46,7 @@ export interface TalerProtocolTimestamp {
|
||||
readonly _flavor?: typeof flavor_TalerProtocolTimestamp;
|
||||
}
|
||||
|
||||
export interface TalerWalletDbTimestamp {
|
||||
export interface TalerPreciseTimestamp {
|
||||
/**
|
||||
* Seconds (as integer) since epoch.
|
||||
*/
|
||||
@ -56,12 +57,32 @@ export interface TalerWalletDbTimestamp {
|
||||
*/
|
||||
readonly off_us?: number;
|
||||
|
||||
readonly _flavor?: typeof flavor_TalerWalletDbTimestamp;
|
||||
readonly _flavor?: typeof flavor_TalerPreciseTimestamp;
|
||||
}
|
||||
|
||||
export namespace TalerPreciseTimestamp {
|
||||
export function now(): TalerPreciseTimestamp {
|
||||
const absNow = AbsoluteTime.now();
|
||||
return AbsoluteTime.toPreciseTimestamp(absNow);
|
||||
}
|
||||
|
||||
export function round(t: TalerPreciseTimestamp): TalerProtocolTimestamp {
|
||||
return {
|
||||
t_s: t.t_s,
|
||||
}
|
||||
}
|
||||
|
||||
export function fromSeconds(s: number): TalerPreciseTimestamp {
|
||||
return {
|
||||
t_s: Math.floor(s),
|
||||
off_us: (s - Math.floor(s)) / 1000 / 1000,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TalerProtocolTimestamp {
|
||||
export function now(): TalerProtocolTimestamp {
|
||||
return AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||
return AbsoluteTime.toProtocolTimestamp(AbsoluteTime.now());
|
||||
}
|
||||
|
||||
export function zero(): TalerProtocolTimestamp {
|
||||
@ -81,6 +102,7 @@ export namespace TalerProtocolTimestamp {
|
||||
t_s: s,
|
||||
};
|
||||
}
|
||||
|
||||
export function min(
|
||||
t1: TalerProtocolTimestamp,
|
||||
t2: TalerProtocolTimestamp,
|
||||
@ -309,7 +331,7 @@ export namespace AbsoluteTime {
|
||||
return cmp(t, now()) <= 0;
|
||||
}
|
||||
|
||||
export function fromTimestamp(t: TalerProtocolTimestamp): AbsoluteTime {
|
||||
export function fromProtocolTimestamp(t: TalerProtocolTimestamp): AbsoluteTime {
|
||||
if (t.t_s === "never") {
|
||||
return { t_ms: "never" };
|
||||
}
|
||||
@ -318,7 +340,31 @@ export namespace AbsoluteTime {
|
||||
};
|
||||
}
|
||||
|
||||
export function toTimestamp(at: AbsoluteTime): TalerProtocolTimestamp {
|
||||
export function fromPreciseTimestamp(t: TalerPreciseTimestamp): AbsoluteTime {
|
||||
if (t.t_s === "never") {
|
||||
return { t_ms: "never" };
|
||||
}
|
||||
const offsetUs = t.off_us ?? 0;
|
||||
return {
|
||||
t_ms: t.t_s * 1000 + offsetUs / 1000,
|
||||
};
|
||||
}
|
||||
|
||||
export function toPreciseTimestamp(at: AbsoluteTime): TalerPreciseTimestamp {
|
||||
if (at.t_ms == "never") {
|
||||
return {
|
||||
t_s: "never",
|
||||
};
|
||||
}
|
||||
const t_s = Math.floor(at.t_ms / 1000);
|
||||
const off_us = Math.floor(1000 * (at.t_ms - t_s * 1000));
|
||||
return {
|
||||
t_s,
|
||||
off_us,
|
||||
}
|
||||
}
|
||||
|
||||
export function toProtocolTimestamp(at: AbsoluteTime): TalerProtocolTimestamp {
|
||||
if (at.t_ms === "never") {
|
||||
return { t_s: "never" };
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { TalerProtocolTimestamp } from "./time.js";
|
||||
import { TalerPreciseTimestamp, TalerProtocolTimestamp } from "./time.js";
|
||||
import {
|
||||
AmountString,
|
||||
Product,
|
||||
@ -141,7 +141,7 @@ export interface TransactionCommon {
|
||||
type: TransactionType;
|
||||
|
||||
// main timestamp of the transaction
|
||||
timestamp: TalerProtocolTimestamp;
|
||||
timestamp: TalerPreciseTimestamp;
|
||||
|
||||
/**
|
||||
* Transaction state, as per DD37.
|
||||
|
@ -1112,7 +1112,7 @@ peerCli
|
||||
partialContractTerms: {
|
||||
amount: args.initiatePayPull.amount,
|
||||
summary: args.initiatePayPull.summary ?? "Invoice",
|
||||
purse_expiration: AbsoluteTime.toTimestamp(purseExpiration),
|
||||
purse_expiration: AbsoluteTime.toProtocolTimestamp(purseExpiration),
|
||||
},
|
||||
},
|
||||
);
|
||||
@ -1168,7 +1168,7 @@ peerCli
|
||||
partialContractTerms: {
|
||||
amount: args.payPush.amount,
|
||||
summary: args.payPush.summary ?? "Payment",
|
||||
purse_expiration: AbsoluteTime.toTimestamp(purseExpiration),
|
||||
purse_expiration: AbsoluteTime.toProtocolTimestamp(purseExpiration),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
@ -14,7 +14,7 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { AbsoluteTime } from "@gnu-taler/taler-util";
|
||||
import { AbsoluteTime, TalerErrorCode } from "@gnu-taler/taler-util";
|
||||
import test from "ava";
|
||||
import { CryptoDispatcher, CryptoWorkerFactory } from "./crypto-dispatcher.js";
|
||||
import {
|
||||
@ -72,7 +72,7 @@ export class MyCryptoWorker implements CryptoWorker {
|
||||
id: msg.id,
|
||||
type: "error",
|
||||
error: {
|
||||
code: 42,
|
||||
code: TalerErrorCode.ANASTASIS_EMAIL_INVALID,
|
||||
when: AbsoluteTime.now(),
|
||||
hint: "bla",
|
||||
},
|
||||
|
@ -57,6 +57,7 @@ import {
|
||||
AttentionInfo,
|
||||
Logger,
|
||||
CoinPublicKeyString,
|
||||
TalerPreciseTimestamp,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import {
|
||||
DbAccess,
|
||||
@ -236,14 +237,14 @@ export interface ReserveBankInfo {
|
||||
*
|
||||
* Set to undefined if that hasn't happened yet.
|
||||
*/
|
||||
timestampReserveInfoPosted: TalerProtocolTimestamp | undefined;
|
||||
timestampReserveInfoPosted: TalerPreciseTimestamp | undefined;
|
||||
|
||||
/**
|
||||
* Time when the reserve was confirmed by the bank.
|
||||
*
|
||||
* Set to undefined if not confirmed yet.
|
||||
*/
|
||||
timestampBankConfirmed: TalerProtocolTimestamp | undefined;
|
||||
timestampBankConfirmed: TalerPreciseTimestamp | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -516,7 +517,7 @@ export interface ExchangeDetailsRecord {
|
||||
tosAccepted:
|
||||
| {
|
||||
etag: string;
|
||||
timestamp: TalerProtocolTimestamp;
|
||||
timestamp: TalerPreciseTimestamp;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
@ -556,7 +557,7 @@ export interface ExchangeDetailsPointer {
|
||||
* Timestamp when the (masterPublicKey, currency) pointer
|
||||
* has been updated.
|
||||
*/
|
||||
updateClock: TalerProtocolTimestamp;
|
||||
updateClock: TalerPreciseTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -573,7 +574,7 @@ export interface ExchangeRecord {
|
||||
*
|
||||
* Used mostly in the UI to suggest exchanges.
|
||||
*/
|
||||
lastWithdrawal?: TalerProtocolTimestamp;
|
||||
lastWithdrawal?: TalerPreciseTimestamp;
|
||||
|
||||
/**
|
||||
* Pointer to the current exchange details.
|
||||
@ -595,14 +596,14 @@ export interface ExchangeRecord {
|
||||
/**
|
||||
* Last time when the exchange was updated (both /keys and /wire).
|
||||
*/
|
||||
lastUpdate: TalerProtocolTimestamp | undefined;
|
||||
lastUpdate: TalerPreciseTimestamp | undefined;
|
||||
|
||||
/**
|
||||
* Next scheduled update for the exchange.
|
||||
*
|
||||
* (This field must always be present, so we can index on the timestamp.)
|
||||
*/
|
||||
nextUpdate: TalerProtocolTimestamp;
|
||||
nextUpdate: TalerPreciseTimestamp;
|
||||
|
||||
lastKeysEtag: string | undefined;
|
||||
|
||||
@ -614,7 +615,7 @@ export interface ExchangeRecord {
|
||||
* Updated whenever the exchange's denominations are updated or when
|
||||
* the refresh check has been done.
|
||||
*/
|
||||
nextRefreshCheck: TalerProtocolTimestamp;
|
||||
nextRefreshCheck: TalerPreciseTimestamp;
|
||||
|
||||
/**
|
||||
* Public key of the reserve that we're currently using for
|
||||
@ -803,7 +804,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: TalerProtocolTimestamp | undefined;
|
||||
acceptedTimestamp: TalerPreciseTimestamp | undefined;
|
||||
|
||||
/**
|
||||
* The tipped amount.
|
||||
@ -856,7 +857,7 @@ export interface TipRecord {
|
||||
*/
|
||||
merchantTipId: string;
|
||||
|
||||
createdTimestamp: TalerProtocolTimestamp;
|
||||
createdTimestamp: TalerPreciseTimestamp;
|
||||
|
||||
/**
|
||||
* The url to be redirected after the tip is accepted.
|
||||
@ -866,7 +867,7 @@ export interface TipRecord {
|
||||
* Timestamp for when the wallet finished picking up the tip
|
||||
* from the merchant.
|
||||
*/
|
||||
pickedUpTimestamp: TalerProtocolTimestamp | undefined;
|
||||
pickedUpTimestamp: TalerPreciseTimestamp | undefined;
|
||||
}
|
||||
|
||||
export enum RefreshCoinStatus {
|
||||
@ -974,12 +975,12 @@ export interface RefreshGroupRecord {
|
||||
*/
|
||||
statusPerCoin: RefreshCoinStatus[];
|
||||
|
||||
timestampCreated: TalerProtocolTimestamp;
|
||||
timestampCreated: TalerPreciseTimestamp;
|
||||
|
||||
/**
|
||||
* Timestamp when the refresh session finished.
|
||||
*/
|
||||
timestampFinished: TalerProtocolTimestamp | undefined;
|
||||
timestampFinished: TalerPreciseTimestamp | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1233,7 +1234,7 @@ export interface PurchaseRecord {
|
||||
* Timestamp of the first time that sending a payment to the merchant
|
||||
* for this purchase was successful.
|
||||
*/
|
||||
timestampFirstSuccessfulPay: TalerProtocolTimestamp | undefined;
|
||||
timestampFirstSuccessfulPay: TalerPreciseTimestamp | undefined;
|
||||
|
||||
merchantPaySig: string | undefined;
|
||||
|
||||
@ -1242,13 +1243,13 @@ export interface PurchaseRecord {
|
||||
/**
|
||||
* When was the purchase record created?
|
||||
*/
|
||||
timestamp: TalerProtocolTimestamp;
|
||||
timestamp: TalerPreciseTimestamp;
|
||||
|
||||
/**
|
||||
* When was the purchase made?
|
||||
* Refers to the time that the user accepted.
|
||||
*/
|
||||
timestampAccept: TalerProtocolTimestamp | undefined;
|
||||
timestampAccept: TalerPreciseTimestamp | undefined;
|
||||
|
||||
/**
|
||||
* Pending refunds for the purchase. A refund is pending
|
||||
@ -1262,7 +1263,7 @@ export interface PurchaseRecord {
|
||||
* When was the last refund made?
|
||||
* Set to 0 if no refund was made on the purchase.
|
||||
*/
|
||||
timestampLastRefundStatus: TalerProtocolTimestamp | undefined;
|
||||
timestampLastRefundStatus: TalerPreciseTimestamp | undefined;
|
||||
|
||||
/**
|
||||
* Last session signature that we submitted to /pay (if any).
|
||||
@ -1312,12 +1313,12 @@ export interface WalletBackupConfState {
|
||||
/**
|
||||
* Timestamp stored in the last backup.
|
||||
*/
|
||||
lastBackupTimestamp?: TalerProtocolTimestamp;
|
||||
lastBackupTimestamp?: TalerPreciseTimestamp;
|
||||
|
||||
/**
|
||||
* Last time we tried to do a backup.
|
||||
*/
|
||||
lastBackupCheckTimestamp?: TalerProtocolTimestamp;
|
||||
lastBackupCheckTimestamp?: TalerPreciseTimestamp;
|
||||
lastBackupNonce?: string;
|
||||
}
|
||||
|
||||
@ -1421,12 +1422,12 @@ export interface WithdrawalGroupRecord {
|
||||
* When was the withdrawal operation started started?
|
||||
* Timestamp in milliseconds.
|
||||
*/
|
||||
timestampStart: TalerProtocolTimestamp;
|
||||
timestampStart: TalerPreciseTimestamp;
|
||||
|
||||
/**
|
||||
* When was the withdrawal operation completed?
|
||||
*/
|
||||
timestampFinish?: TalerProtocolTimestamp;
|
||||
timestampFinish?: TalerPreciseTimestamp;
|
||||
|
||||
/**
|
||||
* Current status of the reserve.
|
||||
@ -1517,9 +1518,9 @@ export interface RecoupGroupRecord {
|
||||
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
timestampStarted: TalerProtocolTimestamp;
|
||||
timestampStarted: TalerPreciseTimestamp;
|
||||
|
||||
timestampFinished: TalerProtocolTimestamp | undefined;
|
||||
timestampFinished: TalerPreciseTimestamp | undefined;
|
||||
|
||||
/**
|
||||
* Public keys that identify the coins being recouped
|
||||
@ -1553,7 +1554,7 @@ export type BackupProviderState =
|
||||
}
|
||||
| {
|
||||
tag: BackupProviderStateTag.Ready;
|
||||
nextBackupTimestamp: TalerProtocolTimestamp;
|
||||
nextBackupTimestamp: TalerPreciseTimestamp;
|
||||
}
|
||||
| {
|
||||
tag: BackupProviderStateTag.Retrying;
|
||||
@ -1598,7 +1599,7 @@ export interface BackupProviderRecord {
|
||||
* Does NOT correspond to the timestamp of the backup,
|
||||
* which only changes when the backup content changes.
|
||||
*/
|
||||
lastBackupCycleTimestamp?: TalerProtocolTimestamp;
|
||||
lastBackupCycleTimestamp?: TalerPreciseTimestamp;
|
||||
|
||||
/**
|
||||
* Proposal that we're currently trying to pay for.
|
||||
@ -1693,9 +1694,9 @@ export interface DepositGroupRecord {
|
||||
*/
|
||||
effectiveDepositAmount: AmountString;
|
||||
|
||||
timestampCreated: TalerProtocolTimestamp;
|
||||
timestampCreated: TalerPreciseTimestamp;
|
||||
|
||||
timestampFinished: TalerProtocolTimestamp | undefined;
|
||||
timestampFinished: TalerPreciseTimestamp | undefined;
|
||||
|
||||
operationStatus: DepositOperationStatus;
|
||||
|
||||
@ -1728,7 +1729,7 @@ export interface GhostDepositGroupRecord {
|
||||
* When multiple deposits for the same contract terms hash
|
||||
* have a different timestamp, we choose the earliest one.
|
||||
*/
|
||||
timestamp: TalerProtocolTimestamp;
|
||||
timestamp: TalerPreciseTimestamp;
|
||||
|
||||
contractTermsHash: string;
|
||||
|
||||
@ -1821,7 +1822,7 @@ export interface PeerPushPaymentInitiationRecord {
|
||||
|
||||
purseExpiration: TalerProtocolTimestamp;
|
||||
|
||||
timestampCreated: TalerProtocolTimestamp;
|
||||
timestampCreated: TalerPreciseTimestamp;
|
||||
|
||||
/**
|
||||
* Status of the peer push payment initiation.
|
||||
@ -1882,7 +1883,7 @@ export interface PeerPullPaymentInitiationRecord {
|
||||
|
||||
contractTerms: PeerContractTerms;
|
||||
|
||||
mergeTimestamp: TalerProtocolTimestamp;
|
||||
mergeTimestamp: TalerPreciseTimestamp;
|
||||
|
||||
mergeReserveRowId: number;
|
||||
|
||||
@ -1924,7 +1925,7 @@ export interface PeerPushPaymentIncomingRecord {
|
||||
|
||||
contractPriv: string;
|
||||
|
||||
timestamp: TalerProtocolTimestamp;
|
||||
timestamp: TalerPreciseTimestamp;
|
||||
|
||||
estimatedAmountEffective: AmountString;
|
||||
|
||||
@ -1984,7 +1985,7 @@ export interface PeerPullPaymentIncomingRecord {
|
||||
|
||||
contractTerms: PeerContractTerms;
|
||||
|
||||
timestampCreated: TalerProtocolTimestamp;
|
||||
timestampCreated: TalerPreciseTimestamp;
|
||||
|
||||
/**
|
||||
* Contract priv that we got from the other party.
|
||||
@ -2081,7 +2082,7 @@ export interface UserAttentionRecord {
|
||||
/**
|
||||
* When the user mark this notification as read.
|
||||
*/
|
||||
read: TalerProtocolTimestamp | undefined;
|
||||
read: TalerPreciseTimestamp | undefined;
|
||||
}
|
||||
|
||||
export interface DbExchangeHandle {
|
||||
@ -2125,7 +2126,7 @@ export interface RefundGroupRecord {
|
||||
/**
|
||||
* Timestamp when the refund group was created.
|
||||
*/
|
||||
timestampCreated: TalerProtocolTimestamp;
|
||||
timestampCreated: TalerPreciseTimestamp;
|
||||
|
||||
proposalId: string;
|
||||
|
||||
@ -2180,7 +2181,7 @@ export interface RefundItemRecord {
|
||||
/**
|
||||
* Time when the wallet became aware of the refund.
|
||||
*/
|
||||
obtainedTime: TalerProtocolTimestamp;
|
||||
obtainedTime: TalerPreciseTimestamp;
|
||||
|
||||
refundAmount: AmountString;
|
||||
|
||||
|
@ -256,7 +256,7 @@ export async function depositCoin(args: {
|
||||
const depositPayto =
|
||||
args.depositPayto ?? "payto://x-taler-bank/localhost/foo";
|
||||
const wireSalt = args.wireSalt ?? encodeCrock(getRandomBytes(16));
|
||||
const timestampNow = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||
const timestampNow = AbsoluteTime.toProtocolTimestamp(AbsoluteTime.now());
|
||||
const contractTermsHash =
|
||||
args.contractTermsHash ?? encodeCrock(getRandomBytes(64));
|
||||
const depositTimestamp = timestampNow;
|
||||
|
@ -22,6 +22,7 @@ import {
|
||||
AttentionInfo,
|
||||
Logger,
|
||||
TalerProtocolTimestamp,
|
||||
TalerPreciseTimestamp,
|
||||
UserAttentionByIdRequest,
|
||||
UserAttentionPriority,
|
||||
UserAttentionsCountResponse,
|
||||
@ -95,7 +96,7 @@ export async function markAttentionRequestAsRead(
|
||||
if (!ua) throw Error("attention request not found");
|
||||
tx.userAttention.put({
|
||||
...ua,
|
||||
read: TalerProtocolTimestamp.now(),
|
||||
read: TalerPreciseTimestamp.now(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ import {
|
||||
Logger,
|
||||
stringToBytes,
|
||||
WalletBackupContentV1,
|
||||
TalerPreciseTimestamp,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import {
|
||||
CoinSourceType,
|
||||
@ -521,7 +522,7 @@ export async function exportBackup(
|
||||
});
|
||||
});
|
||||
|
||||
const ts = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||
const ts = TalerPreciseTimestamp.now();
|
||||
|
||||
if (!bs.lastBackupTimestamp) {
|
||||
bs.lastBackupTimestamp = ts;
|
||||
@ -565,7 +566,7 @@ export async function exportBackup(
|
||||
bs.lastBackupNonce = encodeCrock(getRandomBytes(32));
|
||||
logger.trace(
|
||||
`setting timestamp to ${AbsoluteTime.toIsoString(
|
||||
AbsoluteTime.fromTimestamp(ts),
|
||||
AbsoluteTime.fromPreciseTimestamp(ts),
|
||||
)} and nonce to ${bs.lastBackupNonce}`,
|
||||
);
|
||||
await tx.config.put({
|
||||
|
@ -35,6 +35,7 @@ import {
|
||||
PayCoinSelection,
|
||||
RefreshReason,
|
||||
TalerProtocolTimestamp,
|
||||
TalerPreciseTimestamp,
|
||||
WalletBackupContentV1,
|
||||
WireInfo,
|
||||
} from "@gnu-taler/taler-util";
|
||||
@ -349,8 +350,8 @@ export async function importBackup(
|
||||
},
|
||||
permanent: true,
|
||||
lastUpdate: undefined,
|
||||
nextUpdate: TalerProtocolTimestamp.now(),
|
||||
nextRefreshCheck: TalerProtocolTimestamp.now(),
|
||||
nextUpdate: TalerPreciseTimestamp.now(),
|
||||
nextRefreshCheck: TalerPreciseTimestamp.now(),
|
||||
lastKeysEtag: undefined,
|
||||
lastWireEtag: undefined,
|
||||
});
|
||||
|
@ -71,6 +71,7 @@ import {
|
||||
TalerErrorCode,
|
||||
TalerErrorDetail,
|
||||
TalerProtocolTimestamp,
|
||||
TalerPreciseTimestamp,
|
||||
URL,
|
||||
WalletBackupContentV1,
|
||||
} from "@gnu-taler/taler-util";
|
||||
@ -246,9 +247,9 @@ interface BackupForProviderArgs {
|
||||
backupProviderBaseUrl: string;
|
||||
}
|
||||
|
||||
function getNextBackupTimestamp(): TalerProtocolTimestamp {
|
||||
function getNextBackupTimestamp(): TalerPreciseTimestamp {
|
||||
// FIXME: Randomize!
|
||||
return AbsoluteTime.toTimestamp(
|
||||
return AbsoluteTime.toPreciseTimestamp(
|
||||
AbsoluteTime.addDuration(
|
||||
AbsoluteTime.now(),
|
||||
durationFromSpec({ minutes: 5 }),
|
||||
@ -329,7 +330,7 @@ async function runBackupCycleForProvider(
|
||||
if (!prov) {
|
||||
return;
|
||||
}
|
||||
prov.lastBackupCycleTimestamp = TalerProtocolTimestamp.now();
|
||||
prov.lastBackupCycleTimestamp = TalerPreciseTimestamp.now();
|
||||
prov.state = {
|
||||
tag: BackupProviderStateTag.Ready,
|
||||
nextBackupTimestamp: getNextBackupTimestamp(),
|
||||
@ -442,7 +443,7 @@ async function runBackupCycleForProvider(
|
||||
return;
|
||||
}
|
||||
prov.lastBackupHash = encodeCrock(currentBackupHash);
|
||||
prov.lastBackupCycleTimestamp = TalerProtocolTimestamp.now();
|
||||
prov.lastBackupCycleTimestamp = TalerPreciseTimestamp.now();
|
||||
prov.state = {
|
||||
tag: BackupProviderStateTag.Ready,
|
||||
nextBackupTimestamp: getNextBackupTimestamp(),
|
||||
@ -676,7 +677,7 @@ export async function addBackupProvider(
|
||||
if (req.activate) {
|
||||
oldProv.state = {
|
||||
tag: BackupProviderStateTag.Ready,
|
||||
nextBackupTimestamp: TalerProtocolTimestamp.now(),
|
||||
nextBackupTimestamp: TalerPreciseTimestamp.now(),
|
||||
};
|
||||
logger.info("setting existing backup provider to active");
|
||||
await tx.backupProviders.put(oldProv);
|
||||
@ -698,7 +699,7 @@ export async function addBackupProvider(
|
||||
if (req.activate) {
|
||||
state = {
|
||||
tag: BackupProviderStateTag.Ready,
|
||||
nextBackupTimestamp: TalerProtocolTimestamp.now(),
|
||||
nextBackupTimestamp: TalerPreciseTimestamp.now(),
|
||||
};
|
||||
} else {
|
||||
state = {
|
||||
@ -773,8 +774,8 @@ export interface ProviderInfo {
|
||||
* Last communication issue with the provider.
|
||||
*/
|
||||
lastError?: TalerErrorDetail;
|
||||
lastSuccessfulBackupTimestamp?: TalerProtocolTimestamp;
|
||||
lastAttemptedBackupTimestamp?: TalerProtocolTimestamp;
|
||||
lastSuccessfulBackupTimestamp?: TalerPreciseTimestamp;
|
||||
lastAttemptedBackupTimestamp?: TalerPreciseTimestamp;
|
||||
paymentProposalIds: string[];
|
||||
backupProblem?: BackupProblem;
|
||||
paymentStatus: ProviderPaymentStatus;
|
||||
@ -894,7 +895,7 @@ async function getProviderPaymentInfo(
|
||||
return {
|
||||
type: ProviderPaymentType.Paid,
|
||||
paidUntil: AbsoluteTime.addDuration(
|
||||
AbsoluteTime.fromTimestamp(status.contractTerms.timestamp),
|
||||
AbsoluteTime.fromProtocolTimestamp(status.contractTerms.timestamp),
|
||||
durationFromSpec({ years: 1 }), //FIXME: take this from the contract term
|
||||
),
|
||||
};
|
||||
@ -1010,7 +1011,7 @@ async function backupRecoveryTheirs(
|
||||
shouldRetryFreshProposal: false,
|
||||
state: {
|
||||
tag: BackupProviderStateTag.Ready,
|
||||
nextBackupTimestamp: TalerProtocolTimestamp.now(),
|
||||
nextBackupTimestamp: TalerPreciseTimestamp.now(),
|
||||
},
|
||||
uids: [encodeCrock(getRandomBytes(32))],
|
||||
});
|
||||
|
@ -51,6 +51,7 @@ import {
|
||||
stringToBytes,
|
||||
TalerErrorCode,
|
||||
TalerProtocolTimestamp,
|
||||
TalerPreciseTimestamp,
|
||||
TrackTransaction,
|
||||
TransactionMajorState,
|
||||
TransactionMinorState,
|
||||
@ -779,7 +780,7 @@ export async function processDepositGroup(
|
||||
}
|
||||
}
|
||||
if (allDepositedAndWired) {
|
||||
dg.timestampFinished = TalerProtocolTimestamp.now();
|
||||
dg.timestampFinished = TalerPreciseTimestamp.now();
|
||||
dg.operationStatus = DepositOperationStatus.Finished;
|
||||
await tx.depositGroups.put(dg);
|
||||
}
|
||||
@ -858,9 +859,9 @@ async function getExchangeWireFee(
|
||||
}
|
||||
const fee = fees.find((x) => {
|
||||
return AbsoluteTime.isBetween(
|
||||
AbsoluteTime.fromTimestamp(time),
|
||||
AbsoluteTime.fromTimestamp(x.startStamp),
|
||||
AbsoluteTime.fromTimestamp(x.endStamp),
|
||||
AbsoluteTime.fromProtocolTimestamp(time),
|
||||
AbsoluteTime.fromProtocolTimestamp(x.startStamp),
|
||||
AbsoluteTime.fromProtocolTimestamp(x.endStamp),
|
||||
);
|
||||
});
|
||||
if (!fee) {
|
||||
@ -952,7 +953,7 @@ export async function prepareDepositGroup(
|
||||
});
|
||||
|
||||
const now = AbsoluteTime.now();
|
||||
const nowRounded = AbsoluteTime.toTimestamp(now);
|
||||
const nowRounded = AbsoluteTime.toProtocolTimestamp(now);
|
||||
const contractTerms: MerchantContractTerms = {
|
||||
exchanges: exchangeInfos,
|
||||
amount: req.amount,
|
||||
@ -966,7 +967,7 @@ export async function prepareDepositGroup(
|
||||
wire_transfer_deadline: nowRounded,
|
||||
order_id: "",
|
||||
h_wire: "",
|
||||
pay_deadline: AbsoluteTime.toTimestamp(
|
||||
pay_deadline: AbsoluteTime.toProtocolTimestamp(
|
||||
AbsoluteTime.addDuration(now, durationFromSpec({ hours: 1 })),
|
||||
),
|
||||
merchant: {
|
||||
@ -1066,7 +1067,7 @@ export async function createDepositGroup(
|
||||
});
|
||||
|
||||
const now = AbsoluteTime.now();
|
||||
const nowRounded = AbsoluteTime.toTimestamp(now);
|
||||
const nowRounded = AbsoluteTime.toProtocolTimestamp(now);
|
||||
const noncePair = await ws.cryptoApi.createEddsaKeypair({});
|
||||
const merchantPair = await ws.cryptoApi.createEddsaKeypair({});
|
||||
const wireSalt = encodeCrock(getRandomBytes(16));
|
||||
@ -1084,7 +1085,7 @@ export async function createDepositGroup(
|
||||
wire_transfer_deadline: nowRounded,
|
||||
order_id: "",
|
||||
h_wire: wireHash,
|
||||
pay_deadline: AbsoluteTime.toTimestamp(
|
||||
pay_deadline: AbsoluteTime.toProtocolTimestamp(
|
||||
AbsoluteTime.addDuration(now, durationFromSpec({ hours: 1 })),
|
||||
),
|
||||
merchant: {
|
||||
@ -1150,7 +1151,7 @@ export async function createDepositGroup(
|
||||
depositGroupId,
|
||||
noncePriv: noncePair.priv,
|
||||
noncePub: noncePair.pub,
|
||||
timestampCreated: AbsoluteTime.toTimestamp(now),
|
||||
timestampCreated: AbsoluteTime.toPreciseTimestamp(now),
|
||||
timestampFinished: undefined,
|
||||
transactionPerCoin: payCoinSel.coinSel.coinPubs.map(
|
||||
() => DepositElementStatus.Unknown,
|
||||
@ -1260,8 +1261,8 @@ export async function getCounterpartyEffectiveDepositAmount(
|
||||
const fee = exchangeDetails.wireInfo.feesForType[wireType].find((x) => {
|
||||
return AbsoluteTime.isBetween(
|
||||
AbsoluteTime.now(),
|
||||
AbsoluteTime.fromTimestamp(x.startStamp),
|
||||
AbsoluteTime.fromTimestamp(x.endStamp),
|
||||
AbsoluteTime.fromProtocolTimestamp(x.startStamp),
|
||||
AbsoluteTime.fromProtocolTimestamp(x.endStamp),
|
||||
);
|
||||
})?.wireFee;
|
||||
if (fee) {
|
||||
@ -1337,8 +1338,8 @@ export async function getTotalFeesForDepositAmount(
|
||||
(x) => {
|
||||
return AbsoluteTime.isBetween(
|
||||
AbsoluteTime.now(),
|
||||
AbsoluteTime.fromTimestamp(x.startStamp),
|
||||
AbsoluteTime.fromTimestamp(x.endStamp),
|
||||
AbsoluteTime.fromProtocolTimestamp(x.startStamp),
|
||||
AbsoluteTime.fromProtocolTimestamp(x.endStamp),
|
||||
);
|
||||
},
|
||||
)?.wireFee;
|
||||
|
@ -43,6 +43,7 @@ import {
|
||||
Recoup,
|
||||
TalerError,
|
||||
TalerErrorCode,
|
||||
TalerPreciseTimestamp,
|
||||
TalerProtocolDuration,
|
||||
TalerProtocolTimestamp,
|
||||
URL,
|
||||
@ -229,7 +230,7 @@ export async function acceptExchangeTermsOfService(
|
||||
if (d) {
|
||||
d.tosAccepted = {
|
||||
etag: etag || d.tosCurrentEtag,
|
||||
timestamp: TalerProtocolTimestamp.now(),
|
||||
timestamp: TalerPreciseTimestamp.now(),
|
||||
};
|
||||
await tx.exchangeDetails.put(d);
|
||||
}
|
||||
@ -407,8 +408,8 @@ export async function provideExchangeRecordInTx(
|
||||
baseUrl: baseUrl,
|
||||
detailsPointer: undefined,
|
||||
lastUpdate: undefined,
|
||||
nextUpdate: AbsoluteTime.toTimestamp(now),
|
||||
nextRefreshCheck: AbsoluteTime.toTimestamp(now),
|
||||
nextUpdate: AbsoluteTime.toPreciseTimestamp(now),
|
||||
nextRefreshCheck: AbsoluteTime.toPreciseTimestamp(now),
|
||||
lastKeysEtag: undefined,
|
||||
lastWireEtag: undefined,
|
||||
};
|
||||
@ -497,7 +498,7 @@ async function downloadExchangeKeysInfo(
|
||||
protocolVersion: exchangeKeysJsonUnchecked.version,
|
||||
signingKeys: exchangeKeysJsonUnchecked.signkeys,
|
||||
reserveClosingDelay: exchangeKeysJsonUnchecked.reserve_closing_delay,
|
||||
expiry: AbsoluteTime.toTimestamp(
|
||||
expiry: AbsoluteTime.toProtocolTimestamp(
|
||||
getExpiry(resp, {
|
||||
minDuration: durationFromSpec({ hours: 1 }),
|
||||
}),
|
||||
@ -601,7 +602,9 @@ export async function updateExchangeFromUrlHandler(
|
||||
if (
|
||||
!forceNow &&
|
||||
exchangeDetails !== undefined &&
|
||||
!AbsoluteTime.isExpired(AbsoluteTime.fromTimestamp(exchange.nextUpdate))
|
||||
!AbsoluteTime.isExpired(
|
||||
AbsoluteTime.fromPreciseTimestamp(exchange.nextUpdate),
|
||||
)
|
||||
) {
|
||||
logger.info("using existing exchange info");
|
||||
return {
|
||||
@ -724,15 +727,17 @@ export async function updateExchangeFromUrlHandler(
|
||||
if (existingDetails?.rowId) {
|
||||
newDetails.rowId = existingDetails.rowId;
|
||||
}
|
||||
r.lastUpdate = TalerProtocolTimestamp.now();
|
||||
r.nextUpdate = keysInfo.expiry;
|
||||
r.lastUpdate = TalerPreciseTimestamp.now();
|
||||
r.nextUpdate = AbsoluteTime.toPreciseTimestamp(
|
||||
AbsoluteTime.fromProtocolTimestamp(keysInfo.expiry),
|
||||
);
|
||||
// New denominations might be available.
|
||||
r.nextRefreshCheck = TalerProtocolTimestamp.now();
|
||||
r.nextRefreshCheck = TalerPreciseTimestamp.now();
|
||||
if (detailsPointerChanged) {
|
||||
r.detailsPointer = {
|
||||
currency: newDetails.currency,
|
||||
masterPublicKey: newDetails.masterPublicKey,
|
||||
updateClock: TalerProtocolTimestamp.now(),
|
||||
updateClock: TalerPreciseTimestamp.now(),
|
||||
};
|
||||
}
|
||||
await tx.exchanges.put(r);
|
||||
|
@ -66,6 +66,7 @@ import {
|
||||
TalerError,
|
||||
TalerErrorCode,
|
||||
TalerErrorDetail,
|
||||
TalerPreciseTimestamp,
|
||||
TalerProtocolTimestamp,
|
||||
TalerProtocolViolationError,
|
||||
TalerUriAction,
|
||||
@ -621,7 +622,7 @@ async function createPurchase(
|
||||
noncePriv: priv,
|
||||
noncePub: pub,
|
||||
claimToken,
|
||||
timestamp: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
|
||||
timestamp: TalerPreciseTimestamp.now(),
|
||||
merchantBaseUrl,
|
||||
orderId,
|
||||
proposalId: proposalId,
|
||||
@ -682,7 +683,7 @@ async function storeFirstPaySuccess(
|
||||
tag: TransactionType.Payment,
|
||||
proposalId,
|
||||
});
|
||||
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||
const now = AbsoluteTime.toPreciseTimestamp(AbsoluteTime.now());
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.purchases, x.contractTerms])
|
||||
.runReadWrite(async (tx) => {
|
||||
@ -721,7 +722,7 @@ async function storeFirstPaySuccess(
|
||||
const ar = Duration.fromTalerProtocolDuration(protoAr);
|
||||
logger.info("auto_refund present");
|
||||
purchase.purchaseStatus = PurchaseStatus.QueryingAutoRefund;
|
||||
purchase.autoRefundDeadline = AbsoluteTime.toTimestamp(
|
||||
purchase.autoRefundDeadline = AbsoluteTime.toProtocolTimestamp(
|
||||
AbsoluteTime.addDuration(AbsoluteTime.now(), ar),
|
||||
);
|
||||
}
|
||||
@ -923,7 +924,7 @@ async function unblockBackup(
|
||||
.forEachAsync(async (bp) => {
|
||||
bp.state = {
|
||||
tag: BackupProviderStateTag.Ready,
|
||||
nextBackupTimestamp: TalerProtocolTimestamp.now(),
|
||||
nextBackupTimestamp: TalerPreciseTimestamp.now(),
|
||||
};
|
||||
tx.backupProviders.put(bp);
|
||||
});
|
||||
@ -1422,7 +1423,7 @@ export async function confirmPay(
|
||||
totalPayCost: Amounts.stringify(payCostInfo),
|
||||
};
|
||||
p.lastSessionId = sessionId;
|
||||
p.timestampAccept = TalerProtocolTimestamp.now();
|
||||
p.timestampAccept = TalerPreciseTimestamp.now();
|
||||
p.purchaseStatus = PurchaseStatus.Paying;
|
||||
await tx.purchases.put(p);
|
||||
await spendCoins(ws, tx, {
|
||||
@ -1925,7 +1926,7 @@ async function processPurchaseAutoRefund(
|
||||
if (
|
||||
!purchase.autoRefundDeadline ||
|
||||
AbsoluteTime.isExpired(
|
||||
AbsoluteTime.fromTimestamp(purchase.autoRefundDeadline),
|
||||
AbsoluteTime.fromProtocolTimestamp(purchase.autoRefundDeadline),
|
||||
)
|
||||
) {
|
||||
const transitionInfo = await ws.db
|
||||
@ -2068,9 +2069,9 @@ async function processPurchaseAbortingRefund(
|
||||
coin_pub: payCoinSelection.coinPubs[i],
|
||||
refund_amount: Amounts.stringify(payCoinSelection.coinContributions[i]),
|
||||
rtransaction_id: 0,
|
||||
execution_time: AbsoluteTime.toTimestamp(
|
||||
execution_time: AbsoluteTime.toProtocolTimestamp(
|
||||
AbsoluteTime.addDuration(
|
||||
AbsoluteTime.fromTimestamp(download.contractData.timestamp),
|
||||
AbsoluteTime.fromProtocolTimestamp(download.contractData.timestamp),
|
||||
Duration.fromSpec({ seconds: 1 }),
|
||||
),
|
||||
),
|
||||
@ -2267,7 +2268,7 @@ async function storeRefunds(
|
||||
});
|
||||
|
||||
const newRefundGroupId = encodeCrock(randomBytes(32));
|
||||
const now = TalerProtocolTimestamp.now();
|
||||
const now = TalerPreciseTimestamp.now();
|
||||
|
||||
const download = await expectProposalDownload(ws, purchase);
|
||||
const currency = Amounts.currencyOf(download.contractData.amount);
|
||||
|
@ -78,6 +78,7 @@ import {
|
||||
TransactionState,
|
||||
TransactionMajorState,
|
||||
TransactionMinorState,
|
||||
TalerPreciseTimestamp,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { SpendCoinDetails } from "../crypto/cryptoImplementation.js";
|
||||
import {
|
||||
@ -728,7 +729,7 @@ export async function initiatePeerPushDebit(
|
||||
purseExpiration: purseExpiration,
|
||||
pursePriv: pursePair.priv,
|
||||
pursePub: pursePair.pub,
|
||||
timestampCreated: TalerProtocolTimestamp.now(),
|
||||
timestampCreated: TalerPreciseTimestamp.now(),
|
||||
status: PeerPushPaymentInitiationStatus.PendingCreatePurse,
|
||||
contractTerms: contractTerms,
|
||||
coinSel: {
|
||||
@ -889,7 +890,7 @@ export async function preparePeerPushCredit(
|
||||
exchangeBaseUrl: exchangeBaseUrl,
|
||||
mergePriv: dec.mergePriv,
|
||||
pursePub: pursePub,
|
||||
timestamp: TalerProtocolTimestamp.now(),
|
||||
timestamp: TalerPreciseTimestamp.now(),
|
||||
contractTermsHash,
|
||||
status: PeerPushPaymentIncomingStatus.Proposed,
|
||||
withdrawalGroupId,
|
||||
@ -1450,7 +1451,7 @@ export async function preparePeerPullDebit(
|
||||
contractPriv: contractPriv,
|
||||
exchangeBaseUrl: exchangeBaseUrl,
|
||||
pursePub: pursePub,
|
||||
timestampCreated: TalerProtocolTimestamp.now(),
|
||||
timestampCreated: TalerPreciseTimestamp.now(),
|
||||
contractTerms,
|
||||
status: PeerPullPaymentIncomingStatus.Proposed,
|
||||
totalCostEstimated: Amounts.stringify(totalAmount),
|
||||
@ -1668,7 +1669,7 @@ export async function processPeerPullCredit(
|
||||
contractTermsHash: pullIni.contractTermsHash,
|
||||
flags: WalletAccountMergeFlags.CreateWithPurseFee,
|
||||
mergePriv: pullIni.mergePriv,
|
||||
mergeTimestamp: pullIni.mergeTimestamp,
|
||||
mergeTimestamp: TalerPreciseTimestamp.round(pullIni.mergeTimestamp),
|
||||
purseAmount: pullIni.contractTerms.amount,
|
||||
purseExpiration: purseExpiration,
|
||||
purseFee: purseFee,
|
||||
@ -1680,7 +1681,7 @@ export async function processPeerPullCredit(
|
||||
|
||||
const reservePurseReqBody: ExchangeReservePurseRequest = {
|
||||
merge_sig: sigRes.mergeSig,
|
||||
merge_timestamp: pullIni.mergeTimestamp,
|
||||
merge_timestamp: TalerPreciseTimestamp.round(pullIni.mergeTimestamp),
|
||||
h_contract_terms: pullIni.contractTermsHash,
|
||||
merge_pub: pullIni.mergePub,
|
||||
min_age: 0,
|
||||
@ -1788,8 +1789,8 @@ async function getPreferredExchangeForCurrency(
|
||||
if (candidate.lastWithdrawal && e.lastWithdrawal) {
|
||||
if (
|
||||
AbsoluteTime.cmp(
|
||||
AbsoluteTime.fromTimestamp(e.lastWithdrawal),
|
||||
AbsoluteTime.fromTimestamp(candidate.lastWithdrawal),
|
||||
AbsoluteTime.fromPreciseTimestamp(e.lastWithdrawal),
|
||||
AbsoluteTime.fromPreciseTimestamp(candidate.lastWithdrawal),
|
||||
) > 0
|
||||
) {
|
||||
candidate = e;
|
||||
@ -1874,7 +1875,7 @@ export async function initiatePeerPullPayment(
|
||||
exchangeBaseUrl: exchangeBaseUrl,
|
||||
});
|
||||
|
||||
const mergeTimestamp = TalerProtocolTimestamp.now();
|
||||
const mergeTimestamp = TalerPreciseTimestamp.now();
|
||||
|
||||
const pursePair = await ws.cryptoApi.createEddsaKeypair({});
|
||||
const mergePair = await ws.cryptoApi.createEddsaKeypair({});
|
||||
|
@ -79,7 +79,7 @@ async function gatherExchangePending(
|
||||
const opTag = TaskIdentifiers.forExchangeUpdate(exch);
|
||||
let opr = await tx.operationRetries.get(opTag);
|
||||
const timestampDue =
|
||||
opr?.retryInfo.nextRetry ?? AbsoluteTime.fromTimestamp(exch.nextUpdate);
|
||||
opr?.retryInfo.nextRetry ?? AbsoluteTime.fromPreciseTimestamp(exch.nextUpdate);
|
||||
resp.pendingOperations.push({
|
||||
type: PendingTaskType.ExchangeUpdate,
|
||||
...getPendingCommon(ws, opTag, timestampDue),
|
||||
@ -94,7 +94,7 @@ async function gatherExchangePending(
|
||||
resp.pendingOperations.push({
|
||||
type: PendingTaskType.ExchangeCheckRefresh,
|
||||
...getPendingCommon(ws, opTag, timestampDue),
|
||||
timestampDue: AbsoluteTime.fromTimestamp(exch.nextRefreshCheck),
|
||||
timestampDue: AbsoluteTime.fromPreciseTimestamp(exch.nextRefreshCheck),
|
||||
givesLifeness: false,
|
||||
exchangeBaseUrl: exch.baseUrl,
|
||||
});
|
||||
@ -333,7 +333,7 @@ async function gatherBackupPending(
|
||||
const opId = TaskIdentifiers.forBackup(bp);
|
||||
const retryRecord = await tx.operationRetries.get(opId);
|
||||
if (bp.state.tag === BackupProviderStateTag.Ready) {
|
||||
const timestampDue = AbsoluteTime.fromTimestamp(
|
||||
const timestampDue = AbsoluteTime.fromPreciseTimestamp(
|
||||
bp.state.nextBackupTimestamp,
|
||||
);
|
||||
resp.pendingOperations.push({
|
||||
|
@ -36,6 +36,7 @@ import {
|
||||
NotificationType,
|
||||
RefreshReason,
|
||||
TalerProtocolTimestamp,
|
||||
TalerPreciseTimestamp,
|
||||
URL,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import {
|
||||
@ -424,7 +425,7 @@ export async function processRecoupGroupHandler(
|
||||
if (!rg2) {
|
||||
return;
|
||||
}
|
||||
rg2.timestampFinished = TalerProtocolTimestamp.now();
|
||||
rg2.timestampFinished = TalerPreciseTimestamp.now();
|
||||
if (rg2.scheduleRefreshCoins.length > 0) {
|
||||
const refreshGroupId = await createRefreshGroup(
|
||||
ws,
|
||||
@ -460,7 +461,7 @@ export async function createRecoupGroup(
|
||||
exchangeBaseUrl: exchangeBaseUrl,
|
||||
coinPubs: coinPubs,
|
||||
timestampFinished: undefined,
|
||||
timestampStarted: TalerProtocolTimestamp.now(),
|
||||
timestampStarted: TalerPreciseTimestamp.now(),
|
||||
recoupFinishedPerCoin: coinPubs.map(() => false),
|
||||
scheduleRefreshCoins: [],
|
||||
};
|
||||
|
@ -48,6 +48,7 @@ import {
|
||||
RefreshReason,
|
||||
TalerErrorCode,
|
||||
TalerErrorDetail,
|
||||
TalerPreciseTimestamp,
|
||||
TalerProtocolTimestamp,
|
||||
TransactionMajorState,
|
||||
TransactionState,
|
||||
@ -156,10 +157,10 @@ function updateGroupStatus(rg: RefreshGroupRecord): void {
|
||||
);
|
||||
if (allDone) {
|
||||
if (anyFrozen) {
|
||||
rg.timestampFinished = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||
rg.timestampFinished = TalerPreciseTimestamp.now();
|
||||
rg.operationStatus = RefreshOperationStatus.FinishedWithError;
|
||||
} else {
|
||||
rg.timestampFinished = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||
rg.timestampFinished = TalerPreciseTimestamp.now();
|
||||
rg.operationStatus = RefreshOperationStatus.Finished;
|
||||
}
|
||||
}
|
||||
@ -1046,12 +1047,12 @@ export async function createRefreshGroup(
|
||||
estimatedOutputPerCoin: estimatedOutputPerCoin.map((x) =>
|
||||
Amounts.stringify(x),
|
||||
),
|
||||
timestampCreated: TalerProtocolTimestamp.now(),
|
||||
timestampCreated: TalerPreciseTimestamp.now(),
|
||||
};
|
||||
|
||||
if (oldCoinPubs.length == 0) {
|
||||
logger.warn("created refresh group with zero coins");
|
||||
refreshGroup.timestampFinished = TalerProtocolTimestamp.now();
|
||||
refreshGroup.timestampFinished = TalerPreciseTimestamp.now();
|
||||
refreshGroup.operationStatus = RefreshOperationStatus.Finished;
|
||||
}
|
||||
|
||||
@ -1075,8 +1076,8 @@ export async function createRefreshGroup(
|
||||
* Timestamp after which the wallet would do the next check for an auto-refresh.
|
||||
*/
|
||||
function getAutoRefreshCheckThreshold(d: DenominationRecord): AbsoluteTime {
|
||||
const expireWithdraw = AbsoluteTime.fromTimestamp(d.stampExpireWithdraw);
|
||||
const expireDeposit = AbsoluteTime.fromTimestamp(d.stampExpireDeposit);
|
||||
const expireWithdraw = AbsoluteTime.fromProtocolTimestamp(d.stampExpireWithdraw);
|
||||
const expireDeposit = AbsoluteTime.fromProtocolTimestamp(d.stampExpireDeposit);
|
||||
const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit);
|
||||
const deltaDiv = durationMul(delta, 0.75);
|
||||
return AbsoluteTime.addDuration(expireWithdraw, deltaDiv);
|
||||
@ -1086,8 +1087,8 @@ function getAutoRefreshCheckThreshold(d: DenominationRecord): AbsoluteTime {
|
||||
* Timestamp after which the wallet would do an auto-refresh.
|
||||
*/
|
||||
function getAutoRefreshExecuteThreshold(d: DenominationRecord): AbsoluteTime {
|
||||
const expireWithdraw = AbsoluteTime.fromTimestamp(d.stampExpireWithdraw);
|
||||
const expireDeposit = AbsoluteTime.fromTimestamp(d.stampExpireDeposit);
|
||||
const expireWithdraw = AbsoluteTime.fromProtocolTimestamp(d.stampExpireWithdraw);
|
||||
const expireDeposit = AbsoluteTime.fromProtocolTimestamp(d.stampExpireDeposit);
|
||||
const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit);
|
||||
const deltaDiv = durationMul(delta, 0.5);
|
||||
return AbsoluteTime.addDuration(expireWithdraw, deltaDiv);
|
||||
@ -1174,7 +1175,7 @@ export async function autoRefresh(
|
||||
logger.info(
|
||||
`next refresh check at ${AbsoluteTime.toIsoString(minCheckThreshold)}`,
|
||||
);
|
||||
exchange.nextRefreshCheck = AbsoluteTime.toTimestamp(minCheckThreshold);
|
||||
exchange.nextRefreshCheck = AbsoluteTime.toPreciseTimestamp(minCheckThreshold);
|
||||
await tx.exchanges.put(exchange);
|
||||
});
|
||||
return OperationAttemptResult.finishedEmpty();
|
||||
|
@ -536,7 +536,7 @@ export async function runIntegrationTest2(
|
||||
partialContractTerms: {
|
||||
amount: `${currency}:1`,
|
||||
summary: "Payment Peer Push Test",
|
||||
purse_expiration: AbsoluteTime.toTimestamp(
|
||||
purse_expiration: AbsoluteTime.toProtocolTimestamp(
|
||||
AbsoluteTime.addDuration(
|
||||
AbsoluteTime.now(),
|
||||
Duration.fromSpec({ hours: 1 }),
|
||||
@ -557,7 +557,7 @@ export async function runIntegrationTest2(
|
||||
partialContractTerms: {
|
||||
amount: `${currency}:1`,
|
||||
summary: "Payment Peer Pull Test",
|
||||
purse_expiration: AbsoluteTime.toTimestamp(
|
||||
purse_expiration: AbsoluteTime.toProtocolTimestamp(
|
||||
AbsoluteTime.addDuration(
|
||||
AbsoluteTime.now(),
|
||||
Duration.fromSpec({ hours: 1 }),
|
||||
|
@ -33,6 +33,7 @@ import {
|
||||
parseTipUri,
|
||||
PrepareTipResult,
|
||||
TalerErrorCode,
|
||||
TalerPreciseTimestamp,
|
||||
TalerProtocolTimestamp,
|
||||
TipPlanchetDetail,
|
||||
TransactionMajorState,
|
||||
@ -160,7 +161,7 @@ export async function prepareTip(
|
||||
exchangeBaseUrl: tipPickupStatus.exchange_url,
|
||||
next_url: tipPickupStatus.next_url,
|
||||
merchantBaseUrl: res.merchantBaseUrl,
|
||||
createdTimestamp: TalerProtocolTimestamp.now(),
|
||||
createdTimestamp: TalerPreciseTimestamp.now(),
|
||||
merchantTipId: res.merchantTipId,
|
||||
tipAmountEffective: Amounts.stringify(selectedDenoms.totalCoinValue),
|
||||
denomsSel: selectedDenoms,
|
||||
@ -365,7 +366,7 @@ export async function processTip(
|
||||
if (tr.pickedUpTimestamp) {
|
||||
return;
|
||||
}
|
||||
tr.pickedUpTimestamp = TalerProtocolTimestamp.now();
|
||||
tr.pickedUpTimestamp = TalerPreciseTimestamp.now();
|
||||
await tx.tips.put(tr);
|
||||
for (const cr of newCoinRecords) {
|
||||
await makeCoinAvailable(ws, tx, cr);
|
||||
@ -390,7 +391,7 @@ export async function acceptTip(
|
||||
logger.error("tip not found");
|
||||
return undefined;
|
||||
}
|
||||
tipRecord.acceptedTimestamp = TalerProtocolTimestamp.now();
|
||||
tipRecord.acceptedTimestamp = TalerPreciseTimestamp.now();
|
||||
await tx.tips.put(tipRecord);
|
||||
|
||||
return tipRecord;
|
||||
|
@ -30,6 +30,7 @@ import {
|
||||
stringifyPayPullUri,
|
||||
stringifyPayPushUri,
|
||||
TalerErrorCode,
|
||||
TalerPreciseTimestamp,
|
||||
TalerProtocolTimestamp,
|
||||
Transaction,
|
||||
TransactionByIdRequest,
|
||||
@ -472,7 +473,7 @@ function buildTransactionForPeerPullCredit(
|
||||
amountRaw: Amounts.stringify(wsr.instructedAmount),
|
||||
exchangeBaseUrl: wsr.exchangeBaseUrl,
|
||||
// Old transactions don't have it!
|
||||
timestamp: pullCredit.mergeTimestamp ?? TalerProtocolTimestamp.now(),
|
||||
timestamp: pullCredit.mergeTimestamp ?? TalerPreciseTimestamp.now(),
|
||||
info: {
|
||||
expiration: wsr.wgInfo.contractTerms.purse_expiration,
|
||||
summary: wsr.wgInfo.contractTerms.summary,
|
||||
@ -1171,8 +1172,8 @@ export async function getTransactions(
|
||||
|
||||
const txCmp = (h1: Transaction, h2: Transaction) => {
|
||||
const tsCmp = AbsoluteTime.cmp(
|
||||
AbsoluteTime.fromTimestamp(h1.timestamp),
|
||||
AbsoluteTime.fromTimestamp(h2.timestamp),
|
||||
AbsoluteTime.fromPreciseTimestamp(h1.timestamp),
|
||||
AbsoluteTime.fromPreciseTimestamp(h2.timestamp),
|
||||
);
|
||||
if (tsCmp === 0) {
|
||||
return Math.sign(txOrder[h1.type] - txOrder[h2.type]);
|
||||
|
@ -66,6 +66,7 @@ import {
|
||||
TransactionState,
|
||||
TransactionMajorState,
|
||||
TransactionMinorState,
|
||||
TalerPreciseTimestamp,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { EddsaKeypair } from "../crypto/cryptoImplementation.js";
|
||||
import {
|
||||
@ -1327,7 +1328,7 @@ export async function processWithdrawalGroup(
|
||||
}
|
||||
const txStatusOld = computeWithdrawalTransactionStatus(wg);
|
||||
wg.status = WithdrawalGroupStatus.Finished;
|
||||
wg.timestampFinish = TalerProtocolTimestamp.now();
|
||||
wg.timestampFinish = TalerPreciseTimestamp.now();
|
||||
const txStatusNew = computeWithdrawalTransactionStatus(wg);
|
||||
await tx.withdrawalGroups.put(wg);
|
||||
return {
|
||||
@ -1428,7 +1429,7 @@ export async function processWithdrawalGroup(
|
||||
logger.info(`now withdrawn ${numFinished} of ${numTotalCoins} coins`);
|
||||
if (wg.timestampFinish === undefined && numFinished === numTotalCoins) {
|
||||
finishedForFirstTime = true;
|
||||
wg.timestampFinish = TalerProtocolTimestamp.now();
|
||||
wg.timestampFinish = TalerPreciseTimestamp.now();
|
||||
wg.status = WithdrawalGroupStatus.Finished;
|
||||
}
|
||||
|
||||
@ -1613,8 +1614,8 @@ export async function getExchangeWithdrawalInfo(
|
||||
}
|
||||
if (
|
||||
AbsoluteTime.cmp(
|
||||
AbsoluteTime.fromTimestamp(expireDeposit),
|
||||
AbsoluteTime.fromTimestamp(earliestDepositExpiration),
|
||||
AbsoluteTime.fromProtocolTimestamp(expireDeposit),
|
||||
AbsoluteTime.fromProtocolTimestamp(earliestDepositExpiration),
|
||||
) < 0
|
||||
) {
|
||||
earliestDepositExpiration = expireDeposit;
|
||||
@ -1910,7 +1911,7 @@ async function registerReserveWithBank(
|
||||
if (r.wgInfo.withdrawalType !== WithdrawalRecordType.BankIntegrated) {
|
||||
throw Error("invariant failed");
|
||||
}
|
||||
r.wgInfo.bankInfo.timestampReserveInfoPosted = AbsoluteTime.toTimestamp(
|
||||
r.wgInfo.bankInfo.timestampReserveInfoPosted = AbsoluteTime.toPreciseTimestamp(
|
||||
AbsoluteTime.now(),
|
||||
);
|
||||
const oldTxState = computeWithdrawalTransactionStatus(r);
|
||||
@ -1994,7 +1995,7 @@ async function processReserveBankStatus(
|
||||
if (r.wgInfo.withdrawalType !== WithdrawalRecordType.BankIntegrated) {
|
||||
throw Error("invariant failed");
|
||||
}
|
||||
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||
const now = AbsoluteTime.toPreciseTimestamp(AbsoluteTime.now());
|
||||
const oldTxState = computeWithdrawalTransactionStatus(r);
|
||||
r.wgInfo.bankInfo.timestampBankConfirmed = now;
|
||||
r.status = WithdrawalGroupStatus.FailedBankAborted;
|
||||
@ -2044,7 +2045,7 @@ async function processReserveBankStatus(
|
||||
const oldTxState = computeWithdrawalTransactionStatus(r);
|
||||
if (status.transfer_done) {
|
||||
logger.info("withdrawal: transfer confirmed by bank.");
|
||||
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||
const now = AbsoluteTime.toPreciseTimestamp(AbsoluteTime.now());
|
||||
r.wgInfo.bankInfo.timestampBankConfirmed = now;
|
||||
r.status = WithdrawalGroupStatus.PendingQueryingStatus;
|
||||
// FIXME: Notification is deprecated with DD37.
|
||||
@ -2105,7 +2106,7 @@ export async function internalCreateWithdrawalGroup(
|
||||
): Promise<WithdrawalGroupRecord> {
|
||||
const reserveKeyPair =
|
||||
args.reserveKeyPair ?? (await ws.cryptoApi.createEddsaKeypair({}));
|
||||
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||
const now = AbsoluteTime.toPreciseTimestamp(AbsoluteTime.now());
|
||||
const secretSeed = encodeCrock(getRandomBytes(32));
|
||||
const canonExchange = canonicalizeBaseUrl(args.exchangeBaseUrl);
|
||||
const amount = args.amount;
|
||||
@ -2200,7 +2201,7 @@ export async function internalCreateWithdrawalGroup(
|
||||
|
||||
const exchange = await tx.exchanges.get(withdrawalGroup.exchangeBaseUrl);
|
||||
if (exchange) {
|
||||
exchange.lastWithdrawal = TalerProtocolTimestamp.now();
|
||||
exchange.lastWithdrawal = TalerPreciseTimestamp.now();
|
||||
await tx.exchanges.put(exchange);
|
||||
}
|
||||
|
||||
|
@ -577,8 +577,8 @@ export async function selectCandidates(
|
||||
]?.find((x) => {
|
||||
return AbsoluteTime.isBetween(
|
||||
AbsoluteTime.now(),
|
||||
AbsoluteTime.fromTimestamp(x.startStamp),
|
||||
AbsoluteTime.fromTimestamp(x.endStamp),
|
||||
AbsoluteTime.fromProtocolTimestamp(x.startStamp),
|
||||
AbsoluteTime.fromProtocolTimestamp(x.endStamp),
|
||||
);
|
||||
})?.wireFee;
|
||||
if (wireFeeStr) {
|
||||
|
@ -42,7 +42,7 @@ const VALUES: AmountString[] = Array.from({ length: 10 }).map(
|
||||
(undef, t) => `USD:${t}`,
|
||||
);
|
||||
const TIMESTAMPS = Array.from({ length: 20 }).map((undef, t_s) => ({ t_s }));
|
||||
const ABS_TIME = TIMESTAMPS.map((m) => AbsoluteTime.fromTimestamp(m));
|
||||
const ABS_TIME = TIMESTAMPS.map((m) => AbsoluteTime.fromProtocolTimestamp(m));
|
||||
|
||||
function normalize(
|
||||
list: DenominationInfo[],
|
||||
|
@ -326,7 +326,7 @@ export function createTimeline<Type extends object>(
|
||||
fee: Amounts.stringify(fee),
|
||||
group,
|
||||
id,
|
||||
moment: AbsoluteTime.fromTimestamp(stampStart),
|
||||
moment: AbsoluteTime.fromProtocolTimestamp(stampStart),
|
||||
denom,
|
||||
});
|
||||
ps.push({
|
||||
@ -334,7 +334,7 @@ export function createTimeline<Type extends object>(
|
||||
fee: Amounts.stringify(fee),
|
||||
group,
|
||||
id,
|
||||
moment: AbsoluteTime.fromTimestamp(stampEnd),
|
||||
moment: AbsoluteTime.fromProtocolTimestamp(stampEnd),
|
||||
denom,
|
||||
});
|
||||
return ps;
|
||||
@ -457,8 +457,8 @@ export function isWithdrawableDenom(
|
||||
denomselAllowLate?: boolean,
|
||||
): boolean {
|
||||
const now = AbsoluteTime.now();
|
||||
const start = AbsoluteTime.fromTimestamp(d.stampStart);
|
||||
const withdrawExpire = AbsoluteTime.fromTimestamp(d.stampExpireWithdraw);
|
||||
const start = AbsoluteTime.fromProtocolTimestamp(d.stampStart);
|
||||
const withdrawExpire = AbsoluteTime.fromProtocolTimestamp(d.stampExpireWithdraw);
|
||||
const started = AbsoluteTime.cmp(now, start) >= 0;
|
||||
let lastPossibleWithdraw: AbsoluteTime;
|
||||
if (denomselAllowLate) {
|
||||
|
@ -136,7 +136,7 @@ export function PendingTransactionsView({
|
||||
</Typography>
|
||||
-
|
||||
<Time
|
||||
timestamp={AbsoluteTime.fromTimestamp(t.timestamp)}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(t.timestamp)}
|
||||
format="dd MMMM yyyy"
|
||||
/>
|
||||
</Grid>
|
||||
|
@ -179,7 +179,7 @@ export function HiddenView({ showHandler }: States.Hidden): VNode {
|
||||
}
|
||||
|
||||
export function ShowView({ contractTerms, hideHandler }: States.Show): VNode {
|
||||
const createdAt = AbsoluteTime.fromTimestamp(contractTerms.timestamp);
|
||||
const createdAt = AbsoluteTime.fromProtocolTimestamp(contractTerms.timestamp);
|
||||
const { i18n } = useTranslationContext();
|
||||
|
||||
return (
|
||||
@ -266,7 +266,7 @@ export function ShowView({ contractTerms, hideHandler }: States.Show): VNode {
|
||||
<td>
|
||||
{contractTerms.deliveryDate && (
|
||||
<Time
|
||||
timestamp={AbsoluteTime.fromTimestamp(
|
||||
timestamp={AbsoluteTime.fromProtocolTimestamp(
|
||||
contractTerms.deliveryDate,
|
||||
)}
|
||||
format="dd MMMM yyyy, HH:mm"
|
||||
@ -299,7 +299,7 @@ export function ShowView({ contractTerms, hideHandler }: States.Show): VNode {
|
||||
<td>
|
||||
{contractTerms.timestamp && (
|
||||
<Time
|
||||
timestamp={AbsoluteTime.fromTimestamp(
|
||||
timestamp={AbsoluteTime.fromProtocolTimestamp(
|
||||
contractTerms.timestamp,
|
||||
)}
|
||||
format="dd MMMM yyyy, HH:mm"
|
||||
@ -314,7 +314,7 @@ export function ShowView({ contractTerms, hideHandler }: States.Show): VNode {
|
||||
<td>
|
||||
{
|
||||
<Time
|
||||
timestamp={AbsoluteTime.fromTimestamp(
|
||||
timestamp={AbsoluteTime.fromProtocolTimestamp(
|
||||
contractTerms.refundDeadline,
|
||||
)}
|
||||
format="dd MMMM yyyy, HH:mm"
|
||||
@ -349,7 +349,7 @@ export function ShowView({ contractTerms, hideHandler }: States.Show): VNode {
|
||||
<td>
|
||||
{
|
||||
<Time
|
||||
timestamp={AbsoluteTime.fromTimestamp(
|
||||
timestamp={AbsoluteTime.fromProtocolTimestamp(
|
||||
contractTerms.payDeadline,
|
||||
)}
|
||||
format="dd MMMM yyyy, HH:mm"
|
||||
|
@ -53,7 +53,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"credit"}
|
||||
title={new URL(tx.exchangeBaseUrl).hostname}
|
||||
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"W"}
|
||||
pending={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
@ -76,7 +76,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"credit"}
|
||||
title={new URL(tx.exchangeBaseUrl).hostname}
|
||||
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"I"}
|
||||
pending={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
@ -100,7 +100,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
debitCreditIndicator={"debit"}
|
||||
title={tx.info.merchant.name}
|
||||
subtitle={tx.info.summary}
|
||||
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"P"}
|
||||
pending={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
@ -121,7 +121,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
? tx.paymentInfo.merchant.name
|
||||
: "--unknown merchant--"
|
||||
} //FIXME: DD37 wallet-core is not returning this value
|
||||
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"R"}
|
||||
pending={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
@ -137,7 +137,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"credit"}
|
||||
title={new URL(tx.merchantBaseUrl).hostname}
|
||||
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"T"}
|
||||
pending={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
@ -153,7 +153,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"credit"}
|
||||
title={"Refresh"}
|
||||
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"R"}
|
||||
pending={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
@ -169,7 +169,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"debit"}
|
||||
title={tx.targetPaytoUri}
|
||||
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"D"}
|
||||
pending={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
@ -185,7 +185,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"credit"}
|
||||
title={tx.info.summary || "Invoice"}
|
||||
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"I"}
|
||||
pending={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
@ -201,7 +201,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"debit"}
|
||||
title={tx.info.summary || "Invoice"}
|
||||
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"I"}
|
||||
pending={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
@ -217,7 +217,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"credit"}
|
||||
title={tx.info.summary || "Transfer"}
|
||||
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"T"}
|
||||
pending={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
@ -233,7 +233,7 @@ export function TransactionItem(props: { tx: Transaction }): VNode {
|
||||
amount={tx.amountEffective}
|
||||
debitCreditIndicator={"debit"}
|
||||
title={tx.info.summary || "Transfer"}
|
||||
timestamp={AbsoluteTime.fromTimestamp(tx.timestamp)}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(tx.timestamp)}
|
||||
iconPath={"T"}
|
||||
pending={
|
||||
tx.txState.major === TransactionMajorState.Pending
|
||||
|
@ -123,7 +123,7 @@ export function useComponentState({
|
||||
raw,
|
||||
goToWalletManualWithdraw,
|
||||
summary,
|
||||
expiration: expiration ? AbsoluteTime.fromTimestamp(expiration) : undefined,
|
||||
expiration: expiration ? AbsoluteTime.fromProtocolTimestamp(expiration) : undefined,
|
||||
};
|
||||
|
||||
if (!foundBalance) {
|
||||
|
@ -93,7 +93,7 @@ export function BaseView(state: SupportedStates): VNode {
|
||||
title={i18n.str`Valid until`}
|
||||
text={
|
||||
<Time
|
||||
timestamp={AbsoluteTime.fromTimestamp(
|
||||
timestamp={AbsoluteTime.fromProtocolTimestamp(
|
||||
contractTerms.pay_deadline,
|
||||
)}
|
||||
format="dd MMMM yyyy, HH:mm"
|
||||
|
@ -86,7 +86,7 @@ export function useComponentState({
|
||||
onClick: pushAlertOnError(accept),
|
||||
},
|
||||
summary,
|
||||
expiration: expiration ? AbsoluteTime.fromTimestamp(expiration) : undefined,
|
||||
expiration: expiration ? AbsoluteTime.fromProtocolTimestamp(expiration) : undefined,
|
||||
cancel: {
|
||||
onClick: pushAlertOnError(onClose),
|
||||
},
|
||||
|
@ -26,7 +26,7 @@ import {
|
||||
ShowRecoveryInfo,
|
||||
} from "./BackupPage.js";
|
||||
import * as tests from "@gnu-taler/web-util/testing";
|
||||
import { TalerProtocolTimestamp } from "@gnu-taler/taler-util";
|
||||
import { TalerPreciseTimestamp, TalerProtocolTimestamp } from "@gnu-taler/taler-util";
|
||||
|
||||
export default {
|
||||
title: "backup",
|
||||
@ -39,7 +39,7 @@ export const LotOfProviders = tests.createExample(TestedComponent, {
|
||||
name: "sync.demo",
|
||||
syncProviderBaseUrl: "http://sync.taler:9967/",
|
||||
lastSuccessfulBackupTimestamp:
|
||||
TalerProtocolTimestamp.fromSeconds(1625063925),
|
||||
TalerPreciseTimestamp.fromSeconds(1625063925),
|
||||
paymentProposalIds: [
|
||||
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
||||
],
|
||||
@ -60,7 +60,7 @@ export const LotOfProviders = tests.createExample(TestedComponent, {
|
||||
name: "sync.demo",
|
||||
syncProviderBaseUrl: "http://sync.taler:9967/",
|
||||
lastSuccessfulBackupTimestamp:
|
||||
TalerProtocolTimestamp.fromSeconds(1625063925),
|
||||
TalerPreciseTimestamp.fromSeconds(1625063925),
|
||||
paymentProposalIds: [
|
||||
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
||||
],
|
||||
@ -171,7 +171,7 @@ export const OneProvider = tests.createExample(TestedComponent, {
|
||||
name: "sync.demo",
|
||||
syncProviderBaseUrl: "http://sync.taler:9967/",
|
||||
lastSuccessfulBackupTimestamp:
|
||||
TalerProtocolTimestamp.fromSeconds(1625063925),
|
||||
TalerPreciseTimestamp.fromSeconds(1625063925),
|
||||
paymentProposalIds: [
|
||||
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
||||
],
|
||||
|
@ -199,7 +199,7 @@ export function BackupView({
|
||||
status={provider.paymentStatus}
|
||||
timestamp={
|
||||
provider.lastSuccessfulBackupTimestamp
|
||||
? AbsoluteTime.fromTimestamp(
|
||||
? AbsoluteTime.fromPreciseTimestamp(
|
||||
provider.lastSuccessfulBackupTimestamp,
|
||||
)
|
||||
: undefined
|
||||
|
@ -19,7 +19,7 @@
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { AbsoluteTime, TalerProtocolTimestamp } from "@gnu-taler/taler-util";
|
||||
import { AbsoluteTime, TalerPreciseTimestamp, TalerProtocolTimestamp } from "@gnu-taler/taler-util";
|
||||
import { ProviderPaymentType } from "@gnu-taler/taler-wallet-core";
|
||||
import * as tests from "@gnu-taler/web-util/testing";
|
||||
import { ProviderView as TestedComponent } from "./ProviderDetailPage.js";
|
||||
@ -40,7 +40,7 @@ export const Active = tests.createExample(TestedComponent, {
|
||||
name: "sync.demo",
|
||||
syncProviderBaseUrl: "http://sync.taler:9967/",
|
||||
lastSuccessfulBackupTimestamp:
|
||||
TalerProtocolTimestamp.fromSeconds(1625063925),
|
||||
TalerPreciseTimestamp.fromSeconds(1625063925),
|
||||
paymentProposalIds: [
|
||||
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
||||
],
|
||||
@ -64,9 +64,9 @@ export const ActiveErrorSync = tests.createExample(TestedComponent, {
|
||||
name: "sync.demo",
|
||||
syncProviderBaseUrl: "http://sync.taler:9967/",
|
||||
lastSuccessfulBackupTimestamp:
|
||||
TalerProtocolTimestamp.fromSeconds(1625063925),
|
||||
TalerPreciseTimestamp.fromSeconds(1625063925),
|
||||
lastAttemptedBackupTimestamp:
|
||||
TalerProtocolTimestamp.fromSeconds(1625063925078),
|
||||
TalerPreciseTimestamp.fromSeconds(1625063925078),
|
||||
paymentProposalIds: [
|
||||
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
||||
],
|
||||
@ -99,7 +99,7 @@ export const ActiveBackupProblemUnreadable = tests.createExample(
|
||||
name: "sync.demo",
|
||||
syncProviderBaseUrl: "http://sync.taler:9967/",
|
||||
lastSuccessfulBackupTimestamp:
|
||||
TalerProtocolTimestamp.fromSeconds(1625063925),
|
||||
TalerPreciseTimestamp.fromSeconds(1625063925),
|
||||
paymentProposalIds: [
|
||||
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
||||
],
|
||||
@ -127,7 +127,7 @@ export const ActiveBackupProblemDevice = tests.createExample(TestedComponent, {
|
||||
name: "sync.demo",
|
||||
syncProviderBaseUrl: "http://sync.taler:9967/",
|
||||
lastSuccessfulBackupTimestamp:
|
||||
TalerProtocolTimestamp.fromSeconds(1625063925078),
|
||||
TalerPreciseTimestamp.fromSeconds(1625063925078),
|
||||
paymentProposalIds: [
|
||||
"43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG",
|
||||
],
|
||||
|
@ -151,7 +151,7 @@ export function ProviderView({
|
||||
}: ViewProps): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
const lb = info.lastSuccessfulBackupTimestamp
|
||||
? AbsoluteTime.fromTimestamp(info.lastSuccessfulBackupTimestamp)
|
||||
? AbsoluteTime.fromPreciseTimestamp(info.lastSuccessfulBackupTimestamp)
|
||||
: undefined;
|
||||
const isPaid =
|
||||
info.paymentStatus.type === ProviderPaymentType.Paid ||
|
||||
|
@ -23,6 +23,7 @@ import {
|
||||
AbsoluteTime,
|
||||
PaymentStatus,
|
||||
RefreshReason,
|
||||
TalerPreciseTimestamp,
|
||||
TalerProtocolTimestamp,
|
||||
TransactionCommon,
|
||||
TransactionDeposit,
|
||||
@ -245,7 +246,7 @@ export const WithdrawFiveMinutesAgo = tests.createExample(
|
||||
() => ({
|
||||
transaction: {
|
||||
...exampleData.withdraw,
|
||||
timestamp: TalerProtocolTimestamp.fromSeconds(
|
||||
timestamp: TalerPreciseTimestamp.fromSeconds(
|
||||
new Date().getTime() / 1000 - 60 * 5,
|
||||
),
|
||||
},
|
||||
@ -257,7 +258,7 @@ export const WithdrawFiveMinutesAgoAndPending = tests.createExample(
|
||||
() => ({
|
||||
transaction: {
|
||||
...exampleData.withdraw,
|
||||
timestamp: TalerProtocolTimestamp.fromSeconds(
|
||||
timestamp: TalerPreciseTimestamp.fromSeconds(
|
||||
new Date().getTime() / 1000 - 60 * 5,
|
||||
),
|
||||
txState: {
|
||||
|
@ -25,6 +25,7 @@ import {
|
||||
parsePaytoUri,
|
||||
PaytoUri,
|
||||
stringifyPaytoUri,
|
||||
TalerPreciseTimestamp,
|
||||
TalerProtocolTimestamp,
|
||||
Transaction,
|
||||
TransactionDeposit,
|
||||
@ -585,7 +586,7 @@ export function TransactionView({
|
||||
on{" "}
|
||||
{
|
||||
<Time
|
||||
timestamp={AbsoluteTime.fromTimestamp(
|
||||
timestamp={AbsoluteTime.fromProtocolTimestamp(
|
||||
r.timestamp,
|
||||
)}
|
||||
format="dd MMMM yyyy"
|
||||
@ -671,7 +672,7 @@ export function TransactionView({
|
||||
if (transaction.type === TransactionType.Deposit) {
|
||||
const payto = parsePaytoUri(transaction.targetPaytoUri);
|
||||
|
||||
const wireTime = AbsoluteTime.fromTimestamp(
|
||||
const wireTime = AbsoluteTime.fromProtocolTimestamp(
|
||||
transaction.wireTransferDeadline,
|
||||
);
|
||||
const shouldBeWired = wireTime.t_ms !== "never" && isPast(wireTime.t_ms);
|
||||
@ -1204,7 +1205,7 @@ function DeliveryDetails({
|
||||
</td>
|
||||
<td>
|
||||
<Time
|
||||
timestamp={AbsoluteTime.fromTimestamp(date)}
|
||||
timestamp={AbsoluteTime.fromProtocolTimestamp(date)}
|
||||
format="dd MMMM yyyy, HH:mm"
|
||||
/>
|
||||
</td>
|
||||
@ -1768,7 +1769,7 @@ function Header({
|
||||
kind,
|
||||
type,
|
||||
}: {
|
||||
timestamp: TalerProtocolTimestamp;
|
||||
timestamp: TalerPreciseTimestamp;
|
||||
total: AmountJson;
|
||||
children: ComponentChildren;
|
||||
kind: Kind;
|
||||
@ -1785,7 +1786,7 @@ function Header({
|
||||
<div>
|
||||
<SubTitle>{children}</SubTitle>
|
||||
<Time
|
||||
timestamp={AbsoluteTime.fromTimestamp(timestamp)}
|
||||
timestamp={AbsoluteTime.fromPreciseTimestamp(timestamp)}
|
||||
format="dd MMMM yyyy, HH:mm"
|
||||
/>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user