2021-01-13 00:50:56 +01:00
|
|
|
import {
|
|
|
|
openDatabase,
|
|
|
|
Database,
|
|
|
|
Store,
|
|
|
|
Index,
|
|
|
|
AnyStoreMap,
|
|
|
|
} from "./util/query";
|
2020-12-02 14:55:04 +01:00
|
|
|
import {
|
|
|
|
IDBFactory,
|
|
|
|
IDBDatabase,
|
|
|
|
IDBObjectStore,
|
|
|
|
IDBTransaction,
|
2021-03-17 17:56:37 +01:00
|
|
|
IDBKeyPath,
|
2021-01-30 16:35:55 +01:00
|
|
|
} from "@gnu-taler/idb-bridge";
|
2020-12-02 14:55:04 +01:00
|
|
|
import { Logger } from "./util/logging";
|
2021-05-12 14:16:01 +02:00
|
|
|
import {
|
|
|
|
AmountJson,
|
|
|
|
AmountString,
|
|
|
|
Auditor,
|
|
|
|
CoinDepositPermission,
|
|
|
|
ContractTerms,
|
|
|
|
Duration,
|
|
|
|
ExchangeSignKeyJson,
|
|
|
|
InternationalizedString,
|
|
|
|
MerchantInfo,
|
|
|
|
Product,
|
|
|
|
RefreshReason,
|
|
|
|
ReserveTransaction,
|
|
|
|
TalerErrorDetails,
|
|
|
|
Timestamp,
|
|
|
|
} from "@gnu-taler/taler-util";
|
2021-03-17 17:56:37 +01:00
|
|
|
import { RetryInfo } from "./util/retries.js";
|
|
|
|
import { PayCoinSelection } from "./util/coinSelection.js";
|
2019-07-31 01:33:56 +02:00
|
|
|
|
2020-05-08 14:15:23 +02:00
|
|
|
/**
|
2020-09-08 17:46:11 +02:00
|
|
|
* Name of the Taler database. This is effectively the major
|
|
|
|
* version of the DB schema. Whenever it changes, custom import logic
|
|
|
|
* for all previous versions must be written, which should be
|
|
|
|
* avoided.
|
2020-05-08 14:15:23 +02:00
|
|
|
*/
|
2021-01-13 00:50:56 +01:00
|
|
|
const TALER_DB_NAME = "taler-wallet-main-v2";
|
|
|
|
|
|
|
|
const TALER_META_DB_NAME = "taler-wallet-meta";
|
|
|
|
|
|
|
|
const CURRENT_DB_CONFIG_KEY = "currentMainDbName";
|
2019-07-31 01:33:56 +02:00
|
|
|
|
2019-12-13 13:10:20 +01:00
|
|
|
/**
|
2020-05-08 14:15:23 +02:00
|
|
|
* Current database minor version, should be incremented
|
|
|
|
* each time we do minor schema changes on the database.
|
|
|
|
* A change is considered minor when fields are added in a
|
|
|
|
* backwards-compatible way or object stores and indices
|
|
|
|
* are added.
|
2019-12-13 13:10:20 +01:00
|
|
|
*/
|
2021-01-13 00:50:56 +01:00
|
|
|
export const WALLET_DB_MINOR_VERSION = 1;
|
2020-11-16 14:12:37 +01:00
|
|
|
|
|
|
|
const logger = new Logger("db.ts");
|
2019-12-13 13:10:20 +01:00
|
|
|
|
2021-01-13 00:50:56 +01:00
|
|
|
function upgradeFromStoreMap(
|
|
|
|
storeMap: AnyStoreMap,
|
|
|
|
db: IDBDatabase,
|
|
|
|
oldVersion: number,
|
|
|
|
newVersion: number,
|
|
|
|
upgradeTransaction: IDBTransaction,
|
|
|
|
): void {
|
|
|
|
if (oldVersion === 0) {
|
|
|
|
for (const n in storeMap) {
|
|
|
|
if ((storeMap as any)[n] instanceof Store) {
|
|
|
|
const si: Store<string, any> = (storeMap as any)[n];
|
|
|
|
const s = db.createObjectStore(si.name, si.storeParams);
|
|
|
|
for (const indexName in si as any) {
|
|
|
|
if ((si as any)[indexName] instanceof Index) {
|
|
|
|
const ii: Index<string, string, any, any> = (si as any)[indexName];
|
|
|
|
s.createIndex(ii.indexName, ii.keyPath, ii.options);
|
2020-11-16 14:12:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-13 00:50:56 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (oldVersion === newVersion) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
logger.info(`upgrading database from ${oldVersion} to ${newVersion}`);
|
|
|
|
for (const n in Stores) {
|
|
|
|
if ((Stores as any)[n] instanceof Store) {
|
|
|
|
const si: Store<string, any> = (Stores as any)[n];
|
|
|
|
let s: IDBObjectStore;
|
|
|
|
const storeVersionAdded = si.storeParams?.versionAdded ?? 1;
|
|
|
|
if (storeVersionAdded > oldVersion) {
|
|
|
|
s = db.createObjectStore(si.name, si.storeParams);
|
|
|
|
} else {
|
|
|
|
s = upgradeTransaction.objectStore(si.name);
|
|
|
|
}
|
|
|
|
for (const indexName in si as any) {
|
|
|
|
if ((si as any)[indexName] instanceof Index) {
|
|
|
|
const ii: Index<string, string, any, any> = (si as any)[indexName];
|
|
|
|
const indexVersionAdded = ii.options?.versionAdded ?? 0;
|
|
|
|
if (
|
|
|
|
indexVersionAdded > oldVersion ||
|
|
|
|
storeVersionAdded > oldVersion
|
|
|
|
) {
|
|
|
|
s.createIndex(ii.indexName, ii.keyPath, ii.options);
|
2019-12-19 13:48:37 +01:00
|
|
|
}
|
|
|
|
}
|
2020-11-16 14:12:37 +01:00
|
|
|
}
|
2019-12-19 13:48:37 +01:00
|
|
|
}
|
2021-01-13 00:50:56 +01:00
|
|
|
}
|
|
|
|
}
|
2019-12-19 13:48:37 +01:00
|
|
|
|
2021-01-13 00:50:56 +01:00
|
|
|
function onTalerDbUpgradeNeeded(
|
|
|
|
db: IDBDatabase,
|
|
|
|
oldVersion: number,
|
|
|
|
newVersion: number,
|
|
|
|
upgradeTransaction: IDBTransaction,
|
|
|
|
) {
|
|
|
|
upgradeFromStoreMap(Stores, db, oldVersion, newVersion, upgradeTransaction);
|
|
|
|
}
|
|
|
|
|
|
|
|
function onMetaDbUpgradeNeeded(
|
|
|
|
db: IDBDatabase,
|
|
|
|
oldVersion: number,
|
|
|
|
newVersion: number,
|
|
|
|
upgradeTransaction: IDBTransaction,
|
|
|
|
) {
|
|
|
|
upgradeFromStoreMap(
|
|
|
|
MetaStores,
|
|
|
|
db,
|
|
|
|
oldVersion,
|
|
|
|
newVersion,
|
|
|
|
upgradeTransaction,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a promise that resolves
|
|
|
|
* to the taler wallet db.
|
|
|
|
*/
|
|
|
|
export async function openTalerDatabase(
|
|
|
|
idbFactory: IDBFactory,
|
|
|
|
onVersionChange: () => void,
|
|
|
|
): Promise<Database<typeof Stores>> {
|
|
|
|
const metaDbHandle = await openDatabase(
|
|
|
|
idbFactory,
|
|
|
|
TALER_META_DB_NAME,
|
|
|
|
1,
|
|
|
|
() => {},
|
|
|
|
onMetaDbUpgradeNeeded,
|
|
|
|
);
|
|
|
|
|
|
|
|
const metaDb = new Database(metaDbHandle, MetaStores);
|
|
|
|
let currentMainVersion: string | undefined;
|
|
|
|
await metaDb.runWithWriteTransaction([MetaStores.metaConfig], async (tx) => {
|
|
|
|
const dbVersionRecord = await tx.get(
|
|
|
|
MetaStores.metaConfig,
|
|
|
|
CURRENT_DB_CONFIG_KEY,
|
|
|
|
);
|
|
|
|
if (!dbVersionRecord) {
|
|
|
|
currentMainVersion = TALER_DB_NAME;
|
|
|
|
await tx.put(MetaStores.metaConfig, {
|
|
|
|
key: CURRENT_DB_CONFIG_KEY,
|
|
|
|
value: TALER_DB_NAME,
|
|
|
|
});
|
|
|
|
} else {
|
2021-01-13 00:55:47 +01:00
|
|
|
currentMainVersion = dbVersionRecord.value;
|
2021-01-13 00:50:56 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (currentMainVersion !== TALER_DB_NAME) {
|
|
|
|
// In the future, the migration logic will be implemented here.
|
|
|
|
throw Error(`migration from database ${currentMainVersion} not supported`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const mainDbHandle = await openDatabase(
|
2019-12-12 22:39:45 +01:00
|
|
|
idbFactory,
|
|
|
|
TALER_DB_NAME,
|
2020-07-09 15:26:18 +02:00
|
|
|
WALLET_DB_MINOR_VERSION,
|
2019-12-12 22:39:45 +01:00
|
|
|
onVersionChange,
|
2021-01-13 00:50:56 +01:00
|
|
|
onTalerDbUpgradeNeeded,
|
2019-12-12 22:39:45 +01:00
|
|
|
);
|
2021-01-13 00:50:56 +01:00
|
|
|
|
|
|
|
return new Database(mainDbHandle, Stores);
|
2019-07-31 01:33:56 +02:00
|
|
|
}
|
|
|
|
|
2020-08-10 11:07:20 +02:00
|
|
|
export function deleteTalerDatabase(idbFactory: IDBFactory): void {
|
2019-12-12 22:39:45 +01:00
|
|
|
Database.deleteDatabase(idbFactory, TALER_DB_NAME);
|
2019-12-19 13:48:37 +01:00
|
|
|
}
|
2021-03-17 17:56:37 +01:00
|
|
|
|
|
|
|
export enum ReserveRecordStatus {
|
|
|
|
/**
|
|
|
|
* Reserve must be registered with the bank.
|
|
|
|
*/
|
|
|
|
REGISTERING_BANK = "registering-bank",
|
|
|
|
|
|
|
|
/**
|
|
|
|
* We've registered reserve's information with the bank
|
|
|
|
* and are now waiting for the user to confirm the withdraw
|
|
|
|
* with the bank (typically 2nd factor auth).
|
|
|
|
*/
|
|
|
|
WAIT_CONFIRM_BANK = "wait-confirm-bank",
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Querying reserve status with the exchange.
|
|
|
|
*/
|
|
|
|
QUERYING_STATUS = "querying-status",
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The corresponding withdraw record has been created.
|
|
|
|
* No further processing is done, unless explicitly requested
|
|
|
|
* by the user.
|
|
|
|
*/
|
|
|
|
DORMANT = "dormant",
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The bank aborted the withdrawal.
|
|
|
|
*/
|
|
|
|
BANK_ABORTED = "bank-aborted",
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface ReserveBankInfo {
|
|
|
|
/**
|
|
|
|
* Status URL that the wallet will use to query the status
|
|
|
|
* of the Taler withdrawal operation on the bank's side.
|
|
|
|
*/
|
|
|
|
statusUrl: string;
|
|
|
|
|
|
|
|
confirmUrl?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Exchange payto URI that the bank will use to fund the reserve.
|
|
|
|
*/
|
|
|
|
exchangePaytoUri: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A reserve record as stored in the wallet's database.
|
|
|
|
*/
|
|
|
|
export interface ReserveRecord {
|
|
|
|
/**
|
|
|
|
* The reserve public key.
|
|
|
|
*/
|
|
|
|
reservePub: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The reserve private key.
|
|
|
|
*/
|
|
|
|
reservePriv: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The exchange base URL.
|
|
|
|
*/
|
|
|
|
exchangeBaseUrl: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Currency of the reserve.
|
|
|
|
*/
|
|
|
|
currency: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Time when the reserve was created.
|
|
|
|
*/
|
|
|
|
timestampCreated: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Time when the information about this reserve was posted to the bank.
|
|
|
|
*
|
|
|
|
* Only applies if bankWithdrawStatusUrl is defined.
|
|
|
|
*
|
|
|
|
* Set to 0 if that hasn't happened yet.
|
|
|
|
*/
|
|
|
|
timestampReserveInfoPosted: Timestamp | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Time when the reserve was confirmed by the bank.
|
|
|
|
*
|
|
|
|
* Set to undefined if not confirmed yet.
|
|
|
|
*/
|
|
|
|
timestampBankConfirmed: Timestamp | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wire information (as payto URI) for the bank account that
|
2021-04-27 23:42:25 +02:00
|
|
|
* transferred funds for this reserve.
|
2021-03-17 17:56:37 +01:00
|
|
|
*/
|
|
|
|
senderWire?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Amount that was sent by the user to fund the reserve.
|
|
|
|
*/
|
|
|
|
instructedAmount: AmountJson;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extra state for when this is a withdrawal involving
|
|
|
|
* a Taler-integrated bank.
|
|
|
|
*/
|
|
|
|
bankInfo?: ReserveBankInfo;
|
|
|
|
|
|
|
|
initialWithdrawalGroupId: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Did we start the first withdrawal for this reserve?
|
|
|
|
*
|
|
|
|
* We only report a pending withdrawal for the reserve before
|
|
|
|
* the first withdrawal has started.
|
|
|
|
*/
|
|
|
|
initialWithdrawalStarted: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initial denomination selection, stored here so that
|
|
|
|
* we can show this information in the transactions/balances
|
|
|
|
* before we have a withdrawal group.
|
|
|
|
*/
|
|
|
|
initialDenomSel: DenomSelectionState;
|
|
|
|
|
|
|
|
reserveStatus: ReserveRecordStatus;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Was a reserve query requested? If so, query again instead
|
|
|
|
* of going into dormant status.
|
|
|
|
*/
|
|
|
|
requestedQuery: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Time of the last successful status query.
|
|
|
|
*/
|
|
|
|
lastSuccessfulStatusQuery: Timestamp | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retry info. This field is present even if no retry is scheduled,
|
|
|
|
* because we need it to be present for the index on the object store
|
|
|
|
* to work.
|
|
|
|
*/
|
|
|
|
retryInfo: RetryInfo;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Last error that happened in a reserve operation
|
|
|
|
* (either talking to the bank or the exchange).
|
|
|
|
*/
|
|
|
|
lastError: TalerErrorDetails | undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Auditor record as stored with currencies in the exchange database.
|
|
|
|
*/
|
|
|
|
export interface AuditorRecord {
|
|
|
|
/**
|
|
|
|
* Base url of the auditor.
|
|
|
|
*/
|
|
|
|
baseUrl: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Public signing key of the auditor.
|
|
|
|
*/
|
|
|
|
auditorPub: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Time when the auditing expires.
|
|
|
|
*/
|
|
|
|
expirationStamp: number;
|
|
|
|
}
|
|
|
|
|
2021-05-20 13:14:47 +02:00
|
|
|
export interface AuditorTrustRecord {
|
|
|
|
/**
|
|
|
|
* Currency that we trust this auditor for.
|
|
|
|
*/
|
|
|
|
currency: string;
|
|
|
|
|
2021-03-17 17:56:37 +01:00
|
|
|
/**
|
2021-05-12 16:18:32 +02:00
|
|
|
* Base URL of the auditor.
|
2021-03-17 17:56:37 +01:00
|
|
|
*/
|
2021-05-12 16:18:32 +02:00
|
|
|
auditorBaseUrl: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Public key of the auditor.
|
|
|
|
*/
|
|
|
|
auditorPub: string;
|
2021-03-17 17:56:37 +01:00
|
|
|
|
|
|
|
/**
|
2021-05-12 16:18:32 +02:00
|
|
|
* UIDs for the operation of adding this auditor
|
|
|
|
* as a trusted auditor.
|
|
|
|
*/
|
|
|
|
uids: string[];
|
|
|
|
}
|
|
|
|
|
2021-05-20 13:14:47 +02:00
|
|
|
export interface ExchangeTrustRecord {
|
|
|
|
/**
|
|
|
|
* Currency that we trust this exchange for.
|
|
|
|
*/
|
|
|
|
currency: string;
|
|
|
|
|
2021-05-12 16:18:32 +02:00
|
|
|
/**
|
|
|
|
* Canonicalized exchange base URL.
|
2021-03-17 17:56:37 +01:00
|
|
|
*/
|
|
|
|
exchangeBaseUrl: string;
|
2021-05-12 16:18:32 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Master public key of the exchange.
|
|
|
|
*/
|
|
|
|
exchangeMasterPub: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* UIDs for the operation of adding this exchange
|
|
|
|
* as trusted.
|
|
|
|
*/
|
|
|
|
uids: string[];
|
2021-03-17 17:56:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Status of a denomination.
|
|
|
|
*/
|
|
|
|
export enum DenominationStatus {
|
|
|
|
/**
|
|
|
|
* Verification was delayed.
|
|
|
|
*/
|
|
|
|
Unverified = "unverified",
|
|
|
|
/**
|
|
|
|
* Verified as valid.
|
|
|
|
*/
|
|
|
|
VerifiedGood = "verified-good",
|
|
|
|
/**
|
|
|
|
* Verified as invalid.
|
|
|
|
*/
|
|
|
|
VerifiedBad = "verified-bad",
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Denomination record as stored in the wallet's database.
|
|
|
|
*/
|
|
|
|
export interface DenominationRecord {
|
|
|
|
/**
|
|
|
|
* Value of one coin of the denomination.
|
|
|
|
*/
|
|
|
|
value: AmountJson;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The denomination public key.
|
|
|
|
*/
|
|
|
|
denomPub: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Hash of the denomination public key.
|
|
|
|
* Stored in the database for faster lookups.
|
|
|
|
*/
|
|
|
|
denomPubHash: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fee for withdrawing.
|
|
|
|
*/
|
|
|
|
feeWithdraw: AmountJson;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fee for depositing.
|
|
|
|
*/
|
|
|
|
feeDeposit: AmountJson;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fee for refreshing.
|
|
|
|
*/
|
|
|
|
feeRefresh: AmountJson;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fee for refunding.
|
|
|
|
*/
|
|
|
|
feeRefund: AmountJson;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Validity start date of the denomination.
|
|
|
|
*/
|
|
|
|
stampStart: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Date after which the currency can't be withdrawn anymore.
|
|
|
|
*/
|
|
|
|
stampExpireWithdraw: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Date after the denomination officially doesn't exist anymore.
|
|
|
|
*/
|
|
|
|
stampExpireLegal: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Data after which coins of this denomination can't be deposited anymore.
|
|
|
|
*/
|
|
|
|
stampExpireDeposit: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Signature by the exchange's master key over the denomination
|
|
|
|
* information.
|
|
|
|
*/
|
|
|
|
masterSig: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Did we verify the signature on the denomination?
|
|
|
|
*
|
|
|
|
* FIXME: Rename to "verificationStatus"?
|
|
|
|
*/
|
|
|
|
status: DenominationStatus;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Was this denomination still offered by the exchange the last time
|
|
|
|
* we checked?
|
|
|
|
* Only false when the exchange redacts a previously published denomination.
|
|
|
|
*/
|
|
|
|
isOffered: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Did the exchange revoke the denomination?
|
|
|
|
* When this field is set to true in the database, the same transaction
|
|
|
|
* should also mark all affected coins as revoked.
|
|
|
|
*/
|
|
|
|
isRevoked: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base URL of the exchange.
|
|
|
|
*/
|
|
|
|
exchangeBaseUrl: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Details about the exchange that we only know after
|
|
|
|
* querying /keys and /wire.
|
|
|
|
*/
|
|
|
|
export interface ExchangeDetails {
|
|
|
|
/**
|
|
|
|
* Master public key of the exchange.
|
|
|
|
*/
|
|
|
|
masterPublicKey: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Auditors (partially) auditing the exchange.
|
|
|
|
*/
|
|
|
|
auditors: Auditor[];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Currency that the exchange offers.
|
|
|
|
*/
|
|
|
|
currency: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Last observed protocol version.
|
|
|
|
*/
|
|
|
|
protocolVersion: string;
|
|
|
|
|
|
|
|
reserveClosingDelay: Duration;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Signing keys we got from the exchange, can also contain
|
|
|
|
* older signing keys that are not returned by /keys anymore.
|
|
|
|
*/
|
|
|
|
signingKeys: ExchangeSignKeyJson[];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Timestamp for last update.
|
|
|
|
*/
|
|
|
|
lastUpdateTime: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* When should we next update the information about the exchange?
|
|
|
|
*/
|
|
|
|
nextUpdateTime: Timestamp;
|
|
|
|
}
|
|
|
|
|
|
|
|
export enum ExchangeUpdateStatus {
|
|
|
|
FetchKeys = "fetch-keys",
|
|
|
|
FetchWire = "fetch-wire",
|
|
|
|
FetchTerms = "fetch-terms",
|
|
|
|
FinalizeUpdate = "finalize-update",
|
|
|
|
Finished = "finished",
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface ExchangeBankAccount {
|
|
|
|
payto_uri: string;
|
|
|
|
master_sig: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface ExchangeWireInfo {
|
|
|
|
feesForType: { [wireMethod: string]: WireFee[] };
|
|
|
|
accounts: ExchangeBankAccount[];
|
|
|
|
}
|
|
|
|
|
|
|
|
export enum ExchangeUpdateReason {
|
|
|
|
Initial = "initial",
|
|
|
|
Forced = "forced",
|
|
|
|
Scheduled = "scheduled",
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Exchange record as stored in the wallet's database.
|
|
|
|
*/
|
|
|
|
export interface ExchangeRecord {
|
|
|
|
/**
|
|
|
|
* Base url of the exchange.
|
|
|
|
*/
|
|
|
|
baseUrl: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Did we finish adding the exchange?
|
|
|
|
*/
|
|
|
|
addComplete: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Is this a permanent or temporary exchange record?
|
|
|
|
*/
|
|
|
|
permanent: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Was the exchange added as a built-in exchange?
|
|
|
|
*/
|
|
|
|
builtIn: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Details, once known.
|
|
|
|
*/
|
|
|
|
details: ExchangeDetails | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mapping from wire method type to the wire fee.
|
|
|
|
*/
|
|
|
|
wireInfo: ExchangeWireInfo | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Terms of service text or undefined if not downloaded yet.
|
|
|
|
*
|
|
|
|
* This is just used as a cache of the last downloaded ToS.
|
|
|
|
*/
|
|
|
|
termsOfServiceText: string | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ETag for last terms of service download.
|
|
|
|
*/
|
|
|
|
termsOfServiceLastEtag: string | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ETag for last terms of service download.
|
|
|
|
*/
|
|
|
|
termsOfServiceAcceptedEtag: string | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Time when the update to the exchange has been started or
|
|
|
|
* undefined if no update is in progress.
|
|
|
|
*/
|
|
|
|
updateStarted: Timestamp | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Status of updating the info about the exchange.
|
|
|
|
*/
|
|
|
|
updateStatus: ExchangeUpdateStatus;
|
|
|
|
|
|
|
|
updateReason?: ExchangeUpdateReason;
|
|
|
|
|
|
|
|
lastError?: TalerErrorDetails;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retry status for fetching updated information about the exchange.
|
|
|
|
*/
|
|
|
|
retryInfo: RetryInfo;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Next time that we should check if coins need to be refreshed.
|
|
|
|
*
|
|
|
|
* Updated whenever the exchange's denominations are updated or when
|
|
|
|
* the refresh check has been done.
|
|
|
|
*/
|
|
|
|
nextRefreshCheck?: Timestamp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A coin that isn't yet signed by an exchange.
|
|
|
|
*/
|
|
|
|
export interface PlanchetRecord {
|
|
|
|
/**
|
|
|
|
* Public key of the coin.
|
|
|
|
*/
|
|
|
|
coinPub: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Private key of the coin.
|
|
|
|
*/
|
|
|
|
coinPriv: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Withdrawal group that this planchet belongs to
|
|
|
|
* (or the empty string).
|
|
|
|
*/
|
|
|
|
withdrawalGroupId: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Index within the withdrawal group (or -1).
|
|
|
|
*/
|
|
|
|
coinIdx: number;
|
|
|
|
|
|
|
|
withdrawalDone: boolean;
|
|
|
|
|
|
|
|
lastError: TalerErrorDetails | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Public key of the reserve that this planchet
|
|
|
|
* is being withdrawn from.
|
|
|
|
*
|
|
|
|
* Can be the empty string (non-null/undefined for DB indexing)
|
|
|
|
* if this is a tipping reserve.
|
|
|
|
*/
|
|
|
|
reservePub: string;
|
|
|
|
|
|
|
|
denomPubHash: string;
|
|
|
|
|
|
|
|
denomPub: string;
|
|
|
|
|
|
|
|
blindingKey: string;
|
|
|
|
|
|
|
|
withdrawSig: string;
|
|
|
|
|
|
|
|
coinEv: string;
|
|
|
|
|
|
|
|
coinEvHash: string;
|
|
|
|
|
|
|
|
coinValue: AmountJson;
|
|
|
|
|
|
|
|
isFromTip: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Planchet for a coin during refrehs.
|
|
|
|
*/
|
|
|
|
export interface RefreshPlanchet {
|
|
|
|
/**
|
|
|
|
* Public key for the coin.
|
|
|
|
*/
|
|
|
|
publicKey: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Private key for the coin.
|
|
|
|
*/
|
|
|
|
privateKey: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Blinded public key.
|
|
|
|
*/
|
|
|
|
coinEv: string;
|
|
|
|
|
|
|
|
coinEvHash: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Blinding key used.
|
|
|
|
*/
|
|
|
|
blindingKey: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Status of a coin.
|
|
|
|
*/
|
|
|
|
export enum CoinStatus {
|
|
|
|
/**
|
|
|
|
* Withdrawn and never shown to anybody.
|
|
|
|
*/
|
|
|
|
Fresh = "fresh",
|
|
|
|
/**
|
|
|
|
* A coin that has been spent and refreshed.
|
|
|
|
*/
|
|
|
|
Dormant = "dormant",
|
|
|
|
}
|
|
|
|
|
|
|
|
export enum CoinSourceType {
|
|
|
|
Withdraw = "withdraw",
|
|
|
|
Refresh = "refresh",
|
|
|
|
Tip = "tip",
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface WithdrawCoinSource {
|
|
|
|
type: CoinSourceType.Withdraw;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Can be the empty string for orphaned coins.
|
|
|
|
*/
|
|
|
|
withdrawalGroupId: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Index of the coin in the withdrawal session.
|
|
|
|
*/
|
|
|
|
coinIndex: number;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reserve public key for the reserve we got this coin from.
|
|
|
|
*/
|
|
|
|
reservePub: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface RefreshCoinSource {
|
|
|
|
type: CoinSourceType.Refresh;
|
|
|
|
oldCoinPub: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface TipCoinSource {
|
|
|
|
type: CoinSourceType.Tip;
|
|
|
|
walletTipId: string;
|
|
|
|
coinIndex: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
export type CoinSource = WithdrawCoinSource | RefreshCoinSource | TipCoinSource;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* CoinRecord as stored in the "coins" data store
|
|
|
|
* of the wallet database.
|
|
|
|
*/
|
|
|
|
export interface CoinRecord {
|
|
|
|
/**
|
|
|
|
* Where did the coin come from? Used for recouping coins.
|
|
|
|
*/
|
|
|
|
coinSource: CoinSource;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Public key of the coin.
|
|
|
|
*/
|
|
|
|
coinPub: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Private key to authorize operations on the coin.
|
|
|
|
*/
|
|
|
|
coinPriv: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Key used by the exchange used to sign the coin.
|
|
|
|
*/
|
|
|
|
denomPub: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Hash of the public key that signs the coin.
|
|
|
|
*/
|
|
|
|
denomPubHash: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unblinded signature by the exchange.
|
|
|
|
*/
|
|
|
|
denomSig: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Amount that's left on the coin.
|
|
|
|
*/
|
|
|
|
currentAmount: AmountJson;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base URL that identifies the exchange from which we got the
|
|
|
|
* coin.
|
|
|
|
*/
|
|
|
|
exchangeBaseUrl: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The coin is currently suspended, and will not be used for payments.
|
|
|
|
*/
|
|
|
|
suspended: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Blinding key used when withdrawing the coin.
|
|
|
|
* Potentionally used again during payback.
|
|
|
|
*/
|
|
|
|
blindingKey: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Hash of the coin envelope.
|
|
|
|
*
|
|
|
|
* Stored here for indexing purposes, so that when looking at a
|
|
|
|
* reserve history, we can quickly find the coin for a withdrawal transaction.
|
|
|
|
*/
|
|
|
|
coinEvHash: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Status of the coin.
|
|
|
|
*/
|
|
|
|
status: CoinStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
export enum ProposalStatus {
|
|
|
|
/**
|
|
|
|
* Not downloaded yet.
|
|
|
|
*/
|
|
|
|
DOWNLOADING = "downloading",
|
|
|
|
/**
|
|
|
|
* Proposal downloaded, but the user needs to accept/reject it.
|
|
|
|
*/
|
|
|
|
PROPOSED = "proposed",
|
|
|
|
/**
|
|
|
|
* The user has accepted the proposal.
|
|
|
|
*/
|
|
|
|
ACCEPTED = "accepted",
|
|
|
|
/**
|
|
|
|
* The user has rejected the proposal.
|
|
|
|
*/
|
|
|
|
REFUSED = "refused",
|
|
|
|
/**
|
|
|
|
* Downloading or processing the proposal has failed permanently.
|
|
|
|
*/
|
|
|
|
PERMANENTLY_FAILED = "permanently-failed",
|
|
|
|
/**
|
|
|
|
* Downloaded proposal was detected as a re-purchase.
|
|
|
|
*/
|
|
|
|
REPURCHASE = "repurchase",
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface ProposalDownload {
|
|
|
|
/**
|
|
|
|
* The contract that was offered by the merchant.
|
|
|
|
*/
|
|
|
|
contractTermsRaw: any;
|
|
|
|
|
|
|
|
contractData: WalletContractData;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Record for a downloaded order, stored in the wallet's database.
|
|
|
|
*/
|
|
|
|
export interface ProposalRecord {
|
|
|
|
orderId: string;
|
|
|
|
|
|
|
|
merchantBaseUrl: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Downloaded data from the merchant.
|
|
|
|
*/
|
|
|
|
download: ProposalDownload | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unique ID when the order is stored in the wallet DB.
|
|
|
|
*/
|
|
|
|
proposalId: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Timestamp (in ms) of when the record
|
|
|
|
* was created.
|
|
|
|
*/
|
|
|
|
timestamp: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Private key for the nonce.
|
|
|
|
*/
|
|
|
|
noncePriv: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Public key for the nonce.
|
|
|
|
*/
|
|
|
|
noncePub: string;
|
|
|
|
|
|
|
|
claimToken: string | undefined;
|
|
|
|
|
|
|
|
proposalStatus: ProposalStatus;
|
|
|
|
|
|
|
|
repurchaseProposalId: string | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Session ID we got when downloading the contract.
|
|
|
|
*/
|
|
|
|
downloadSessionId?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retry info, even present when the operation isn't active to allow indexing
|
|
|
|
* on the next retry timestamp.
|
|
|
|
*/
|
|
|
|
retryInfo: RetryInfo;
|
|
|
|
|
|
|
|
lastError: TalerErrorDetails | undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Status of a tip we got from a merchant.
|
|
|
|
*/
|
|
|
|
export interface TipRecord {
|
|
|
|
lastError: TalerErrorDetails | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Has the user accepted the tip? Only after the tip has been accepted coins
|
|
|
|
* withdrawn from the tip may be used.
|
|
|
|
*/
|
|
|
|
acceptedTimestamp: Timestamp | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The tipped amount.
|
|
|
|
*/
|
|
|
|
tipAmountRaw: AmountJson;
|
|
|
|
|
|
|
|
tipAmountEffective: AmountJson;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Timestamp, the tip can't be picked up anymore after this deadline.
|
|
|
|
*/
|
|
|
|
tipExpiration: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The exchange that will sign our coins, chosen by the merchant.
|
|
|
|
*/
|
|
|
|
exchangeBaseUrl: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base URL of the merchant that is giving us the tip.
|
|
|
|
*/
|
|
|
|
merchantBaseUrl: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Denomination selection made by the wallet for picking up
|
|
|
|
* this tip.
|
|
|
|
*/
|
|
|
|
denomsSel: DenomSelectionState;
|
|
|
|
|
2021-05-12 13:34:49 +02:00
|
|
|
denomSelUid: string;
|
|
|
|
|
2021-03-17 17:56:37 +01:00
|
|
|
/**
|
|
|
|
* Tip ID chosen by the wallet.
|
|
|
|
*/
|
|
|
|
walletTipId: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Secret seed used to derive planchets for this tip.
|
|
|
|
*/
|
|
|
|
secretSeed: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The merchant's identifier for this tip.
|
|
|
|
*/
|
|
|
|
merchantTipId: string;
|
|
|
|
|
|
|
|
createdTimestamp: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Timestamp for when the wallet finished picking up the tip
|
|
|
|
* from the merchant.
|
|
|
|
*/
|
|
|
|
pickedUpTimestamp: Timestamp | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retry info, even present when the operation isn't active to allow indexing
|
|
|
|
* on the next retry timestamp.
|
|
|
|
*/
|
|
|
|
retryInfo: RetryInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface RefreshGroupRecord {
|
|
|
|
/**
|
|
|
|
* Retry info, even present when the operation isn't active to allow indexing
|
|
|
|
* on the next retry timestamp.
|
|
|
|
*/
|
|
|
|
retryInfo: RetryInfo;
|
|
|
|
|
|
|
|
lastError: TalerErrorDetails | undefined;
|
|
|
|
|
|
|
|
lastErrorPerCoin: { [coinIndex: number]: TalerErrorDetails };
|
|
|
|
|
|
|
|
refreshGroupId: string;
|
|
|
|
|
|
|
|
reason: RefreshReason;
|
|
|
|
|
|
|
|
oldCoinPubs: string[];
|
|
|
|
|
|
|
|
refreshSessionPerCoin: (RefreshSessionRecord | undefined)[];
|
|
|
|
|
|
|
|
inputPerCoin: AmountJson[];
|
|
|
|
|
|
|
|
estimatedOutputPerCoin: AmountJson[];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Flag for each coin whether refreshing finished.
|
|
|
|
* If a coin can't be refreshed (remaining value too small),
|
|
|
|
* it will be marked as finished, but no refresh session will
|
|
|
|
* be created.
|
|
|
|
*/
|
|
|
|
finishedPerCoin: boolean[];
|
|
|
|
|
|
|
|
timestampCreated: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Timestamp when the refresh session finished.
|
|
|
|
*/
|
|
|
|
timestampFinished: Timestamp | undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ongoing refresh
|
|
|
|
*/
|
|
|
|
export interface RefreshSessionRecord {
|
|
|
|
/**
|
|
|
|
* 512-bit secret that can be used to derive
|
|
|
|
* the other cryptographic material for the refresh session.
|
|
|
|
*
|
|
|
|
* FIXME: We currently store the derived material, but
|
|
|
|
* should always derive it.
|
|
|
|
*/
|
|
|
|
sessionSecretSeed: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sum of the value of denominations we want
|
|
|
|
* to withdraw in this session, without fees.
|
|
|
|
*/
|
|
|
|
amountRefreshOutput: AmountJson;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Hashed denominations of the newly requested coins.
|
|
|
|
*/
|
|
|
|
newDenoms: {
|
|
|
|
denomPubHash: string;
|
|
|
|
count: number;
|
|
|
|
}[];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The no-reveal-index after we've done the melting.
|
|
|
|
*/
|
|
|
|
norevealIndex?: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wire fee for one wire method as stored in the
|
|
|
|
* wallet's database.
|
|
|
|
*/
|
|
|
|
export interface WireFee {
|
|
|
|
/**
|
|
|
|
* Fee for wire transfers.
|
|
|
|
*/
|
|
|
|
wireFee: AmountJson;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fees to close and refund a reserve.
|
|
|
|
*/
|
|
|
|
closingFee: AmountJson;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Start date of the fee.
|
|
|
|
*/
|
|
|
|
startStamp: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* End date of the fee.
|
|
|
|
*/
|
|
|
|
endStamp: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Signature made by the exchange master key.
|
|
|
|
*/
|
|
|
|
sig: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Record to store information about a refund event.
|
|
|
|
*
|
|
|
|
* All information about a refund is stored with the purchase,
|
|
|
|
* this event is just for the history.
|
|
|
|
*
|
|
|
|
* The event is only present for completed refunds.
|
|
|
|
*/
|
|
|
|
export interface RefundEventRecord {
|
|
|
|
timestamp: Timestamp;
|
|
|
|
merchantExecutionTimestamp: Timestamp;
|
|
|
|
refundGroupId: string;
|
|
|
|
proposalId: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
export enum RefundState {
|
|
|
|
Failed = "failed",
|
|
|
|
Applied = "applied",
|
|
|
|
Pending = "pending",
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* State of one refund from the merchant, maintained by the wallet.
|
|
|
|
*/
|
|
|
|
export type WalletRefundItem =
|
|
|
|
| WalletRefundFailedItem
|
|
|
|
| WalletRefundPendingItem
|
|
|
|
| WalletRefundAppliedItem;
|
|
|
|
|
|
|
|
export interface WalletRefundItemCommon {
|
|
|
|
// Execution time as claimed by the merchant
|
|
|
|
executionTime: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Time when the wallet became aware of the refund.
|
|
|
|
*/
|
|
|
|
obtainedTime: Timestamp;
|
|
|
|
|
|
|
|
refundAmount: AmountJson;
|
|
|
|
|
|
|
|
refundFee: AmountJson;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Upper bound on the refresh cost incurred by
|
|
|
|
* applying this refund.
|
|
|
|
*
|
|
|
|
* Might be lower in practice when two refunds on the same
|
|
|
|
* coin are refreshed in the same refresh operation.
|
|
|
|
*/
|
|
|
|
totalRefreshCostBound: AmountJson;
|
|
|
|
|
|
|
|
coinPub: string;
|
|
|
|
|
|
|
|
rtransactionId: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Failed refund, either because the merchant did
|
|
|
|
* something wrong or it expired.
|
|
|
|
*/
|
|
|
|
export interface WalletRefundFailedItem extends WalletRefundItemCommon {
|
|
|
|
type: RefundState.Failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface WalletRefundPendingItem extends WalletRefundItemCommon {
|
|
|
|
type: RefundState.Pending;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface WalletRefundAppliedItem extends WalletRefundItemCommon {
|
|
|
|
type: RefundState.Applied;
|
|
|
|
}
|
|
|
|
|
|
|
|
export enum RefundReason {
|
|
|
|
/**
|
|
|
|
* Normal refund given by the merchant.
|
|
|
|
*/
|
|
|
|
NormalRefund = "normal-refund",
|
|
|
|
/**
|
|
|
|
* Refund from an aborted payment.
|
|
|
|
*/
|
|
|
|
AbortRefund = "abort-pay-refund",
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface AllowedAuditorInfo {
|
|
|
|
auditorBaseUrl: string;
|
|
|
|
auditorPub: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface AllowedExchangeInfo {
|
|
|
|
exchangeBaseUrl: string;
|
|
|
|
exchangePub: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Data extracted from the contract terms that is relevant for payment
|
|
|
|
* processing in the wallet.
|
|
|
|
*/
|
|
|
|
export interface WalletContractData {
|
|
|
|
products?: Product[];
|
|
|
|
summaryI18n: { [lang_tag: string]: string } | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fulfillment URL, or the empty string if the order has no fulfillment URL.
|
|
|
|
*
|
|
|
|
* Stored as a non-nullable string as we use this field for IndexedDB indexing.
|
|
|
|
*/
|
|
|
|
fulfillmentUrl: string;
|
|
|
|
|
|
|
|
contractTermsHash: string;
|
|
|
|
fulfillmentMessage?: string;
|
|
|
|
fulfillmentMessageI18n?: InternationalizedString;
|
|
|
|
merchantSig: string;
|
|
|
|
merchantPub: string;
|
|
|
|
merchant: MerchantInfo;
|
|
|
|
amount: AmountJson;
|
|
|
|
orderId: string;
|
|
|
|
merchantBaseUrl: string;
|
|
|
|
summary: string;
|
|
|
|
autoRefund: Duration | undefined;
|
|
|
|
maxWireFee: AmountJson;
|
|
|
|
wireFeeAmortization: number;
|
|
|
|
payDeadline: Timestamp;
|
|
|
|
refundDeadline: Timestamp;
|
|
|
|
allowedAuditors: AllowedAuditorInfo[];
|
|
|
|
allowedExchanges: AllowedExchangeInfo[];
|
|
|
|
timestamp: Timestamp;
|
|
|
|
wireMethod: string;
|
|
|
|
wireInfoHash: string;
|
|
|
|
maxDepositFee: AmountJson;
|
|
|
|
}
|
|
|
|
|
|
|
|
export enum AbortStatus {
|
|
|
|
None = "none",
|
|
|
|
AbortRefund = "abort-refund",
|
|
|
|
AbortFinished = "abort-finished",
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Record that stores status information about one purchase, starting from when
|
|
|
|
* the customer accepts a proposal. Includes refund status if applicable.
|
|
|
|
*/
|
|
|
|
export interface PurchaseRecord {
|
|
|
|
/**
|
|
|
|
* Proposal ID for this purchase. Uniquely identifies the
|
|
|
|
* purchase and the proposal.
|
|
|
|
*/
|
|
|
|
proposalId: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Private key for the nonce.
|
|
|
|
*/
|
|
|
|
noncePriv: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Public key for the nonce.
|
|
|
|
*/
|
|
|
|
noncePub: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Downloaded and parsed proposal data.
|
|
|
|
*/
|
|
|
|
download: ProposalDownload;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Deposit permissions, available once the user has accepted the payment.
|
|
|
|
*
|
|
|
|
* This value is cached and derived from payCoinSelection.
|
|
|
|
*/
|
|
|
|
coinDepositPermissions: CoinDepositPermission[] | undefined;
|
|
|
|
|
|
|
|
payCoinSelection: PayCoinSelection;
|
|
|
|
|
2021-05-12 13:34:49 +02:00
|
|
|
payCoinSelectionUid: string;
|
|
|
|
|
2021-03-17 17:56:37 +01:00
|
|
|
/**
|
|
|
|
* Pending removals from pay coin selection.
|
2021-05-12 14:16:01 +02:00
|
|
|
*
|
2021-03-17 17:56:37 +01:00
|
|
|
* Used when a the pay coin selection needs to be changed
|
|
|
|
* because a coin became known as double-spent or invalid,
|
|
|
|
* but a new coin selection can't immediately be done, as
|
|
|
|
* there is not enough balance (e.g. when waiting for a refresh).
|
|
|
|
*/
|
|
|
|
pendingRemovedCoinPubs?: string[];
|
|
|
|
|
|
|
|
totalPayCost: AmountJson;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Timestamp of the first time that sending a payment to the merchant
|
|
|
|
* for this purchase was successful.
|
|
|
|
*/
|
|
|
|
timestampFirstSuccessfulPay: Timestamp | undefined;
|
|
|
|
|
|
|
|
merchantPaySig: string | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* When was the purchase made?
|
|
|
|
* Refers to the time that the user accepted.
|
|
|
|
*/
|
|
|
|
timestampAccept: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pending refunds for the purchase. A refund is pending
|
|
|
|
* when the merchant reports a transient error from the exchange.
|
|
|
|
*/
|
|
|
|
refunds: { [refundKey: string]: WalletRefundItem };
|
|
|
|
|
|
|
|
/**
|
|
|
|
* When was the last refund made?
|
|
|
|
* Set to 0 if no refund was made on the purchase.
|
|
|
|
*/
|
|
|
|
timestampLastRefundStatus: Timestamp | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Last session signature that we submitted to /pay (if any).
|
|
|
|
*/
|
|
|
|
lastSessionId: string | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set for the first payment, or on re-plays.
|
|
|
|
*/
|
|
|
|
paymentSubmitPending: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Do we need to query the merchant for the refund status
|
|
|
|
* of the payment?
|
|
|
|
*/
|
|
|
|
refundQueryRequested: boolean;
|
|
|
|
|
|
|
|
abortStatus: AbortStatus;
|
|
|
|
|
|
|
|
payRetryInfo: RetryInfo;
|
|
|
|
|
|
|
|
lastPayError: TalerErrorDetails | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retry information for querying the refund status with the merchant.
|
|
|
|
*/
|
|
|
|
refundStatusRetryInfo: RetryInfo;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Last error (or undefined) for querying the refund status with the merchant.
|
|
|
|
*/
|
|
|
|
lastRefundStatusError: TalerErrorDetails | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Continue querying the refund status until this deadline has expired.
|
|
|
|
*/
|
|
|
|
autoRefundDeadline: Timestamp | undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Configuration key/value entries to configure
|
|
|
|
* the wallet.
|
|
|
|
*/
|
|
|
|
export interface ConfigRecord<T> {
|
|
|
|
key: string;
|
|
|
|
value: T;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* FIXME: Eliminate this in favor of DenomSelectionState.
|
|
|
|
*/
|
|
|
|
export interface DenominationSelectionInfo {
|
|
|
|
totalCoinValue: AmountJson;
|
|
|
|
totalWithdrawCost: AmountJson;
|
|
|
|
selectedDenoms: {
|
|
|
|
/**
|
|
|
|
* How many times do we withdraw this denomination?
|
|
|
|
*/
|
|
|
|
count: number;
|
|
|
|
denom: DenominationRecord;
|
|
|
|
}[];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Selected denominations withn some extra info.
|
|
|
|
*/
|
|
|
|
export interface DenomSelectionState {
|
|
|
|
totalCoinValue: AmountJson;
|
|
|
|
totalWithdrawCost: AmountJson;
|
|
|
|
selectedDenoms: {
|
|
|
|
denomPubHash: string;
|
|
|
|
count: number;
|
|
|
|
}[];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Group of withdrawal operations that need to be executed.
|
|
|
|
* (Either for a normal withdrawal or from a tip.)
|
|
|
|
*
|
|
|
|
* The withdrawal group record is only created after we know
|
|
|
|
* the coin selection we want to withdraw.
|
|
|
|
*/
|
|
|
|
export interface WithdrawalGroupRecord {
|
|
|
|
withdrawalGroupId: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Secret seed used to derive planchets.
|
|
|
|
*/
|
|
|
|
secretSeed: string;
|
|
|
|
|
|
|
|
reservePub: string;
|
|
|
|
|
|
|
|
exchangeBaseUrl: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* When was the withdrawal operation started started?
|
|
|
|
* Timestamp in milliseconds.
|
|
|
|
*/
|
|
|
|
timestampStart: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* When was the withdrawal operation completed?
|
|
|
|
*/
|
|
|
|
timestampFinish?: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Amount including fees (i.e. the amount subtracted from the
|
|
|
|
* reserve to withdraw all coins in this withdrawal session).
|
|
|
|
*/
|
|
|
|
rawWithdrawalAmount: AmountJson;
|
|
|
|
|
|
|
|
denomsSel: DenomSelectionState;
|
|
|
|
|
2021-05-12 13:34:49 +02:00
|
|
|
denomSelUid: string;
|
|
|
|
|
2021-03-17 17:56:37 +01:00
|
|
|
/**
|
|
|
|
* Retry info, always present even on completed operations so that indexing works.
|
|
|
|
*/
|
|
|
|
retryInfo: RetryInfo;
|
|
|
|
|
|
|
|
lastError: TalerErrorDetails | undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface BankWithdrawUriRecord {
|
|
|
|
/**
|
|
|
|
* The withdraw URI we got from the bank.
|
|
|
|
*/
|
|
|
|
talerWithdrawUri: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reserve that was created for the withdraw URI.
|
|
|
|
*/
|
|
|
|
reservePub: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Status of recoup operations that were grouped together.
|
|
|
|
*
|
|
|
|
* The remaining amount of involved coins should be set to zero
|
|
|
|
* in the same transaction that inserts the RecoupGroupRecord.
|
|
|
|
*/
|
|
|
|
export interface RecoupGroupRecord {
|
|
|
|
/**
|
|
|
|
* Unique identifier for the recoup group record.
|
|
|
|
*/
|
|
|
|
recoupGroupId: string;
|
|
|
|
|
|
|
|
timestampStarted: Timestamp;
|
|
|
|
|
|
|
|
timestampFinished: Timestamp | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Public keys that identify the coins being recouped
|
|
|
|
* as part of this session.
|
|
|
|
*
|
|
|
|
* (Structured like this to enable multiEntry indexing in IndexedDB.)
|
|
|
|
*/
|
|
|
|
coinPubs: string[];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of flags to indicate whether the recoup finished on each individual coin.
|
|
|
|
*/
|
|
|
|
recoupFinishedPerCoin: boolean[];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* We store old amount (i.e. before recoup) of recouped coins here,
|
|
|
|
* as the balance of a recouped coin is set to zero when the
|
|
|
|
* recoup group is created.
|
|
|
|
*/
|
|
|
|
oldAmountPerCoin: AmountJson[];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Public keys of coins that should be scheduled for refreshing
|
|
|
|
* after all individual recoups are done.
|
|
|
|
*/
|
|
|
|
scheduleRefreshCoins: string[];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retry info.
|
|
|
|
*/
|
|
|
|
retryInfo: RetryInfo;
|
|
|
|
|
|
|
|
/**
|
2021-04-27 23:42:25 +02:00
|
|
|
* Last error that occurred, if any.
|
2021-03-17 17:56:37 +01:00
|
|
|
*/
|
|
|
|
lastError: TalerErrorDetails | undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
export enum ImportPayloadType {
|
|
|
|
CoreSchema = "core-schema",
|
|
|
|
}
|
|
|
|
|
|
|
|
export enum BackupProviderStatus {
|
|
|
|
PaymentRequired = "payment-required",
|
|
|
|
Ready = "ready",
|
|
|
|
}
|
|
|
|
|
2021-05-12 14:16:01 +02:00
|
|
|
export interface BackupProviderTerms {
|
|
|
|
supportedProtocolVersion: string;
|
|
|
|
annualFee: AmountString;
|
|
|
|
storageLimitInMegabytes: number;
|
|
|
|
}
|
|
|
|
|
2021-03-17 17:56:37 +01:00
|
|
|
export interface BackupProviderRecord {
|
|
|
|
baseUrl: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Terms of service of the provider.
|
|
|
|
* Might be unavailable in the DB in certain situations
|
|
|
|
* (such as loading a recovery document).
|
|
|
|
*/
|
2021-05-12 14:16:01 +02:00
|
|
|
terms?: BackupProviderTerms;
|
2021-03-17 17:56:37 +01:00
|
|
|
|
|
|
|
active: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Hash of the last encrypted backup that we already merged
|
|
|
|
* or successfully uploaded ourselves.
|
|
|
|
*/
|
|
|
|
lastBackupHash?: string;
|
|
|
|
|
|
|
|
lastBackupTimestamp?: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Proposal that we're currently trying to pay for.
|
|
|
|
*
|
|
|
|
* (Also included in paymentProposalIds.)
|
|
|
|
*/
|
|
|
|
currentPaymentProposalId?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Proposals that were used to pay (or attempt to pay) the provider.
|
|
|
|
*
|
|
|
|
* Stored to display a history of payments to the provider, and
|
|
|
|
* to make sure that the wallet isn't overpaying.
|
|
|
|
*/
|
|
|
|
paymentProposalIds: string[];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Next scheduled backup.
|
|
|
|
*/
|
|
|
|
nextBackupTimestamp?: Timestamp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retry info.
|
|
|
|
*/
|
|
|
|
retryInfo: RetryInfo;
|
|
|
|
|
|
|
|
/**
|
2021-04-27 23:42:25 +02:00
|
|
|
* Last error that occurred, if any.
|
2021-03-17 17:56:37 +01:00
|
|
|
*/
|
|
|
|
lastError: TalerErrorDetails | undefined;
|
2021-05-12 14:16:01 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* UIDs for the operation that added the backup provider.
|
|
|
|
*/
|
|
|
|
uids: string[];
|
2021-03-17 17:56:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Group of deposits made by the wallet.
|
|
|
|
*/
|
|
|
|
export interface DepositGroupRecord {
|
|
|
|
depositGroupId: string;
|
|
|
|
|
|
|
|
merchantPub: string;
|
|
|
|
merchantPriv: string;
|
|
|
|
|
|
|
|
noncePriv: string;
|
|
|
|
noncePub: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wire information used by all deposits in this
|
|
|
|
* deposit group.
|
|
|
|
*/
|
|
|
|
wire: {
|
|
|
|
payto_uri: string;
|
|
|
|
salt: string;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Verbatim contract terms.
|
|
|
|
*/
|
|
|
|
contractTermsRaw: ContractTerms;
|
|
|
|
|
|
|
|
contractTermsHash: string;
|
|
|
|
|
|
|
|
payCoinSelection: PayCoinSelection;
|
|
|
|
|
|
|
|
totalPayCost: AmountJson;
|
|
|
|
|
|
|
|
effectiveDepositAmount: AmountJson;
|
|
|
|
|
|
|
|
depositedPerCoin: boolean[];
|
|
|
|
|
|
|
|
timestampCreated: Timestamp;
|
|
|
|
|
|
|
|
timestampFinished: Timestamp | undefined;
|
|
|
|
|
|
|
|
lastError: TalerErrorDetails | undefined;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retry info.
|
|
|
|
*/
|
|
|
|
retryInfo: RetryInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Record for a deposits that the wallet observed
|
|
|
|
* as a result of double spending, but which is not
|
|
|
|
* present in the wallet's own database otherwise.
|
|
|
|
*/
|
|
|
|
export interface GhostDepositGroupRecord {
|
|
|
|
/**
|
|
|
|
* When multiple deposits for the same contract terms hash
|
|
|
|
* have a different timestamp, we choose the earliest one.
|
|
|
|
*/
|
|
|
|
timestamp: Timestamp;
|
|
|
|
|
|
|
|
contractTermsHash: string;
|
|
|
|
|
|
|
|
deposits: {
|
|
|
|
coinPub: string;
|
|
|
|
amount: AmountString;
|
|
|
|
timestamp: Timestamp;
|
|
|
|
depositFee: AmountString;
|
|
|
|
merchantPub: string;
|
|
|
|
coinSig: string;
|
|
|
|
wireHash: string;
|
|
|
|
}[];
|
|
|
|
}
|
|
|
|
|
2021-05-12 15:26:15 +02:00
|
|
|
export interface TombstoneRecord {
|
|
|
|
/**
|
|
|
|
* Tombstone ID, with the syntax "<type>:<key>".
|
|
|
|
*/
|
|
|
|
id: string;
|
|
|
|
}
|
|
|
|
|
2021-03-17 17:56:37 +01:00
|
|
|
class ExchangesStore extends Store<"exchanges", ExchangeRecord> {
|
|
|
|
constructor() {
|
|
|
|
super("exchanges", { keyPath: "baseUrl" });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class CoinsStore extends Store<"coins", CoinRecord> {
|
|
|
|
constructor() {
|
|
|
|
super("coins", { keyPath: "coinPub" });
|
|
|
|
}
|
|
|
|
|
|
|
|
exchangeBaseUrlIndex = new Index<
|
|
|
|
"coins",
|
|
|
|
"exchangeBaseUrl",
|
|
|
|
string,
|
|
|
|
CoinRecord
|
|
|
|
>(this, "exchangeBaseUrl", "exchangeBaseUrl");
|
|
|
|
|
|
|
|
denomPubHashIndex = new Index<
|
|
|
|
"coins",
|
|
|
|
"denomPubHashIndex",
|
|
|
|
string,
|
|
|
|
CoinRecord
|
|
|
|
>(this, "denomPubHashIndex", "denomPubHash");
|
|
|
|
|
|
|
|
coinEvHashIndex = new Index<"coins", "coinEvHashIndex", string, CoinRecord>(
|
|
|
|
this,
|
|
|
|
"coinEvHashIndex",
|
|
|
|
"coinEvHash",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
class ProposalsStore extends Store<"proposals", ProposalRecord> {
|
|
|
|
constructor() {
|
|
|
|
super("proposals", { keyPath: "proposalId" });
|
|
|
|
}
|
|
|
|
urlAndOrderIdIndex = new Index<
|
|
|
|
"proposals",
|
|
|
|
"urlIndex",
|
|
|
|
string,
|
|
|
|
ProposalRecord
|
|
|
|
>(this, "urlIndex", ["merchantBaseUrl", "orderId"]);
|
|
|
|
}
|
|
|
|
|
|
|
|
class PurchasesStore extends Store<"purchases", PurchaseRecord> {
|
|
|
|
constructor() {
|
|
|
|
super("purchases", { keyPath: "proposalId" });
|
|
|
|
}
|
|
|
|
|
|
|
|
fulfillmentUrlIndex = new Index<
|
|
|
|
"purchases",
|
|
|
|
"fulfillmentUrlIndex",
|
|
|
|
string,
|
|
|
|
PurchaseRecord
|
|
|
|
>(this, "fulfillmentUrlIndex", "download.contractData.fulfillmentUrl");
|
|
|
|
|
|
|
|
orderIdIndex = new Index<"purchases", "orderIdIndex", string, PurchaseRecord>(
|
|
|
|
this,
|
|
|
|
"orderIdIndex",
|
|
|
|
["download.contractData.merchantBaseUrl", "download.contractData.orderId"],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
class DenominationsStore extends Store<"denominations", DenominationRecord> {
|
|
|
|
constructor() {
|
|
|
|
// cast needed because of bug in type annotations
|
|
|
|
super("denominations", {
|
|
|
|
keyPath: (["exchangeBaseUrl", "denomPubHash"] as any) as IDBKeyPath,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
exchangeBaseUrlIndex = new Index<
|
|
|
|
"denominations",
|
|
|
|
"exchangeBaseUrlIndex",
|
|
|
|
string,
|
|
|
|
DenominationRecord
|
|
|
|
>(this, "exchangeBaseUrlIndex", "exchangeBaseUrl");
|
|
|
|
}
|
|
|
|
|
2021-05-20 13:14:47 +02:00
|
|
|
class AuditorTrustStore extends Store<"auditorTrust", AuditorTrustRecord> {
|
2021-03-17 17:56:37 +01:00
|
|
|
constructor() {
|
2021-05-20 13:14:47 +02:00
|
|
|
super("auditorTrust", {
|
|
|
|
keyPath: ["currency", "auditorBaseUrl", "auditorPub"],
|
|
|
|
});
|
2021-03-17 17:56:37 +01:00
|
|
|
}
|
2021-05-20 13:14:47 +02:00
|
|
|
auditorPubIndex = new Index<
|
|
|
|
"auditorTrust",
|
|
|
|
"auditorPubIndex",
|
|
|
|
string,
|
|
|
|
AuditorTrustRecord
|
|
|
|
>(this, "auditorPubIndex", "auditorPub");
|
|
|
|
uidIndex = new Index<"auditorTrust", "uidIndex", string, AuditorTrustRecord>(
|
|
|
|
this,
|
|
|
|
"uidIndex",
|
|
|
|
"uids",
|
|
|
|
{ multiEntry: true },
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
class ExchangeTrustStore extends Store<"exchangeTrust", ExchangeTrustRecord> {
|
|
|
|
constructor() {
|
|
|
|
super("exchangeTrust", {
|
|
|
|
keyPath: ["currency", "exchangeBaseUrl", "exchangePub"],
|
|
|
|
});
|
|
|
|
}
|
|
|
|
exchangeMasterPubIndex = new Index<
|
|
|
|
"exchangeTrust",
|
|
|
|
"exchangeMasterPubIndex",
|
|
|
|
string,
|
|
|
|
ExchangeTrustRecord
|
|
|
|
>(this, "exchangeMasterPubIndex", "exchangePub");
|
|
|
|
uidIndex = new Index<
|
|
|
|
"exchangeTrust",
|
|
|
|
"uidIndex",
|
|
|
|
string,
|
|
|
|
ExchangeTrustRecord
|
|
|
|
>(this, "uidIndex", "uids", { multiEntry: true });
|
2021-03-17 17:56:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
class ConfigStore extends Store<"config", ConfigRecord<any>> {
|
|
|
|
constructor() {
|
|
|
|
super("config", { keyPath: "key" });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class ReservesStore extends Store<"reserves", ReserveRecord> {
|
|
|
|
constructor() {
|
|
|
|
super("reserves", { keyPath: "reservePub" });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class TipsStore extends Store<"tips", TipRecord> {
|
|
|
|
constructor() {
|
|
|
|
super("tips", { keyPath: "walletTipId" });
|
|
|
|
}
|
|
|
|
// Added in version 2
|
|
|
|
byMerchantTipIdAndBaseUrl = new Index<
|
|
|
|
"tips",
|
|
|
|
"tipsByMerchantTipIdAndOriginIndex",
|
|
|
|
[string, string],
|
|
|
|
TipRecord
|
|
|
|
>(this, "tipsByMerchantTipIdAndOriginIndex", [
|
|
|
|
"merchantTipId",
|
|
|
|
"merchantBaseUrl",
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
class WithdrawalGroupsStore extends Store<
|
|
|
|
"withdrawals",
|
|
|
|
WithdrawalGroupRecord
|
|
|
|
> {
|
|
|
|
constructor() {
|
|
|
|
super("withdrawals", { keyPath: "withdrawalGroupId" });
|
|
|
|
}
|
|
|
|
byReservePub = new Index<
|
|
|
|
"withdrawals",
|
|
|
|
"withdrawalsByReserveIndex",
|
|
|
|
string,
|
|
|
|
WithdrawalGroupRecord
|
|
|
|
>(this, "withdrawalsByReserveIndex", "reservePub");
|
|
|
|
}
|
|
|
|
|
|
|
|
class PlanchetsStore extends Store<"planchets", PlanchetRecord> {
|
|
|
|
constructor() {
|
|
|
|
super("planchets", { keyPath: "coinPub" });
|
|
|
|
}
|
|
|
|
byGroupAndIndex = new Index<
|
|
|
|
"planchets",
|
|
|
|
"withdrawalGroupAndCoinIdxIndex",
|
|
|
|
string,
|
|
|
|
PlanchetRecord
|
|
|
|
>(this, "withdrawalGroupAndCoinIdxIndex", ["withdrawalGroupId", "coinIdx"]);
|
|
|
|
byGroup = new Index<
|
|
|
|
"planchets",
|
|
|
|
"withdrawalGroupIndex",
|
|
|
|
string,
|
|
|
|
PlanchetRecord
|
|
|
|
>(this, "withdrawalGroupIndex", "withdrawalGroupId");
|
|
|
|
|
|
|
|
coinEvHashIndex = new Index<
|
|
|
|
"planchets",
|
|
|
|
"coinEvHashIndex",
|
|
|
|
string,
|
|
|
|
PlanchetRecord
|
|
|
|
>(this, "coinEvHashIndex", "coinEvHash");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This store is effectively a materialized index for
|
|
|
|
* reserve records that are for a bank-integrated withdrawal.
|
|
|
|
*/
|
|
|
|
class BankWithdrawUrisStore extends Store<
|
|
|
|
"bankWithdrawUris",
|
|
|
|
BankWithdrawUriRecord
|
|
|
|
> {
|
|
|
|
constructor() {
|
|
|
|
super("bankWithdrawUris", { keyPath: "talerWithdrawUri" });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*/
|
|
|
|
class BackupProvidersStore extends Store<
|
|
|
|
"backupProviders",
|
|
|
|
BackupProviderRecord
|
|
|
|
> {
|
|
|
|
constructor() {
|
|
|
|
super("backupProviders", { keyPath: "baseUrl" });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class DepositGroupsStore extends Store<"depositGroups", DepositGroupRecord> {
|
|
|
|
constructor() {
|
|
|
|
super("depositGroups", { keyPath: "depositGroupId" });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-12 15:26:15 +02:00
|
|
|
class TombstonesStore extends Store<"tombstones", TombstoneRecord> {
|
|
|
|
constructor() {
|
|
|
|
super("tombstones", { keyPath: "id" });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-17 17:56:37 +01:00
|
|
|
/**
|
|
|
|
* The stores and indices for the wallet database.
|
|
|
|
*/
|
|
|
|
export const Stores = {
|
|
|
|
coins: new CoinsStore(),
|
|
|
|
config: new ConfigStore(),
|
2021-05-20 13:14:47 +02:00
|
|
|
auditorTrustStore: new AuditorTrustStore(),
|
|
|
|
exchangeTrustStore: new ExchangeTrustStore(),
|
2021-03-17 17:56:37 +01:00
|
|
|
denominations: new DenominationsStore(),
|
|
|
|
exchanges: new ExchangesStore(),
|
|
|
|
proposals: new ProposalsStore(),
|
|
|
|
refreshGroups: new Store<"refreshGroups", RefreshGroupRecord>(
|
|
|
|
"refreshGroups",
|
|
|
|
{
|
|
|
|
keyPath: "refreshGroupId",
|
|
|
|
},
|
|
|
|
),
|
|
|
|
recoupGroups: new Store<"recoupGroups", RecoupGroupRecord>("recoupGroups", {
|
|
|
|
keyPath: "recoupGroupId",
|
|
|
|
}),
|
|
|
|
reserves: new ReservesStore(),
|
|
|
|
purchases: new PurchasesStore(),
|
|
|
|
tips: new TipsStore(),
|
|
|
|
withdrawalGroups: new WithdrawalGroupsStore(),
|
|
|
|
planchets: new PlanchetsStore(),
|
|
|
|
bankWithdrawUris: new BankWithdrawUrisStore(),
|
|
|
|
backupProviders: new BackupProvidersStore(),
|
|
|
|
depositGroups: new DepositGroupsStore(),
|
2021-05-12 15:26:15 +02:00
|
|
|
tombstones: new TombstonesStore(),
|
2021-03-17 17:56:37 +01:00
|
|
|
ghostDepositGroups: new Store<"ghostDepositGroups", GhostDepositGroupRecord>(
|
|
|
|
"ghostDepositGroups",
|
|
|
|
{
|
|
|
|
keyPath: "contractTermsHash",
|
|
|
|
},
|
|
|
|
),
|
|
|
|
};
|
|
|
|
|
|
|
|
export class MetaConfigStore extends Store<"metaConfig", ConfigRecord<any>> {
|
|
|
|
constructor() {
|
|
|
|
super("metaConfig", { keyPath: "key" });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const MetaStores = {
|
|
|
|
metaConfig: new MetaConfigStore(),
|
|
|
|
};
|