wallet-core: towards DD48
This commit is contained in:
parent
88f7338d7c
commit
d19aef746c
@ -312,6 +312,14 @@ export namespace Duration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export namespace AbsoluteTime {
|
export namespace AbsoluteTime {
|
||||||
|
export function getStampMsNow(): number {
|
||||||
|
return new Date().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStampMsNever(): number {
|
||||||
|
return Number.MAX_SAFE_INTEGER;
|
||||||
|
}
|
||||||
|
|
||||||
export function now(): AbsoluteTime {
|
export function now(): AbsoluteTime {
|
||||||
return {
|
return {
|
||||||
t_ms: new Date().getTime() + timeshift,
|
t_ms: new Date().getTime() + timeshift,
|
||||||
@ -398,6 +406,13 @@ export namespace AbsoluteTime {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function fromStampMs(stampMs: number): AbsoluteTime {
|
||||||
|
return {
|
||||||
|
t_ms: stampMs,
|
||||||
|
[opaque_AbsoluteTime]: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function fromPreciseTimestamp(t: TalerPreciseTimestamp): AbsoluteTime {
|
export function fromPreciseTimestamp(t: TalerPreciseTimestamp): AbsoluteTime {
|
||||||
if (t.t_s === "never") {
|
if (t.t_s === "never") {
|
||||||
return { t_ms: "never", [opaque_AbsoluteTime]: true };
|
return { t_ms: "never", [opaque_AbsoluteTime]: true };
|
||||||
@ -409,6 +424,13 @@ export namespace AbsoluteTime {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toStampMs(at: AbsoluteTime): number {
|
||||||
|
if (at.t_ms === "never") {
|
||||||
|
return Number.MAX_SAFE_INTEGER;
|
||||||
|
}
|
||||||
|
return at.t_ms;
|
||||||
|
}
|
||||||
|
|
||||||
export function toPreciseTimestamp(at: AbsoluteTime): TalerPreciseTimestamp {
|
export function toPreciseTimestamp(at: AbsoluteTime): TalerPreciseTimestamp {
|
||||||
if (at.t_ms == "never") {
|
if (at.t_ms == "never") {
|
||||||
return {
|
return {
|
||||||
|
@ -1262,17 +1262,25 @@ export interface ExchangeFullDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum ExchangeTosStatus {
|
export enum ExchangeTosStatus {
|
||||||
New = "new",
|
Pending = "pending",
|
||||||
|
Proposed = "proposed",
|
||||||
Accepted = "accepted",
|
Accepted = "accepted",
|
||||||
Changed = "changed",
|
|
||||||
NotFound = "not-found",
|
|
||||||
Unknown = "unknown",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ExchangeEntryStatus {
|
export enum ExchangeEntryStatus {
|
||||||
Unknown = "unknown",
|
Preset = "preset",
|
||||||
Outdated = "outdated",
|
Ephemeral = "ephemeral",
|
||||||
Ok = "ok",
|
Used = "used",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ExchangeUpdateStatus {
|
||||||
|
Initial = "initial",
|
||||||
|
InitialUpdate = "initial(update)",
|
||||||
|
Suspended = "suspended",
|
||||||
|
Failed = "failed",
|
||||||
|
OutdatedUpdate = "outdated(update)",
|
||||||
|
Ready = "ready",
|
||||||
|
ReadyUpdate = "ready(update)",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OperationErrorInfo {
|
export interface OperationErrorInfo {
|
||||||
@ -1285,13 +1293,9 @@ export interface ExchangeListItem {
|
|||||||
currency: string | undefined;
|
currency: string | undefined;
|
||||||
paytoUris: string[];
|
paytoUris: string[];
|
||||||
tosStatus: ExchangeTosStatus;
|
tosStatus: ExchangeTosStatus;
|
||||||
exchangeStatus: ExchangeEntryStatus;
|
exchangeEntryStatus: ExchangeEntryStatus;
|
||||||
|
exchangeUpdateStatus: ExchangeUpdateStatus;
|
||||||
ageRestrictionOptions: number[];
|
ageRestrictionOptions: number[];
|
||||||
/**
|
|
||||||
* Permanently added to the wallet, as opposed to just
|
|
||||||
* temporarily queried.
|
|
||||||
*/
|
|
||||||
permanent: boolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about the last error that occurred when trying
|
* Information about the last error that occurred when trying
|
||||||
@ -1370,8 +1374,8 @@ export const codecForExchangeListItem = (): Codec<ExchangeListItem> =>
|
|||||||
.property("exchangeBaseUrl", codecForString())
|
.property("exchangeBaseUrl", codecForString())
|
||||||
.property("paytoUris", codecForList(codecForString()))
|
.property("paytoUris", codecForList(codecForString()))
|
||||||
.property("tosStatus", codecForAny())
|
.property("tosStatus", codecForAny())
|
||||||
.property("exchangeStatus", codecForAny())
|
.property("exchangeEntryStatus", codecForAny())
|
||||||
.property("permanent", codecForBoolean())
|
.property("exchangeUpdateStatus", codecForAny())
|
||||||
.property("ageRestrictionOptions", codecForList(codecForNumber()))
|
.property("ageRestrictionOptions", codecForList(codecForNumber()))
|
||||||
.build("ExchangeListItem");
|
.build("ExchangeListItem");
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ import { HttpRequestLibrary } from "@gnu-taler/taler-util/http";
|
|||||||
import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
|
import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
|
||||||
import {
|
import {
|
||||||
ExchangeDetailsRecord,
|
ExchangeDetailsRecord,
|
||||||
ExchangeRecord,
|
ExchangeEntryRecord,
|
||||||
RefreshReasonDetails,
|
RefreshReasonDetails,
|
||||||
WalletStoresV1,
|
WalletStoresV1,
|
||||||
} from "./db.js";
|
} from "./db.js";
|
||||||
@ -108,7 +108,7 @@ export interface ExchangeOperations {
|
|||||||
): Promise<ExchangeDetailsRecord | undefined>;
|
): Promise<ExchangeDetailsRecord | undefined>;
|
||||||
getExchangeTrust(
|
getExchangeTrust(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
exchangeInfo: ExchangeRecord,
|
exchangeInfo: ExchangeEntryRecord,
|
||||||
): Promise<TrustInfo>;
|
): Promise<TrustInfo>;
|
||||||
updateExchangeFromUrl(
|
updateExchangeFromUrl(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
@ -118,7 +118,7 @@ export interface ExchangeOperations {
|
|||||||
cancellationToken?: CancellationToken;
|
cancellationToken?: CancellationToken;
|
||||||
},
|
},
|
||||||
): Promise<{
|
): Promise<{
|
||||||
exchange: ExchangeRecord;
|
exchange: ExchangeEntryRecord;
|
||||||
exchangeDetails: ExchangeDetailsRecord;
|
exchangeDetails: ExchangeDetailsRecord;
|
||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
@ -342,20 +342,19 @@ export async function importBackup(
|
|||||||
if (existingExchange) {
|
if (existingExchange) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
await tx.exchanges.put({
|
// await tx.exchanges.put({
|
||||||
baseUrl: backupExchange.base_url,
|
// baseUrl: backupExchange.base_url,
|
||||||
detailsPointer: {
|
// detailsPointer: {
|
||||||
currency: backupExchange.currency,
|
// currency: backupExchange.currency,
|
||||||
masterPublicKey: backupExchange.master_public_key,
|
// masterPublicKey: backupExchange.master_public_key,
|
||||||
updateClock: backupExchange.update_clock,
|
// updateClock: backupExchange.update_clock,
|
||||||
},
|
// },
|
||||||
permanent: true,
|
// lastUpdate: undefined,
|
||||||
lastUpdate: undefined,
|
// nextUpdate: TalerPreciseTimestamp.now(),
|
||||||
nextUpdate: TalerPreciseTimestamp.now(),
|
// nextRefreshCheck: TalerPreciseTimestamp.now(),
|
||||||
nextRefreshCheck: TalerPreciseTimestamp.now(),
|
// lastKeysEtag: undefined,
|
||||||
lastKeysEtag: undefined,
|
// lastWireEtag: undefined,
|
||||||
lastWireEtag: undefined,
|
// });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const backupExchangeDetails of backupBlob.exchange_details) {
|
for (const backupExchangeDetails of backupBlob.exchange_details) {
|
||||||
|
@ -30,6 +30,7 @@ import {
|
|||||||
ExchangeEntryStatus,
|
ExchangeEntryStatus,
|
||||||
ExchangeListItem,
|
ExchangeListItem,
|
||||||
ExchangeTosStatus,
|
ExchangeTosStatus,
|
||||||
|
ExchangeUpdateStatus,
|
||||||
getErrorDetailFromException,
|
getErrorDetailFromException,
|
||||||
j2s,
|
j2s,
|
||||||
Logger,
|
Logger,
|
||||||
@ -47,7 +48,7 @@ import {
|
|||||||
WalletStoresV1,
|
WalletStoresV1,
|
||||||
CoinRecord,
|
CoinRecord,
|
||||||
ExchangeDetailsRecord,
|
ExchangeDetailsRecord,
|
||||||
ExchangeRecord,
|
ExchangeEntryRecord,
|
||||||
BackupProviderRecord,
|
BackupProviderRecord,
|
||||||
DepositGroupRecord,
|
DepositGroupRecord,
|
||||||
PeerPullPaymentIncomingRecord,
|
PeerPullPaymentIncomingRecord,
|
||||||
@ -59,6 +60,8 @@ import {
|
|||||||
RefreshGroupRecord,
|
RefreshGroupRecord,
|
||||||
RewardRecord,
|
RewardRecord,
|
||||||
WithdrawalGroupRecord,
|
WithdrawalGroupRecord,
|
||||||
|
ExchangeEntryDbUpdateStatus,
|
||||||
|
ExchangeEntryDbRecordStatus,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { makeErrorDetail, TalerError } from "@gnu-taler/taler-util";
|
import { makeErrorDetail, TalerError } from "@gnu-taler/taler-util";
|
||||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||||
@ -529,16 +532,16 @@ export function getExchangeTosStatus(
|
|||||||
exchangeDetails: ExchangeDetailsRecord,
|
exchangeDetails: ExchangeDetailsRecord,
|
||||||
): ExchangeTosStatus {
|
): ExchangeTosStatus {
|
||||||
if (!exchangeDetails.tosAccepted) {
|
if (!exchangeDetails.tosAccepted) {
|
||||||
return ExchangeTosStatus.New;
|
return ExchangeTosStatus.Proposed;
|
||||||
}
|
}
|
||||||
if (exchangeDetails.tosAccepted?.etag == exchangeDetails.tosCurrentEtag) {
|
if (exchangeDetails.tosAccepted?.etag == exchangeDetails.tosCurrentEtag) {
|
||||||
return ExchangeTosStatus.Accepted;
|
return ExchangeTosStatus.Accepted;
|
||||||
}
|
}
|
||||||
return ExchangeTosStatus.Changed;
|
return ExchangeTosStatus.Proposed;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeExchangeListItem(
|
export function makeExchangeListItem(
|
||||||
r: ExchangeRecord,
|
r: ExchangeEntryRecord,
|
||||||
exchangeDetails: ExchangeDetailsRecord | undefined,
|
exchangeDetails: ExchangeDetailsRecord | undefined,
|
||||||
lastError: TalerErrorDetail | undefined,
|
lastError: TalerErrorDetail | undefined,
|
||||||
): ExchangeListItem {
|
): ExchangeListItem {
|
||||||
@ -547,30 +550,57 @@ export function makeExchangeListItem(
|
|||||||
error: lastError,
|
error: lastError,
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
if (!exchangeDetails) {
|
|
||||||
return {
|
let exchangeUpdateStatus: ExchangeUpdateStatus;
|
||||||
exchangeBaseUrl: r.baseUrl,
|
switch (r.updateStatus) {
|
||||||
currency: undefined,
|
case ExchangeEntryDbUpdateStatus.Failed:
|
||||||
tosStatus: ExchangeTosStatus.Unknown,
|
exchangeUpdateStatus = ExchangeUpdateStatus.Failed;
|
||||||
paytoUris: [],
|
break;
|
||||||
exchangeStatus: ExchangeEntryStatus.Unknown,
|
case ExchangeEntryDbUpdateStatus.Initial:
|
||||||
permanent: r.permanent,
|
exchangeUpdateStatus = ExchangeUpdateStatus.Initial;
|
||||||
ageRestrictionOptions: [],
|
break;
|
||||||
lastUpdateErrorInfo,
|
case ExchangeEntryDbUpdateStatus.InitialUpdate:
|
||||||
};
|
exchangeUpdateStatus = ExchangeUpdateStatus.InitialUpdate;
|
||||||
|
break;
|
||||||
|
case ExchangeEntryDbUpdateStatus.OutdatedUpdate:
|
||||||
|
exchangeUpdateStatus = ExchangeUpdateStatus.OutdatedUpdate;
|
||||||
|
break;
|
||||||
|
case ExchangeEntryDbUpdateStatus.Ready:
|
||||||
|
exchangeUpdateStatus = ExchangeUpdateStatus.Ready;
|
||||||
|
break;
|
||||||
|
case ExchangeEntryDbUpdateStatus.ReadyUpdate:
|
||||||
|
exchangeUpdateStatus = ExchangeUpdateStatus.ReadyUpdate;
|
||||||
|
break;
|
||||||
|
case ExchangeEntryDbUpdateStatus.Suspended:
|
||||||
|
exchangeUpdateStatus = ExchangeUpdateStatus.Suspended;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
let exchangeStatus;
|
|
||||||
exchangeStatus = ExchangeEntryStatus.Ok;
|
let exchangeEntryStatus: ExchangeEntryStatus;
|
||||||
|
switch (r.entryStatus) {
|
||||||
|
case ExchangeEntryDbRecordStatus.Ephemeral:
|
||||||
|
exchangeEntryStatus = ExchangeEntryStatus.Ephemeral;
|
||||||
|
break;
|
||||||
|
case ExchangeEntryDbRecordStatus.Preset:
|
||||||
|
exchangeEntryStatus = ExchangeEntryStatus.Preset;
|
||||||
|
break;
|
||||||
|
case ExchangeEntryDbRecordStatus.Used:
|
||||||
|
exchangeEntryStatus = ExchangeEntryStatus.Used;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
exchangeBaseUrl: r.baseUrl,
|
exchangeBaseUrl: r.baseUrl,
|
||||||
currency: exchangeDetails.currency,
|
currency: exchangeDetails?.currency,
|
||||||
tosStatus: getExchangeTosStatus(exchangeDetails),
|
exchangeUpdateStatus,
|
||||||
paytoUris: exchangeDetails.wireInfo.accounts.map((x) => x.payto_uri),
|
exchangeEntryStatus,
|
||||||
exchangeStatus,
|
tosStatus: exchangeDetails
|
||||||
permanent: r.permanent,
|
? getExchangeTosStatus(exchangeDetails)
|
||||||
ageRestrictionOptions: exchangeDetails.ageMask
|
: ExchangeTosStatus.Pending,
|
||||||
|
ageRestrictionOptions: exchangeDetails?.ageMask
|
||||||
? AgeRestriction.getAgeGroupsFromMask(exchangeDetails.ageMask)
|
? AgeRestriction.getAgeGroupsFromMask(exchangeDetails.ageMask)
|
||||||
: [],
|
: [],
|
||||||
|
paytoUris: exchangeDetails?.wireInfo.accounts.map((x) => x.payto_uri) ?? [],
|
||||||
lastUpdateErrorInfo,
|
lastUpdateErrorInfo,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -892,13 +922,13 @@ export namespace TaskIdentifiers {
|
|||||||
export function forWithdrawal(wg: WithdrawalGroupRecord): TaskId {
|
export function forWithdrawal(wg: WithdrawalGroupRecord): TaskId {
|
||||||
return `${PendingTaskType.Withdraw}:${wg.withdrawalGroupId}` as TaskId;
|
return `${PendingTaskType.Withdraw}:${wg.withdrawalGroupId}` as TaskId;
|
||||||
}
|
}
|
||||||
export function forExchangeUpdate(exch: ExchangeRecord): TaskId {
|
export function forExchangeUpdate(exch: ExchangeEntryRecord): TaskId {
|
||||||
return `${PendingTaskType.ExchangeUpdate}:${exch.baseUrl}` as TaskId;
|
return `${PendingTaskType.ExchangeUpdate}:${exch.baseUrl}` as TaskId;
|
||||||
}
|
}
|
||||||
export function forExchangeUpdateFromUrl(exchBaseUrl: string): TaskId {
|
export function forExchangeUpdateFromUrl(exchBaseUrl: string): TaskId {
|
||||||
return `${PendingTaskType.ExchangeUpdate}:${exchBaseUrl}` as TaskId;
|
return `${PendingTaskType.ExchangeUpdate}:${exchBaseUrl}` as TaskId;
|
||||||
}
|
}
|
||||||
export function forExchangeCheckRefresh(exch: ExchangeRecord): TaskId {
|
export function forExchangeCheckRefresh(exch: ExchangeEntryRecord): TaskId {
|
||||||
return `${PendingTaskType.ExchangeCheckRefresh}:${exch.baseUrl}` as TaskId;
|
return `${PendingTaskType.ExchangeCheckRefresh}:${exch.baseUrl}` as TaskId;
|
||||||
}
|
}
|
||||||
export function forTipPickup(tipRecord: RewardRecord): TaskId {
|
export function forTipPickup(tipRecord: RewardRecord): TaskId {
|
||||||
|
@ -32,6 +32,7 @@ import {
|
|||||||
encodeCrock,
|
encodeCrock,
|
||||||
ExchangeAuditor,
|
ExchangeAuditor,
|
||||||
ExchangeDenomination,
|
ExchangeDenomination,
|
||||||
|
ExchangeEntryStatus,
|
||||||
ExchangeGlobalFees,
|
ExchangeGlobalFees,
|
||||||
ExchangeSignKeyJson,
|
ExchangeSignKeyJson,
|
||||||
ExchangeWireJson,
|
ExchangeWireJson,
|
||||||
@ -66,10 +67,15 @@ import {
|
|||||||
DenominationRecord,
|
DenominationRecord,
|
||||||
DenominationVerificationStatus,
|
DenominationVerificationStatus,
|
||||||
ExchangeDetailsRecord,
|
ExchangeDetailsRecord,
|
||||||
ExchangeRecord,
|
ExchangeEntryRecord,
|
||||||
WalletStoresV1,
|
WalletStoresV1,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { isWithdrawableDenom } from "../index.js";
|
import {
|
||||||
|
ExchangeEntryDbRecordStatus,
|
||||||
|
ExchangeEntryDbUpdateStatus,
|
||||||
|
isWithdrawableDenom,
|
||||||
|
WalletDbReadWriteTransaction,
|
||||||
|
} from "../index.js";
|
||||||
import { InternalWalletState, TrustInfo } from "../internal-wallet-state.js";
|
import { InternalWalletState, TrustInfo } from "../internal-wallet-state.js";
|
||||||
import { checkDbInvariant } from "../util/invariants.js";
|
import { checkDbInvariant } from "../util/invariants.js";
|
||||||
import {
|
import {
|
||||||
@ -326,6 +332,26 @@ export async function downloadExchangeInfo(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function addPresetExchangeEntry(
|
||||||
|
tx: WalletDbReadWriteTransaction<"exchanges">,
|
||||||
|
exchangeBaseUrl: string,
|
||||||
|
): Promise<void> {
|
||||||
|
let exchange = await tx.exchanges.get(exchangeBaseUrl);
|
||||||
|
if (!exchange) {
|
||||||
|
const r: ExchangeEntryRecord = {
|
||||||
|
entryStatus: ExchangeEntryDbRecordStatus.Preset,
|
||||||
|
updateStatus: ExchangeEntryDbUpdateStatus.Initial,
|
||||||
|
baseUrl: exchangeBaseUrl,
|
||||||
|
detailsPointer: undefined,
|
||||||
|
lastUpdate: undefined,
|
||||||
|
lastKeysEtag: undefined,
|
||||||
|
nextRefreshCheckStampMs: AbsoluteTime.getStampMsNever(),
|
||||||
|
nextUpdateStampMs: AbsoluteTime.getStampMsNever(),
|
||||||
|
};
|
||||||
|
await tx.exchanges.put(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function provideExchangeRecordInTx(
|
export async function provideExchangeRecordInTx(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
tx: GetReadWriteAccess<{
|
tx: GetReadWriteAccess<{
|
||||||
@ -335,20 +361,20 @@ export async function provideExchangeRecordInTx(
|
|||||||
baseUrl: string,
|
baseUrl: string,
|
||||||
now: AbsoluteTime,
|
now: AbsoluteTime,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
exchange: ExchangeRecord;
|
exchange: ExchangeEntryRecord;
|
||||||
exchangeDetails: ExchangeDetailsRecord | undefined;
|
exchangeDetails: ExchangeDetailsRecord | undefined;
|
||||||
}> {
|
}> {
|
||||||
let exchange = await tx.exchanges.get(baseUrl);
|
let exchange = await tx.exchanges.get(baseUrl);
|
||||||
if (!exchange) {
|
if (!exchange) {
|
||||||
const r: ExchangeRecord = {
|
const r: ExchangeEntryRecord = {
|
||||||
permanent: true,
|
entryStatus: ExchangeEntryDbRecordStatus.Ephemeral,
|
||||||
|
updateStatus: ExchangeEntryDbUpdateStatus.InitialUpdate,
|
||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
detailsPointer: undefined,
|
detailsPointer: undefined,
|
||||||
lastUpdate: undefined,
|
lastUpdate: undefined,
|
||||||
nextUpdate: AbsoluteTime.toPreciseTimestamp(now),
|
nextUpdateStampMs: AbsoluteTime.getStampMsNever(),
|
||||||
nextRefreshCheck: AbsoluteTime.toPreciseTimestamp(now),
|
nextRefreshCheckStampMs: AbsoluteTime.getStampMsNever(),
|
||||||
lastKeysEtag: undefined,
|
lastKeysEtag: undefined,
|
||||||
lastWireEtag: undefined,
|
|
||||||
};
|
};
|
||||||
await tx.exchanges.put(r);
|
await tx.exchanges.put(r);
|
||||||
exchange = r;
|
exchange = r;
|
||||||
@ -534,6 +560,10 @@ export async function downloadTosFromAcceptedFormat(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIXME: Split this into two parts: (a) triggering the exchange
|
||||||
|
* to be updated and (b) waiting for the update to finish.
|
||||||
|
*/
|
||||||
export async function updateExchangeFromUrl(
|
export async function updateExchangeFromUrl(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
baseUrl: string,
|
baseUrl: string,
|
||||||
@ -543,7 +573,7 @@ export async function updateExchangeFromUrl(
|
|||||||
cancellationToken?: CancellationToken;
|
cancellationToken?: CancellationToken;
|
||||||
} = {},
|
} = {},
|
||||||
): Promise<{
|
): Promise<{
|
||||||
exchange: ExchangeRecord;
|
exchange: ExchangeEntryRecord;
|
||||||
exchangeDetails: ExchangeDetailsRecord;
|
exchangeDetails: ExchangeDetailsRecord;
|
||||||
}> {
|
}> {
|
||||||
const canonUrl = canonicalizeBaseUrl(baseUrl);
|
const canonUrl = canonicalizeBaseUrl(baseUrl);
|
||||||
@ -613,7 +643,7 @@ export async function updateExchangeFromUrlHandler(
|
|||||||
!forceNow &&
|
!forceNow &&
|
||||||
exchangeDetails !== undefined &&
|
exchangeDetails !== undefined &&
|
||||||
!AbsoluteTime.isExpired(
|
!AbsoluteTime.isExpired(
|
||||||
AbsoluteTime.fromPreciseTimestamp(exchange.nextUpdate),
|
AbsoluteTime.fromStampMs(exchange.nextUpdateStampMs),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
logger.trace("using existing exchange info");
|
logger.trace("using existing exchange info");
|
||||||
@ -755,11 +785,11 @@ export async function updateExchangeFromUrlHandler(
|
|||||||
newDetails.rowId = existingDetails.rowId;
|
newDetails.rowId = existingDetails.rowId;
|
||||||
}
|
}
|
||||||
r.lastUpdate = TalerPreciseTimestamp.now();
|
r.lastUpdate = TalerPreciseTimestamp.now();
|
||||||
r.nextUpdate = AbsoluteTime.toPreciseTimestamp(
|
r.nextUpdateStampMs = AbsoluteTime.toStampMs(
|
||||||
AbsoluteTime.fromProtocolTimestamp(keysInfo.expiry),
|
AbsoluteTime.fromProtocolTimestamp(keysInfo.expiry),
|
||||||
);
|
);
|
||||||
// New denominations might be available.
|
// New denominations might be available.
|
||||||
r.nextRefreshCheck = TalerPreciseTimestamp.now();
|
r.nextRefreshCheckStampMs = AbsoluteTime.getStampMsNow();
|
||||||
if (detailsPointerChanged) {
|
if (detailsPointerChanged) {
|
||||||
r.detailsPointer = {
|
r.detailsPointer = {
|
||||||
currency: newDetails.currency,
|
currency: newDetails.currency,
|
||||||
@ -948,7 +978,7 @@ export async function getExchangePaytoUri(
|
|||||||
*/
|
*/
|
||||||
export async function getExchangeTrust(
|
export async function getExchangeTrust(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
exchangeInfo: ExchangeRecord,
|
exchangeInfo: ExchangeEntryRecord,
|
||||||
): Promise<TrustInfo> {
|
): Promise<TrustInfo> {
|
||||||
let isTrusted = false;
|
let isTrusted = false;
|
||||||
let isAudited = false;
|
let isAudited = false;
|
||||||
|
@ -45,6 +45,7 @@ import {
|
|||||||
PeerPushPaymentIncomingRecord,
|
PeerPushPaymentIncomingRecord,
|
||||||
RefundGroupRecord,
|
RefundGroupRecord,
|
||||||
RefundGroupStatus,
|
RefundGroupStatus,
|
||||||
|
ExchangeEntryDbUpdateStatus,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import {
|
import {
|
||||||
PendingOperationsResponse,
|
PendingOperationsResponse,
|
||||||
@ -81,19 +82,25 @@ async function gatherExchangePending(
|
|||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
tx: GetReadOnlyAccess<{
|
tx: GetReadOnlyAccess<{
|
||||||
exchanges: typeof WalletStoresV1.exchanges;
|
exchanges: typeof WalletStoresV1.exchanges;
|
||||||
exchangeDetails: typeof WalletStoresV1.exchangeDetails;
|
|
||||||
operationRetries: typeof WalletStoresV1.operationRetries;
|
operationRetries: typeof WalletStoresV1.operationRetries;
|
||||||
}>,
|
}>,
|
||||||
now: AbsoluteTime,
|
now: AbsoluteTime,
|
||||||
resp: PendingOperationsResponse,
|
resp: PendingOperationsResponse,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// FIXME: We should do a range query here based on the update time.
|
// FIXME: We should do a range query here based on the update time
|
||||||
|
// and/or the entry state.
|
||||||
await tx.exchanges.iter().forEachAsync(async (exch) => {
|
await tx.exchanges.iter().forEachAsync(async (exch) => {
|
||||||
|
switch (exch.updateStatus) {
|
||||||
|
case ExchangeEntryDbUpdateStatus.Initial:
|
||||||
|
case ExchangeEntryDbUpdateStatus.Suspended:
|
||||||
|
case ExchangeEntryDbUpdateStatus.Failed:
|
||||||
|
return;
|
||||||
|
}
|
||||||
const opTag = TaskIdentifiers.forExchangeUpdate(exch);
|
const opTag = TaskIdentifiers.forExchangeUpdate(exch);
|
||||||
let opr = await tx.operationRetries.get(opTag);
|
let opr = await tx.operationRetries.get(opTag);
|
||||||
const timestampDue =
|
const timestampDue =
|
||||||
opr?.retryInfo.nextRetry ??
|
opr?.retryInfo.nextRetry ??
|
||||||
AbsoluteTime.fromPreciseTimestamp(exch.nextUpdate);
|
AbsoluteTime.fromStampMs(exch.nextUpdateStampMs);
|
||||||
resp.pendingOperations.push({
|
resp.pendingOperations.push({
|
||||||
type: PendingTaskType.ExchangeUpdate,
|
type: PendingTaskType.ExchangeUpdate,
|
||||||
...getPendingCommon(ws, opTag, timestampDue),
|
...getPendingCommon(ws, opTag, timestampDue),
|
||||||
@ -108,7 +115,7 @@ async function gatherExchangePending(
|
|||||||
resp.pendingOperations.push({
|
resp.pendingOperations.push({
|
||||||
type: PendingTaskType.ExchangeCheckRefresh,
|
type: PendingTaskType.ExchangeCheckRefresh,
|
||||||
...getPendingCommon(ws, opTag, timestampDue),
|
...getPendingCommon(ws, opTag, timestampDue),
|
||||||
timestampDue: AbsoluteTime.fromPreciseTimestamp(exch.nextRefreshCheck),
|
timestampDue: AbsoluteTime.fromStampMs(exch.nextRefreshCheckStampMs),
|
||||||
givesLifeness: false,
|
givesLifeness: false,
|
||||||
exchangeBaseUrl: exch.baseUrl,
|
exchangeBaseUrl: exch.baseUrl,
|
||||||
});
|
});
|
||||||
@ -184,8 +191,9 @@ export async function iterRecordsForWithdrawal(
|
|||||||
WithdrawalGroupStatus.PendingRegisteringBank,
|
WithdrawalGroupStatus.PendingRegisteringBank,
|
||||||
WithdrawalGroupStatus.PendingAml,
|
WithdrawalGroupStatus.PendingAml,
|
||||||
);
|
);
|
||||||
withdrawalGroupRecords =
|
withdrawalGroupRecords = await tx.withdrawalGroups.indexes.byStatus.getAll(
|
||||||
await tx.withdrawalGroups.indexes.byStatus.getAll(range);
|
range,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
withdrawalGroupRecords =
|
withdrawalGroupRecords =
|
||||||
await tx.withdrawalGroups.indexes.byStatus.getAll();
|
await tx.withdrawalGroups.indexes.byStatus.getAll();
|
||||||
@ -344,12 +352,8 @@ export async function iterRecordsForRefund(
|
|||||||
f: (r: RefundGroupRecord) => Promise<void>,
|
f: (r: RefundGroupRecord) => Promise<void>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (filter.onlyState === "nonfinal") {
|
if (filter.onlyState === "nonfinal") {
|
||||||
const keyRange = GlobalIDB.KeyRange.only(
|
const keyRange = GlobalIDB.KeyRange.only(RefundGroupStatus.Pending);
|
||||||
RefundGroupStatus.Pending
|
await tx.refundGroups.indexes.byStatus.iter(keyRange).forEachAsync(f);
|
||||||
);
|
|
||||||
await tx.refundGroups.indexes.byStatus
|
|
||||||
.iter(keyRange)
|
|
||||||
.forEachAsync(f);
|
|
||||||
} else {
|
} else {
|
||||||
await tx.refundGroups.iter().forEachAsync(f);
|
await tx.refundGroups.iter().forEachAsync(f);
|
||||||
}
|
}
|
||||||
|
@ -1190,14 +1190,14 @@ export async function autoRefresh(
|
|||||||
`created refresh group for auto-refresh (${res.refreshGroupId})`,
|
`created refresh group for auto-refresh (${res.refreshGroupId})`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// logger.trace(
|
// logger.trace(
|
||||||
// `current wallet time: ${AbsoluteTime.toIsoString(AbsoluteTime.now())}`,
|
// `current wallet time: ${AbsoluteTime.toIsoString(AbsoluteTime.now())}`,
|
||||||
// );
|
// );
|
||||||
logger.trace(
|
logger.trace(
|
||||||
`next refresh check at ${AbsoluteTime.toIsoString(minCheckThreshold)}`,
|
`next refresh check at ${AbsoluteTime.toIsoString(minCheckThreshold)}`,
|
||||||
);
|
);
|
||||||
exchange.nextRefreshCheck =
|
exchange.nextRefreshCheckStampMs =
|
||||||
AbsoluteTime.toPreciseTimestamp(minCheckThreshold);
|
AbsoluteTime.toStampMs(minCheckThreshold);
|
||||||
await tx.exchanges.put(exchange);
|
await tx.exchanges.put(exchange);
|
||||||
});
|
});
|
||||||
return TaskRunResult.finished();
|
return TaskRunResult.finished();
|
||||||
|
@ -128,6 +128,8 @@ import {
|
|||||||
} from "../util/coinSelection.js";
|
} from "../util/coinSelection.js";
|
||||||
import {
|
import {
|
||||||
ExchangeDetailsRecord,
|
ExchangeDetailsRecord,
|
||||||
|
ExchangeEntryDbRecordStatus,
|
||||||
|
ExchangeEntryDbUpdateStatus,
|
||||||
PendingTaskType,
|
PendingTaskType,
|
||||||
isWithdrawableDenom,
|
isWithdrawableDenom,
|
||||||
} from "../index.js";
|
} from "../index.js";
|
||||||
@ -2341,10 +2343,6 @@ export async function internalPerformCreateWithdrawalGroup(
|
|||||||
}>,
|
}>,
|
||||||
prep: PrepareCreateWithdrawalGroupResult,
|
prep: PrepareCreateWithdrawalGroupResult,
|
||||||
): Promise<PerformCreateWithdrawalGroupResult> {
|
): Promise<PerformCreateWithdrawalGroupResult> {
|
||||||
const transactionId = constructTransactionIdentifier({
|
|
||||||
tag: TransactionType.Withdrawal,
|
|
||||||
withdrawalGroupId: prep.withdrawalGroup.withdrawalGroupId,
|
|
||||||
});
|
|
||||||
const { withdrawalGroup } = prep;
|
const { withdrawalGroup } = prep;
|
||||||
if (!prep.creationInfo) {
|
if (!prep.creationInfo) {
|
||||||
return { withdrawalGroup, transitionInfo: undefined };
|
return { withdrawalGroup, transitionInfo: undefined };
|
||||||
@ -2361,6 +2359,7 @@ export async function internalPerformCreateWithdrawalGroup(
|
|||||||
const exchange = await tx.exchanges.get(withdrawalGroup.exchangeBaseUrl);
|
const exchange = await tx.exchanges.get(withdrawalGroup.exchangeBaseUrl);
|
||||||
if (exchange) {
|
if (exchange) {
|
||||||
exchange.lastWithdrawal = TalerPreciseTimestamp.now();
|
exchange.lastWithdrawal = TalerPreciseTimestamp.now();
|
||||||
|
exchange.entryStatus = ExchangeEntryDbRecordStatus.Used;
|
||||||
await tx.exchanges.put(exchange);
|
await tx.exchanges.put(exchange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,6 +189,7 @@ import {
|
|||||||
} from "./operations/deposits.js";
|
} from "./operations/deposits.js";
|
||||||
import {
|
import {
|
||||||
acceptExchangeTermsOfService,
|
acceptExchangeTermsOfService,
|
||||||
|
addPresetExchangeEntry,
|
||||||
downloadTosFromAcceptedFormat,
|
downloadTosFromAcceptedFormat,
|
||||||
getExchangeDetails,
|
getExchangeDetails,
|
||||||
getExchangeRequestTimeout,
|
getExchangeRequestTimeout,
|
||||||
@ -533,6 +534,7 @@ async function fillDefaults(ws: InternalWalletState): Promise<void> {
|
|||||||
await tx.auditorTrust.put(c);
|
await tx.auditorTrust.put(c);
|
||||||
}
|
}
|
||||||
for (const baseUrl of ws.config.builtin.exchanges) {
|
for (const baseUrl of ws.config.builtin.exchanges) {
|
||||||
|
await addPresetExchangeEntry(tx, baseUrl);
|
||||||
const now = AbsoluteTime.now();
|
const now = AbsoluteTime.now();
|
||||||
provideExchangeRecordInTx(ws, tx, baseUrl, now);
|
provideExchangeRecordInTx(ws, tx, baseUrl, now);
|
||||||
}
|
}
|
||||||
@ -1688,8 +1690,7 @@ export class Wallet {
|
|||||||
|
|
||||||
public static defaultConfig: Readonly<WalletConfig> = {
|
public static defaultConfig: Readonly<WalletConfig> = {
|
||||||
builtin: {
|
builtin: {
|
||||||
//exchanges: ["https://exchange.demo.taler.net/"],
|
exchanges: ["https://exchange.demo.taler.net/"],
|
||||||
exchanges: [],
|
|
||||||
auditors: [
|
auditors: [
|
||||||
{
|
{
|
||||||
currency: "KUDOS",
|
currency: "KUDOS",
|
||||||
|
Loading…
Reference in New Issue
Block a user