2022-03-23 16:37:40 +01:00
|
|
|
/*
|
|
|
|
This file is part of GNU Taler
|
|
|
|
(C) 2019 GNUnet e.V.
|
|
|
|
|
|
|
|
GNU Taler is free software; you can redistribute it and/or modify it under the
|
|
|
|
terms of the GNU General Public License as published by the Free Software
|
|
|
|
Foundation; either version 3, or (at your option) any later version.
|
|
|
|
|
|
|
|
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
|
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Common interface of the internal wallet state. This object is passed
|
|
|
|
* to the various operations (exchange management, withdrawal, refresh, reserve
|
|
|
|
* management, etc.).
|
|
|
|
*
|
|
|
|
* Some operations can be accessed via this state object. This allows mutual
|
|
|
|
* recursion between operations, without having cyclic dependencies between
|
|
|
|
* the respective TypeScript files.
|
|
|
|
*
|
|
|
|
* (You can think of this as a "header file" for the wallet implementation.)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Imports.
|
|
|
|
*/
|
|
|
|
import {
|
|
|
|
WalletNotification,
|
|
|
|
BalancesResponse,
|
|
|
|
AmountJson,
|
|
|
|
DenominationPubKey,
|
|
|
|
TalerProtocolTimestamp,
|
2022-03-28 23:59:16 +02:00
|
|
|
CancellationToken,
|
2022-08-26 06:08:51 +02:00
|
|
|
DenominationInfo,
|
2022-10-08 20:56:57 +02:00
|
|
|
RefreshGroupId,
|
2022-10-15 11:52:07 +02:00
|
|
|
CoinRefreshRequest,
|
2022-10-08 20:56:57 +02:00
|
|
|
RefreshReason,
|
2022-03-23 16:37:40 +01:00
|
|
|
} from "@gnu-taler/taler-util";
|
2023-01-04 13:24:19 +01:00
|
|
|
import { CryptoDispatcher } from "./crypto/workers/crypto-dispatcher.js";
|
2022-03-23 21:24:23 +01:00
|
|
|
import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
|
2022-03-23 16:37:40 +01:00
|
|
|
import { ExchangeDetailsRecord, ExchangeRecord, WalletStoresV1 } from "./db.js";
|
|
|
|
import { PendingOperationsResponse } from "./pending-types.js";
|
|
|
|
import { AsyncOpMemoMap, AsyncOpMemoSingle } from "./util/asyncMemo.js";
|
|
|
|
import { HttpRequestLibrary } from "./util/http.js";
|
|
|
|
import { AsyncCondition } from "./util/promiseUtils.js";
|
|
|
|
import {
|
|
|
|
DbAccess,
|
|
|
|
GetReadOnlyAccess,
|
|
|
|
GetReadWriteAccess,
|
|
|
|
} from "./util/query.js";
|
|
|
|
import { TimerGroup } from "./util/timer.js";
|
|
|
|
|
|
|
|
export const EXCHANGE_COINS_LOCK = "exchange-coins-lock";
|
|
|
|
export const EXCHANGE_RESERVES_LOCK = "exchange-reserves-lock";
|
|
|
|
|
|
|
|
export interface TrustInfo {
|
|
|
|
isTrusted: boolean;
|
|
|
|
isAudited: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface MerchantInfo {
|
|
|
|
protocolVersionCurrent: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Interface for merchant-related operations.
|
|
|
|
*/
|
|
|
|
export interface MerchantOperations {
|
|
|
|
getMerchantInfo(
|
|
|
|
ws: InternalWalletState,
|
|
|
|
merchantBaseUrl: string,
|
|
|
|
): Promise<MerchantInfo>;
|
|
|
|
}
|
|
|
|
|
2022-10-08 20:56:57 +02:00
|
|
|
export interface RefreshOperations {
|
|
|
|
createRefreshGroup(
|
|
|
|
ws: InternalWalletState,
|
|
|
|
tx: GetReadWriteAccess<{
|
|
|
|
denominations: typeof WalletStoresV1.denominations;
|
|
|
|
coins: typeof WalletStoresV1.coins;
|
|
|
|
refreshGroups: typeof WalletStoresV1.refreshGroups;
|
|
|
|
coinAvailability: typeof WalletStoresV1.coinAvailability;
|
|
|
|
}>,
|
2022-10-15 11:52:07 +02:00
|
|
|
oldCoinPubs: CoinRefreshRequest[],
|
2022-10-08 20:56:57 +02:00
|
|
|
reason: RefreshReason,
|
|
|
|
): Promise<RefreshGroupId>;
|
|
|
|
}
|
|
|
|
|
2022-03-23 16:37:40 +01:00
|
|
|
/**
|
|
|
|
* Interface for exchange-related operations.
|
|
|
|
*/
|
|
|
|
export interface ExchangeOperations {
|
|
|
|
// FIXME: Should other operations maybe always use
|
|
|
|
// updateExchangeFromUrl?
|
|
|
|
getExchangeDetails(
|
|
|
|
tx: GetReadOnlyAccess<{
|
|
|
|
exchanges: typeof WalletStoresV1.exchanges;
|
|
|
|
exchangeDetails: typeof WalletStoresV1.exchangeDetails;
|
|
|
|
}>,
|
|
|
|
exchangeBaseUrl: string,
|
|
|
|
): Promise<ExchangeDetailsRecord | undefined>;
|
|
|
|
getExchangeTrust(
|
|
|
|
ws: InternalWalletState,
|
|
|
|
exchangeInfo: ExchangeRecord,
|
|
|
|
): Promise<TrustInfo>;
|
|
|
|
updateExchangeFromUrl(
|
|
|
|
ws: InternalWalletState,
|
|
|
|
baseUrl: string,
|
2022-03-29 13:47:32 +02:00
|
|
|
options?: {
|
|
|
|
forceNow?: boolean;
|
|
|
|
cancellationToken?: CancellationToken;
|
|
|
|
},
|
2022-03-23 16:37:40 +01:00
|
|
|
): Promise<{
|
|
|
|
exchange: ExchangeRecord;
|
|
|
|
exchangeDetails: ExchangeDetailsRecord;
|
|
|
|
}>;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface RecoupOperations {
|
|
|
|
createRecoupGroup(
|
|
|
|
ws: InternalWalletState,
|
|
|
|
tx: GetReadWriteAccess<{
|
|
|
|
recoupGroups: typeof WalletStoresV1.recoupGroups;
|
|
|
|
denominations: typeof WalletStoresV1.denominations;
|
|
|
|
refreshGroups: typeof WalletStoresV1.refreshGroups;
|
|
|
|
coins: typeof WalletStoresV1.coins;
|
|
|
|
}>,
|
2022-08-26 01:18:01 +02:00
|
|
|
exchangeBaseUrl: string,
|
2022-03-23 16:37:40 +01:00
|
|
|
coinPubs: string[],
|
|
|
|
): Promise<string>;
|
|
|
|
processRecoupGroup(
|
|
|
|
ws: InternalWalletState,
|
|
|
|
recoupGroupId: string,
|
2022-03-29 13:47:32 +02:00
|
|
|
options?: {
|
|
|
|
forceNow?: boolean;
|
|
|
|
},
|
2022-03-23 16:37:40 +01:00
|
|
|
): Promise<void>;
|
|
|
|
}
|
|
|
|
|
|
|
|
export type NotificationListener = (n: WalletNotification) => void;
|
|
|
|
|
2022-09-23 18:56:21 +02:00
|
|
|
export interface ActiveLongpollInfo {
|
|
|
|
[opId: string]: {
|
|
|
|
cancel: () => void;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-03-23 16:37:40 +01:00
|
|
|
/**
|
|
|
|
* Internal, shard wallet state that is used by the implementation
|
|
|
|
* of wallet operations.
|
|
|
|
*
|
|
|
|
* FIXME: This should not be exported anywhere from the taler-wallet-core package,
|
|
|
|
* as it's an opaque implementation detail.
|
|
|
|
*/
|
|
|
|
export interface InternalWalletState {
|
2022-09-23 18:56:21 +02:00
|
|
|
/**
|
|
|
|
* Active longpoll operations.
|
|
|
|
*/
|
|
|
|
activeLongpoll: ActiveLongpollInfo;
|
2022-03-23 16:37:40 +01:00
|
|
|
|
2022-03-29 13:47:32 +02:00
|
|
|
cryptoApi: TalerCryptoInterface;
|
2022-03-28 23:59:16 +02:00
|
|
|
|
2022-03-23 16:37:40 +01:00
|
|
|
timerGroup: TimerGroup;
|
|
|
|
stopped: boolean;
|
|
|
|
|
|
|
|
insecureTrustExchange: boolean;
|
|
|
|
|
2022-05-03 17:53:32 +02:00
|
|
|
batchWithdrawal: boolean;
|
|
|
|
|
2022-03-23 16:37:40 +01:00
|
|
|
/**
|
|
|
|
* Asynchronous condition to interrupt the sleep of the
|
|
|
|
* retry loop.
|
|
|
|
*
|
|
|
|
* Used to allow processing of new work faster.
|
|
|
|
*/
|
|
|
|
latch: AsyncCondition;
|
|
|
|
|
|
|
|
listeners: NotificationListener[];
|
|
|
|
|
|
|
|
initCalled: boolean;
|
|
|
|
|
|
|
|
merchantInfoCache: Record<string, MerchantInfo>;
|
|
|
|
|
|
|
|
exchangeOps: ExchangeOperations;
|
|
|
|
recoupOps: RecoupOperations;
|
|
|
|
merchantOps: MerchantOperations;
|
2022-10-08 20:56:57 +02:00
|
|
|
refreshOps: RefreshOperations;
|
2022-03-23 16:37:40 +01:00
|
|
|
|
2022-10-12 22:27:50 +02:00
|
|
|
devModeActive: boolean;
|
|
|
|
|
2022-03-23 16:37:40 +01:00
|
|
|
getDenomInfo(
|
|
|
|
ws: InternalWalletState,
|
|
|
|
tx: GetReadOnlyAccess<{
|
|
|
|
denominations: typeof WalletStoresV1.denominations;
|
|
|
|
}>,
|
|
|
|
exchangeBaseUrl: string,
|
|
|
|
denomPubHash: string,
|
2022-09-16 17:35:06 +02:00
|
|
|
): Promise<DenominationInfo | undefined>;
|
2022-03-23 16:37:40 +01:00
|
|
|
|
|
|
|
db: DbAccess<typeof WalletStoresV1>;
|
|
|
|
http: HttpRequestLibrary;
|
|
|
|
|
|
|
|
notify(n: WalletNotification): void;
|
|
|
|
|
|
|
|
addNotificationListener(f: (n: WalletNotification) => void): void;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stop ongoing processing.
|
|
|
|
*/
|
|
|
|
stop(): void;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Run an async function after acquiring a list of locks, identified
|
|
|
|
* by string tokens.
|
|
|
|
*/
|
|
|
|
runSequentialized<T>(tokens: string[], f: () => Promise<T>): Promise<T>;
|
|
|
|
|
|
|
|
runUntilDone(req?: { maxRetries?: number }): Promise<void>;
|
|
|
|
}
|