diff options
Diffstat (limited to 'packages/taler-wallet-core/src/db.ts')
-rw-r--r-- | packages/taler-wallet-core/src/db.ts | 817 |
1 files changed, 294 insertions, 523 deletions
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index b52a503bc..25757ef25 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -22,59 +22,59 @@ import { IDBDatabase, IDBFactory, IDBObjectStore, + IDBRequest, IDBTransaction, structuredEncapsulate, } from "@gnu-taler/idb-bridge"; import { AgeCommitmentProof, - AmountJson, AmountString, + Amounts, + AttentionInfo, + Codec, CoinEnvelope, + CoinPublicKeyString, CoinRefreshRequest, CoinStatus, - MerchantContractTerms, + DenomSelectionState, DenominationInfo, DenominationPubKey, - DenomSelectionState, EddsaPublicKeyString, EddsaSignatureString, ExchangeAuditor, ExchangeGlobalFees, + HashCodeString, InternationalizedString, - Location, + Logger, + MerchantContractTerms, MerchantInfo, PayCoinSelection, PeerContractTerms, - Product, RefreshReason, TalerErrorDetail, + TalerPreciseTimestamp, TalerProtocolDuration, TalerProtocolTimestamp, TransactionIdStr, UnblindedSignature, WireInfo, - HashCodeString, - Amounts, - AttentionInfo, - Logger, - CoinPublicKeyString, - TalerPreciseTimestamp, + codecForAny, } from "@gnu-taler/taler-util"; +import { RetryInfo, TaskIdentifiers } from "./operations/common.js"; import { DbAccess, DbReadOnlyTransaction, DbReadWriteTransaction, - describeContents, - describeIndex, - describeStore, GetReadWriteAccess, IndexDescriptor, - openDatabase, StoreDescriptor, StoreNames, StoreWithIndexes, + describeContents, + describeIndex, + describeStore, + openDatabase, } from "./util/query.js"; -import { RetryInfo, TaskIdentifiers } from "./operations/common.js"; /** * This file contains the database schema of the Taler wallet together @@ -99,12 +99,25 @@ import { RetryInfo, TaskIdentifiers } from "./operations/common.js"; */ /** + FIXMEs: + - Contract terms can be quite large. We currently tend to read the + full contract terms from the DB quite often. + Instead, we should probably extract what we need into a separate object + store. + - More object stores should have an "id" primary key, + as this makes referencing less expensive. + - Coin selections should probably go into a separate object store. + - Some records should be split up into an extra "details" record + that we don't always need to iterate over. + */ + +/** * 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. */ -export const TALER_WALLET_MAIN_DB_NAME = "taler-wallet-main-v9"; +export const TALER_WALLET_MAIN_DB_NAME = "taler-wallet-main-v10"; /** * Name of the metadata database. This database is used @@ -115,10 +128,17 @@ export const TALER_WALLET_MAIN_DB_NAME = "taler-wallet-main-v9"; export const TALER_WALLET_META_DB_NAME = "taler-wallet-meta"; /** - * Stored backups, mainly created when manually importing a backup. + * Name of the "stored backups" database. + * Stored backups are created before manually importing a backup. + * We use IndexedDB for this purpose, since we don't have file system + * access on some platforms. */ -export const TALER_WALLET_STORED_BACKUPS_DB_NAME = "taler-wallet-stored-backups"; +export const TALER_WALLET_STORED_BACKUPS_DB_NAME = + "taler-wallet-stored-backups"; +/** + * Name of the "meta config" database. + */ export const CURRENT_DB_CONFIG_KEY = "currentMainDbName"; /** @@ -128,26 +148,26 @@ export const CURRENT_DB_CONFIG_KEY = "currentMainDbName"; * backwards-compatible way or object stores and indices * are added. */ -export const WALLET_DB_MINOR_VERSION = 10; +export const WALLET_DB_MINOR_VERSION = 1; /** - * Ranges for operation status fields. - * - * All individual enums should make sure that the values they - * defined are in the right range. + * Format of the operation status code: 0x0abc_nnnn + + * a=1: active + * 0x0100_nnnn: pending + * 0x0101_nnnn: dialog + * 0x0102_nnnn: (reserved) + * 0x0103_nnnn: aborting + * 0x0110_nnnn: suspended + * 0x0113_nnnn: suspended-aborting + * a=5: final + * 0x0500_nnnn: done + * 0x0501_nnnn: failed + * 0x0502_nnnn: expired + * 0x0503_nnnn: aborted + * + * nnnn=0000 should always be the most generic minor state for the major state */ -export enum OperationStatusRange { - // Operations that need to be actively processed. - ACTIVE_START = 10, - ACTIVE_END = 29, - // Operations that are suspended and might - // expire, but nothing else can be done. - SUSPENDED_START = 30, - SUSPENDED_END = 49, - // Operations that don't need any attention or processing. - DORMANT_START = 50, - DORMANT_END = 69, -} /** * Status of a withdrawal. @@ -156,71 +176,70 @@ export enum WithdrawalGroupStatus { /** * Reserve must be registered with the bank. */ - PendingRegisteringBank = 10, + PendingRegisteringBank = 0x0100_0001, + SuspendedRegisteringBank = 0x0110_0001, /** * 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). */ - PendingWaitConfirmBank = 11, + PendingWaitConfirmBank = 0x0100_0002, + SuspendedWaitConfirmBank = 0x0110_0002, /** * Querying reserve status with the exchange. */ - PendingQueryingStatus = 12, + PendingQueryingStatus = 0x0100_0003, + SuspendedQueryingStatus = 0x0110_0003, /** * Ready for withdrawal. */ - PendingReady = 13, + PendingReady = 0x0100_0004, + SuspendedReady = 0x0110_0004, /** * We are telling the bank that we don't want to complete * the withdrawal! */ - AbortingBank = 14, + AbortingBank = 0x0103_0001, + SuspendedAbortingBank = 0x0113_0001, /** * Exchange wants KYC info from the user. */ - PendingKyc = 16, + PendingKyc = 0x0100_0005, + SuspendedKyc = 0x0110_005, /** * Exchange is doing AML checks. */ - PendingAml = 17, - - SuspendedRegisteringBank = 30, - SuspendedWaitConfirmBank = 31, - SuspendedQueryingStatus = 32, - SuspendedReady = 33, - SuspendedAbortingBank = 34, - SuspendedKyc = 35, - SuspendedAml = 36, + PendingAml = 0x0100_0006, + SuspendedAml = 0x0100_0006, /** * The corresponding withdraw record has been created. * No further processing is done, unless explicitly requested * by the user. */ - Finished = 50, + Done = 0x0500_0000, /** * The bank aborted the withdrawal. */ - FailedBankAborted = 51, + FailedBankAborted = 0x0501_0001, - FailedAbortingBank = 59, + FailedAbortingBank = 0x0501_0002, /** * Aborted in a state where we were supposed to * talk to the exchange. Money might have been * wired or not. */ - AbortedExchange = 60, + AbortedExchange = 0x0503_0001, - AbortedBank = 61, + AbortedBank = 0x0503_0002, } /** @@ -259,78 +278,23 @@ export interface ReserveBankInfo { } /** - * Record that indicates the wallet trusts - * a particular auditor. - */ -export interface AuditorTrustRecord { - /** - * Currency that we trust this auditor for. - */ - currency: string; - - /** - * Base URL of the auditor. - */ - auditorBaseUrl: string; - - /** - * Public key of the auditor. - */ - auditorPub: string; - - /** - * UIDs for the operation of adding this auditor - * as a trusted auditor. - * - * (Used for backup/sync merging and tombstones.) - */ - uids: string[]; -} - -/** - * Record to indicate trust for a particular exchange. - */ -export interface ExchangeTrustRecord { - /** - * Currency that we trust this exchange for. - */ - currency: string; - - /** - * Canonicalized exchange base URL. - */ - exchangeBaseUrl: string; - - /** - * Master public key of the exchange. - */ - exchangeMasterPub: string; - - /** - * UIDs for the operation of adding this exchange - * as trusted. - */ - uids: string[]; -} - -/** * Status of a denomination. */ export enum DenominationVerificationStatus { /** - * Verification was delayed. + * Verification was delayed (pending). */ - Unverified = OperationStatusRange.ACTIVE_START, + Unverified = 0x0100_0000, /** * Verified as valid. */ - VerifiedGood = OperationStatusRange.DORMANT_START, + VerifiedGood = 0x0500_0000, /** * Verified as invalid. */ - VerifiedBad = OperationStatusRange.DORMANT_START + 1, + VerifiedBad = 0x0501_0000, } export interface DenomFees { @@ -359,12 +323,14 @@ export interface DenomFees { * Denomination record as stored in the wallet's database. */ export interface DenominationRecord { + /** + * Currency of the denomination. + * + * Stored separately as we have an index on it. + */ currency: string; - // FIXME: Use binary encoding of amount instead? - amountVal: number; - - amountFrac: number; + value: AmountString; /** * The denomination public key. @@ -443,14 +409,6 @@ export interface DenominationRecord { } export namespace DenominationRecord { - export function getValue(d: DenominationRecord): AmountJson { - return { - currency: d.currency, - fraction: d.amountFrac, - value: d.amountVal, - }; - } - export function toDenomInfo(d: DenominationRecord): DenominationInfo { return { denomPub: d.denomPub, @@ -463,7 +421,7 @@ export namespace DenominationRecord { stampExpireLegal: d.stampExpireLegal, stampExpireWithdraw: d.stampExpireWithdraw, stampStart: d.stampStart, - value: Amounts.stringify(DenominationRecord.getValue(d)), + value: Amounts.stringify(d.value), exchangeBaseUrl: d.exchangeBaseUrl, }; } @@ -578,6 +536,7 @@ export enum ExchangeEntryDbRecordStatus { Used = 3, } +// FIXME: Use status ranges for this as well? export enum ExchangeEntryDbUpdateStatus { Initial = 1, InitialUpdate = 2, @@ -658,9 +617,9 @@ export interface ExchangeEntryRecord { } export enum PlanchetStatus { - Pending = 10 /* ACTIVE_START */, - KycRequired = 11 /* ACTIVE_START + 1 */, - WithdrawalDone = 50 /* DORMANT_START */, + Pending = 0x0100_0000, + KycRequired = 0x0100_0001, + WithdrawalDone = 0x0500_000, } /** @@ -933,50 +892,45 @@ export interface RewardRecord { } export enum RewardRecordStatus { - PendingPickup = 10, - - SuspendidPickup = 20, - - DialogAccept = 30, - - Done = 50, - Aborted = 51, + PendingPickup = 0x0100_0000, + SuspendedPickup = 0x0110_0000, + DialogAccept = 0x0101_0000, + Done = 0x0500_0000, + Aborted = 0x0500_0000, } export enum RefreshCoinStatus { - Pending = OperationStatusRange.ACTIVE_START, - Finished = OperationStatusRange.DORMANT_START, + Pending = 0x0100_0000, + Finished = 0x0500_0000, /** * The refresh for this coin has been frozen, because of a permanent error. * More info in lastErrorPerCoin. */ - Failed = OperationStatusRange.DORMANT_START + 1, -} - -export enum OperationStatus { - Finished = OperationStatusRange.DORMANT_START, - Pending = OperationStatusRange.ACTIVE_START, + Failed = 0x0501_000, } export enum RefreshOperationStatus { - Pending = 10 /* ACTIVE_START */, - Suspended = 20 /* DORMANT_START + 2 */, + Pending = 0x0100_0000, + Suspended = 0x0110_0000, - Finished = 50 /* DORMANT_START */, - Failed = 51 /* DORMANT_START + 1 */, + Finished = 0x0500_000, + Failed = 0x0501_000, } /** * Status of a single element of a deposit group. */ export enum DepositElementStatus { - Unknown = 10, - Accepted = 20, - KycRequired = 30, - Wired = 40, - RefundSuccess = 50, - RefundFailed = 51, + DepositPending = 0x0100_0000, + /** + * Accepted, but tracking. + */ + Tracking = 0x0100_0001, + KycRequired = 0x0100_0002, + Wired = 0x0500_0000, + RefundSuccess = 0x0503_0000, + RefundFailed = 0x0501_0000, } /** @@ -991,16 +945,10 @@ export interface RefreshReasonDetails { * Group of refresh operations. The refreshed coins do not * have to belong to the same exchange, but must have the same * currency. - * - * FIXME: Should include the currency as a top-level field, - * but we need to write a migration for that. */ export interface RefreshGroupRecord { operationStatus: RefreshOperationStatus; - // FIXME: Put this into a different object store? - lastErrorPerCoin: { [coinIndex: number]: TalerErrorDetail }; - /** * Unique, randomly generated identifier for this group of * refresh operations. @@ -1009,8 +957,6 @@ export interface RefreshGroupRecord { /** * Currency of this refresh group. - * - * FIXME: Write a migration to add this to earlier DB versions. */ currency: string; @@ -1026,13 +972,9 @@ export interface RefreshGroupRecord { oldCoinPubs: string[]; - // FIXME: Should this go into a separate - // object store for faster updates? - refreshSessionPerCoin: (RefreshSessionRecord | undefined)[]; - inputPerCoin: AmountString[]; - estimatedOutputPerCoin: AmountString[]; + expectedOutputPerCoin: AmountString[]; /** * Flag for each coin whether refreshing finished. @@ -1054,6 +996,13 @@ export interface RefreshGroupRecord { * Ongoing refresh */ export interface RefreshSessionRecord { + refreshGroupId: string; + + /** + * Index of the coin in the refresh group. + */ + coinIndex: number; + /** * 512-bit secret that can be used to derive * the other cryptographic material for the refresh session. @@ -1078,6 +1027,8 @@ export interface RefreshSessionRecord { * The no-reveal-index after we've done the melting. */ norevealIndex?: number; + + lastError?: TalerErrorDetail; } export enum RefundReason { @@ -1106,9 +1057,6 @@ export interface AllowedExchangeInfo { * 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. * @@ -1126,6 +1074,7 @@ export interface WalletContractData { orderId: string; merchantBaseUrl: string; summary: string; + summaryI18n: { [lang_tag: string]: string } | undefined; autoRefund: TalerProtocolDuration | undefined; maxWireFee: AmountString; wireFeeAmortization: number; @@ -1137,89 +1086,90 @@ export interface WalletContractData { wireInfoHash: string; maxDepositFee: AmountString; minimumAge?: number; - deliveryDate: TalerProtocolTimestamp | undefined; - deliveryLocation: Location | undefined; } export enum PurchaseStatus { /** * Not downloaded yet. */ - PendingDownloadingProposal = 10, + PendingDownloadingProposal = 0x0100_0000, + SuspendedDownloadingProposal = 0x0110_0000, /** * The user has accepted the proposal. */ - PendingPaying = 11, + PendingPaying = 0x0100_0001, + SuspendedPaying = 0x0110_0001, /** * Currently in the process of aborting with a refund. */ - AbortingWithRefund = 12, + AbortingWithRefund = 0x0103_0000, + SuspendedAbortingWithRefund = 0x0113_0000, /** * Paying a second time, likely with different session ID */ - PendingPayingReplay = 13, + PendingPayingReplay = 0x0100_0002, + SuspendedPayingReplay = 0x0110_0002, /** * Query for refunds (until query succeeds). */ - PendingQueryingRefund = 14, + PendingQueryingRefund = 0x0100_0003, + SuspendedQueryingRefund = 0x0110_0003, /** * Query for refund (until auto-refund deadline is reached). */ - PendingQueryingAutoRefund = 15, + PendingQueryingAutoRefund = 0x0100_0004, + SuspendedQueryingAutoRefund = 0x0110_0004, - PendingAcceptRefund = 16, - - SuspendedDownloadingProposal = 20, - SuspendedPaying = 21, - SuspendedAbortingWithRefund = 22, - SuspendedPayingReplay = 23, - SuspendedQueryingRefund = 24, - SuspendedQueryingAutoRefund = 25, - SuspendedPendingAcceptRefund = 26, + PendingAcceptRefund = 0x0100_0005, + SuspendedPendingAcceptRefund = 0x0100_0005, /** * Proposal downloaded, but the user needs to accept/reject it. */ - DialogProposed = 30, + DialogProposed = 0x0101_0000, + /** * Proposal shared to other wallet or read from other wallet * the user needs to accept/reject it. */ - DialogShared = 31, + DialogShared = 0x0101_0001, /** * The user has rejected the proposal. */ - AbortedProposalRefused = 50, + AbortedProposalRefused = 0x0503_0000, /** * Downloading or processing the proposal has failed permanently. */ - FailedClaim = 51, + FailedClaim = 0x0501_0000, + + /** + * Payment was successful. + */ + Done = 0x0500_0000, /** * Downloaded proposal was detected as a re-purchase. */ - RepurchaseDetected = 52, + DoneRepurchaseDetected = 0x0500_0001, /** * The payment has been aborted. */ - AbortedIncompletePayment = 53, + AbortedIncompletePayment = 0x0503_0000, /** - * Payment was successful. + * Tried to abort, but aborting failed or was cancelled. */ - Done = 54, + FailedAbort = 0x0501_0001, - FailedAbort = 55, - - AbortedRefunded = 56, + AbortedRefunded = 0x0503_0000, } /** @@ -1334,14 +1284,6 @@ export interface PurchaseRecord { timestampAccept: TalerPreciseTimestamp | undefined; /** - * Pending refunds for the purchase. A refund is pending - * when the merchant reports a transient error from the exchange. - * - * FIXME: Put this into a separate object store? - */ - // refunds: { [refundKey: string]: WalletRefundItem }; - - /** * When was the last refund made? * Set to 0 if no refund was made on the purchase. */ @@ -1404,6 +1346,7 @@ export interface WalletBackupConfState { lastBackupNonce?: string; } +// FIXME: Should these be numeric codes? export const enum WithdrawalRecordType { BankManual = "bank-manual", BankIntegrated = "bank-integrated", @@ -1428,7 +1371,8 @@ export interface WgInfoBankManual { export interface WgInfoBankPeerPull { withdrawalType: WithdrawalRecordType.PeerPullCredit; - contractTerms: any; + // FIXME: include a transaction ID here? + /** * Needed to quickly construct the taler:// URI for the counterparty * without a join. @@ -1439,7 +1383,7 @@ export interface WgInfoBankPeerPull { export interface WgInfoBankPeerPush { withdrawalType: WithdrawalRecordType.PeerPushCredit; - contractTerms: any; + // FIXME: include a transaction ID here? } export interface WgInfoBankRecoup { @@ -1713,19 +1657,21 @@ export interface BackupProviderRecord { } export enum DepositOperationStatus { - PendingDeposit = 10, - Aborting = 11, - PendingTrack = 12, - PendingKyc = 13, + PendingDeposit = 0x0100_0000, + PendingTrack = 0x0100_0001, + PendingKyc = 0x0100_0002, + + Aborting = 0x0103_0000, - SuspendedDeposit = 20, - SuspendedAborting = 21, - SuspendedTrack = 22, - SuspendedKyc = 23, + SuspendedDeposit = 0x0110_0000, + SuspendedTrack = 0x0110_0001, + SuspendedKyc = 0x0110_0002, - Finished = 50, - Failed = 51, - Aborted = 52, + SuspendedAborting = 0x0113_0000, + + Finished = 0x0500_0000, + Failed = 0x0501_0000, + Aborted = 0x0503_0000, } export interface DepositTrackingInfo { @@ -1777,10 +1723,8 @@ export interface DepositGroupRecord { /** * The counterparty effective deposit amount. - * - * FIXME: If possible, rename to counterpartyEffectiveDepositAmount. */ - effectiveDepositAmount: AmountString; + counterpartyEffectiveDepositAmount: AmountString; timestampCreated: TalerPreciseTimestamp; @@ -1788,11 +1732,7 @@ export interface DepositGroupRecord { operationStatus: DepositOperationStatus; - // FIXME: Duplication between this and transactionPerCoin! - depositedPerCoin: boolean[]; - - // FIXME: Improve name! - transactionPerCoin: DepositElementStatus[]; + statusPerCoin: DepositElementStatus[]; /** * When the deposit transaction was aborted and @@ -1816,31 +1756,6 @@ export interface DepositKycInfo { exchangeBaseUrl: string; } -/** - * 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: TalerPreciseTimestamp; - - contractTermsHash: string; - - deposits: { - coinPub: string; - amount: AmountString; - timestamp: TalerProtocolTimestamp; - depositFee: AmountString; - merchantPub: string; - coinSig: string; - wireHash: string; - }[]; -} - export interface TombstoneRecord { /** * Tombstone ID, with the syntax "tmb:<type>:<key>". @@ -1848,24 +1763,24 @@ export interface TombstoneRecord { id: string; } -export enum PeerPushPaymentInitiationStatus { +export enum PeerPushDebitStatus { /** * Initiated, but no purse created yet. */ - PendingCreatePurse = 10 /* ACTIVE_START */, - PendingReady = 11, - AbortingDeletePurse = 12, - AbortingRefresh = 13, + PendingCreatePurse = 0x0100_0000 /* ACTIVE_START */, + PendingReady = 0x0100_0001, + AbortingDeletePurse = 0x0103_0000, + AbortingRefresh = 0x0103_0001, - SuspendedCreatePurse = 30, - SuspendedReady = 31, - SuspendedAbortingDeletePurse = 32, - SuspendedAbortingRefresh = 33, + SuspendedCreatePurse = 0x0110_0000, + SuspendedReady = 0x0110_0001, + SuspendedAbortingDeletePurse = 0x0113_0000, + SuspendedAbortingRefresh = 0x0113_0001, - Done = 50 /* DORMANT_START */, - Aborted = 51, - Failed = 52, - Expired = 53, + Done = 0x0500_0000, + Aborted = 0x0503_0000, + Failed = 0x0501_0000, + Expired = 0x0502_0000, } export interface PeerPushPaymentCoinSelection { @@ -1876,7 +1791,7 @@ export interface PeerPushPaymentCoinSelection { /** * Record for a push P2P payment that this wallet initiated. */ -export interface PeerPushPaymentInitiationRecord { +export interface PeerPushDebitRecord { /** * What exchange are funds coming from? */ @@ -1922,11 +1837,6 @@ export interface PeerPushPaymentInitiationRecord { */ contractEncNonce: string; - /** - * FIXME: Put those in a different object store! - */ - contractTerms: PeerContractTerms; - purseExpiration: TalerProtocolTimestamp; timestampCreated: TalerPreciseTimestamp; @@ -1936,32 +1846,34 @@ export interface PeerPushPaymentInitiationRecord { /** * Status of the peer push payment initiation. */ - status: PeerPushPaymentInitiationStatus; + status: PeerPushDebitStatus; } -export enum PeerPullPaymentInitiationStatus { - PendingCreatePurse = 10 /* ACTIVE_START */, +export enum PeerPullPaymentCreditStatus { + PendingCreatePurse = 0x0100_0000, /** * Purse created, waiting for the other party to accept the * invoice and deposit money into it. */ - PendingReady = 11 /* ACTIVE_START + 1 */, - PendingMergeKycRequired = 12 /* ACTIVE_START + 2 */, - PendingWithdrawing = 13, - AbortingDeletePurse = 14, + PendingReady = 0x0100_0001, + PendingMergeKycRequired = 0x0100_0002, + PendingWithdrawing = 0x0100_0003, + + AbortingDeletePurse = 0x0103_0000, + + SuspendedCreatePurse = 0x0110_0000, + SuspendedReady = 0x0110_0001, + SuspendedMergeKycRequired = 0x0110_0002, + SuspendedWithdrawing = 0x0110_0000, - SuspendedCreatePurse = 30, - SuspendedReady = 31, - SuspendedMergeKycRequired = 32, - SuspendedWithdrawing = 33, - SuspendedAbortingDeletePurse = 34, + SuspendedAbortingDeletePurse = 0x0113_0000, - Done = 50 /* DORMANT_START */, - Failed = 51, - Aborted = 52, + Done = 0x0500_0000, + Failed = 0x0501_0000, + Aborted = 0x0503_0000, } -export interface PeerPullPaymentInitiationRecord { +export interface PeerPullCreditRecord { /** * What exchange are we using for the payment request? */ @@ -1969,6 +1881,7 @@ export interface PeerPullPaymentInitiationRecord { /** * Amount requested. + * FIXME: What type of instructed amount is i? */ amount: AmountString; @@ -1999,11 +1912,6 @@ export interface PeerPullPaymentInitiationRecord { contractEncNonce: string; - /** - * FIXME: Put in separate object store! - */ - contractTerms: PeerContractTerms; - mergeTimestamp: TalerPreciseTimestamp; mergeReserveRowId: number; @@ -2011,7 +1919,7 @@ export interface PeerPullPaymentInitiationRecord { /** * Status of the peer pull payment initiation. */ - status: PeerPullPaymentInitiationStatus; + status: PeerPullPaymentCreditStatus; kycInfo?: KycPendingInfo; @@ -2020,24 +1928,24 @@ export interface PeerPullPaymentInitiationRecord { withdrawalGroupId: string | undefined; } -export enum PeerPushPaymentIncomingStatus { - PendingMerge = 10 /* ACTIVE_START */, - PendingMergeKycRequired = 11 /* ACTIVE_START + 1 */, +export enum PeerPushCreditStatus { + PendingMerge = 0x0100_0000, + PendingMergeKycRequired = 0x0100_0001, /** * Merge was successful and withdrawal group has been created, now * everything is in the hand of the withdrawal group. */ - PendingWithdrawing = 12, + PendingWithdrawing = 0x0100_0002, - SuspendedMerge = 20, - SuspendedMergeKycRequired = 21, - SuspendedWithdrawing = 22, + SuspendedMerge = 0x0110_0000, + SuspendedMergeKycRequired = 0x0110_0001, + SuspendedWithdrawing = 0x0110_0002, - DialogProposed = 30 /* USER_ATTENTION_START */, + DialogProposed = 0x0101_0000, - Done = 50 /* DORMANT_START */, - Aborted = 51, - Failed = 52, + Done = 0x0500_0000, + Aborted = 0x0503_0000, + Failed = 0x0501_0000, } /** @@ -2046,7 +1954,7 @@ export enum PeerPushPaymentIncomingStatus { * Unique: (exchangeBaseUrl, pursePub) */ export interface PeerPushPaymentIncomingRecord { - peerPushPaymentIncomingId: string; + peerPushCreditId: string; exchangeBaseUrl: string; @@ -2069,7 +1977,7 @@ export interface PeerPushPaymentIncomingRecord { /** * Status of the peer push payment incoming initiation. */ - status: PeerPushPaymentIncomingStatus; + status: PeerPushCreditStatus; /** * Associated withdrawal group. @@ -2090,17 +1998,17 @@ export interface PeerPushPaymentIncomingRecord { } export enum PeerPullDebitRecordStatus { - PendingDeposit = 10 /* ACTIVE_START */, - AbortingRefresh = 11, + PendingDeposit = 0x0100_0001, + AbortingRefresh = 0x0103_0001, - SuspendedDeposit = 20, - SuspendedAbortingRefresh = 21, + SuspendedDeposit = 0x0110_0001, + SuspendedAbortingRefresh = 0x0113_0001, - DialogProposed = 30 /* USER_ATTENTION_START */, + DialogProposed = 0x0101_0001, - DonePaid = 50 /* DORMANT_START */, - Aborted = 51, - Failed = 52, + Done = 0x0500_0000, + Aborted = 0x0503_0000, + Failed = 0x0501_0000, } export interface PeerPullPaymentCoinSelection { @@ -2118,7 +2026,7 @@ export interface PeerPullPaymentCoinSelection { * AKA PeerPullDebit. */ export interface PeerPullPaymentIncomingRecord { - peerPullPaymentIncomingId: string; + peerPullDebitId: string; pursePub: string; @@ -2184,8 +2092,7 @@ export interface OperationRetryRecord { */ export interface CoinAvailabilityRecord { currency: string; - amountVal: number; - amountFrac: number; + value: AmountString; denomPubHash: string; exchangeBaseUrl: string; @@ -2263,10 +2170,10 @@ export interface CurrencySettingsRecord { } export enum RefundGroupStatus { - Pending = 10, - Done = 50, - Failed = 51, - Aborted = 52, + Pending = 0x0100_0000, + Done = 0x0500_0000, + Failed = 0x0501_0000, + Aborted = 0x0503_0000, } /** @@ -2302,16 +2209,16 @@ export enum RefundItemStatus { * * We'll try again! */ - Pending = 10, + Pending = 0x0100_0000, /** * Refund was obtained successfully. */ - Done = 50, + Done = 0x0500_0000, /** * Permanent error reported by the exchange * for the refund. */ - Failed = 51, + Failed = 0x0501_0000, } /** @@ -2327,7 +2234,9 @@ export interface RefundItemRecord { refundGroupId: string; - // Execution time as claimed by the merchant + /** + * Execution time as claimed by the merchant + */ executionTime: TalerProtocolTimestamp; /** @@ -2337,22 +2246,15 @@ export interface RefundItemRecord { refundAmount: AmountString; - //refundFee: AmountString; - - /** - * 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: AmountString; - coinPub: string; rtxid: number; } +export function passthroughCodec<T>(): Codec<T> { + return codecForAny(); +} + /** * Schema definition for the IndexedDB * wallet database. @@ -2362,7 +2264,6 @@ export const WalletStoresV1 = { "currencySettings", describeContents<CurrencySettingsRecord>({ keyPath: ["currency"], - versionAdded: 3, }), {}, ), @@ -2411,42 +2312,11 @@ export const WalletStoresV1 = { byReservePub: describeIndex("byReservePub", "reservePub", {}), }, ), - exchangeTos: describeStore( - "exchangeTos", - describeContents<ExchangeTosRecord>({ - keyPath: ["exchangeBaseUrl", "etag"], - }), - {}, - ), config: describeStore( "config", describeContents<ConfigRecord>({ keyPath: "key" }), {}, ), - auditorTrust: describeStore( - "auditorTrust", - describeContents<AuditorTrustRecord>({ - keyPath: ["currency", "auditorBaseUrl"], - }), - { - byAuditorPub: describeIndex("byAuditorPub", "auditorPub"), - byUid: describeIndex("byUid", "uids", { - multiEntry: true, - }), - }, - ), - exchangeTrust: describeStore( - "exchangeTrust", - describeContents<ExchangeTrustRecord>({ - keyPath: ["currency", "exchangeBaseUrl"], - }), - { - byExchangeMasterPub: describeIndex( - "byExchangeMasterPub", - "exchangeMasterPub", - ), - }, - ), denominations: describeStore( "denominations", describeContents<DenominationRecord>({ @@ -2499,6 +2369,13 @@ export const WalletStoresV1 = { byStatus: describeIndex("byStatus", "operationStatus"), }, ), + refreshSessions: describeStore( + "refreshSessions", + describeContents<RefreshSessionRecord>({ + keyPath: ["refreshGroupId", "coinIndex"], + }), + {}, + ), recoupGroups: describeStore( "recoupGroups", describeContents<RecoupGroupRecord>({ @@ -2607,17 +2484,10 @@ export const WalletStoresV1 = { }), {}, ), - ghostDepositGroups: describeStore( - "ghostDepositGroups", - describeContents<GhostDepositGroupRecord>({ - keyPath: "contractTermsHash", - }), - {}, - ), - peerPushPaymentIncoming: describeStore( - "peerPushPaymentIncoming", + peerPushCredit: describeStore( + "peerPushCredit", describeContents<PeerPushPaymentIncomingRecord>({ - keyPath: "peerPushPaymentIncomingId", + keyPath: "peerPushCreditId", }), { byExchangeAndPurse: describeIndex("byExchangeAndPurse", [ @@ -2628,24 +2498,21 @@ export const WalletStoresV1 = { "byExchangeAndContractPriv", ["exchangeBaseUrl", "contractPriv"], { - versionAdded: 5, unique: true, }, ), byWithdrawalGroupId: describeIndex( "byWithdrawalGroupId", "withdrawalGroupId", - { - versionAdded: 5, - }, + {}, ), byStatus: describeIndex("byStatus", "status"), }, ), - peerPullPaymentIncoming: describeStore( - "peerPullPaymentIncoming", + peerPullDebit: describeStore( + "peerPullDebit", describeContents<PeerPullPaymentIncomingRecord>({ - keyPath: "peerPullPaymentIncomingId", + keyPath: "peerPullDebitId", }), { byExchangeAndPurse: describeIndex("byExchangeAndPurse", [ @@ -2656,16 +2523,15 @@ export const WalletStoresV1 = { "byExchangeAndContractPriv", ["exchangeBaseUrl", "contractPriv"], { - versionAdded: 5, unique: true, }, ), byStatus: describeIndex("byStatus", "status"), }, ), - peerPullPaymentInitiations: describeStore( - "peerPullPaymentInitiations", - describeContents<PeerPullPaymentInitiationRecord>({ + peerPullCredit: describeStore( + "peerPullCredit", + describeContents<PeerPullCreditRecord>({ keyPath: "pursePub", }), { @@ -2673,15 +2539,13 @@ export const WalletStoresV1 = { byWithdrawalGroupId: describeIndex( "byWithdrawalGroupId", "withdrawalGroupId", - { - versionAdded: 5, - }, + {}, ), }, ), - peerPushPaymentInitiations: describeStore( - "peerPushPaymentInitiations", - describeContents<PeerPushPaymentInitiationRecord>({ + peerPushDebit: describeStore( + "peerPushDebit", + describeContents<PeerPushDebitRecord>({ keyPath: "pursePub", }), { @@ -2706,7 +2570,6 @@ export const WalletStoresV1 = { "userAttention", describeContents<UserAttentionRecord>({ keyPath: ["entityId", "info.type"], - versionAdded: 2, }), {}, ), @@ -2714,20 +2577,16 @@ export const WalletStoresV1 = { "refundGroups", describeContents<RefundGroupRecord>({ keyPath: "refundGroupId", - versionAdded: 7, }), { byProposalId: describeIndex("byProposalId", "proposalId"), - byStatus: describeIndex("byStatus", "status", { - versionAdded: 10, - }), + byStatus: describeIndex("byStatus", "status", {}), }, ), refundItems: describeStore( "refundItems", describeContents<RefundItemRecord>({ keyPath: "id", - versionAdded: 7, autoIncrement: true, }), { @@ -2742,7 +2601,6 @@ export const WalletStoresV1 = { "fixups", describeContents<FixupRecord>({ keyPath: "fixupName", - versionAdded: 2, }), {}, ), @@ -2845,11 +2703,10 @@ export async function exportSingleDb( dbName, undefined, () => { - // May not happen, since we're not requesting a specific version - throw Error("unexpected version change"); + logger.info(`unexpected onversionchange in exportSingleDb of ${dbName}`); }, () => { - logger.info("unexpected onupgradeneeded"); + logger.info(`unexpected onupgradeneeded in exportSingleDb of ${dbName}`); }, ); @@ -2861,7 +2718,7 @@ export async function exportSingleDb( return new Promise((resolve, reject) => { const tx = myDb.transaction(Array.from(myDb.objectStoreNames)); tx.addEventListener("complete", () => { - myDb.close(); + //myDb.close(); resolve(singleDbDump); }); // tslint:disable-next-line:prefer-for-of @@ -2897,6 +2754,7 @@ export async function exportSingleDb( if (store.keyPath == null) { rec.key = structuredEncapsulate(cursor.key); } + storeDump.records.push(rec); cursor.continue(); } }); @@ -2925,21 +2783,22 @@ async function recoverFromDump( db: IDBDatabase, dbDump: DbDumpDatabase, ): Promise<void> { - return new Promise((resolve, reject) => { - const tx = db.transaction(Array.from(db.objectStoreNames), "readwrite"); - tx.addEventListener("complete", () => { - resolve(); - }); - for (let i = 0; i < db.objectStoreNames.length; i++) { - const name = db.objectStoreNames[i]; - const storeDump = dbDump.stores[name]; - if (!storeDump) continue; - for (let rec of storeDump.records) { - tx.objectStore(name).put(rec.value, rec.key); - } + const tx = db.transaction(Array.from(db.objectStoreNames), "readwrite"); + const txProm = promiseFromTransaction(tx); + const storeNames = db.objectStoreNames; + for (let i = 0; i < storeNames.length; i++) { + const name = db.objectStoreNames[i]; + const storeDump = dbDump.stores[name]; + if (!storeDump) continue; + await promiseFromRequest(tx.objectStore(name).clear()); + logger.info(`importing ${storeDump.records.length} records into ${name}`); + for (let rec of storeDump.records) { + await promiseFromRequest(tx.objectStore(name).put(rec.value, rec.key)); + logger.info("importing record done"); } - tx.commit(); - }); + } + tx.commit(); + return await txProm; } function checkDbDump(x: any): x is DbDump { @@ -2969,106 +2828,7 @@ export interface FixupDescription { /** * Manual migrations between minor versions of the DB schema. */ -export const walletDbFixups: FixupDescription[] = [ - { - name: "RefreshGroupRecord_currency", - async fn(tx): Promise<void> { - await tx.refreshGroups.iter().forEachAsync(async (rg) => { - if (rg.currency) { - return; - } - // Empty refresh group without input coin, delete it! - if (rg.inputPerCoin.length === 0) { - await tx.refreshGroups.delete(rg.refreshGroupId); - return; - } - rg.currency = Amounts.parseOrThrow(rg.inputPerCoin[0]).currency; - await tx.refreshGroups.put(rg); - }); - }, - }, - { - name: "DepositGroupRecord_transactionPerCoin", - async fn(tx): Promise<void> { - await tx.depositGroups.iter().forEachAsync(async (dg) => { - if (dg.transactionPerCoin) { - return; - } - dg.transactionPerCoin = dg.depositedPerCoin.map( - (c) => DepositElementStatus.Unknown, - ); - await tx.depositGroups.put(dg); - }); - }, - }, - { - name: "PeerPullPaymentIncomingRecord_totalCostEstimated_add", - async fn(tx): Promise<void> { - await tx.peerPullPaymentIncoming.iter().forEachAsync(async (pi) => { - if (pi.totalCostEstimated) { - return; - } - // Not really the cost, but a good substitute for older transactions - // that don't sture the effective cost of the transaction. - pi.totalCostEstimated = pi.contractTerms.amount; - await tx.peerPullPaymentIncoming.put(pi); - }); - }, - }, - { - name: "PeerPushPaymentIncomingRecord_totalCostEstimated_add", - async fn(tx): Promise<void> { - await tx.peerPushPaymentIncoming.iter().forEachAsync(async (pi) => { - if (pi.estimatedAmountEffective) { - return; - } - const contractTerms = await tx.contractTerms.get(pi.contractTermsHash); - if (!contractTerms) { - // Not sure what we can do here! - } else { - // Not really the cost, but a good substitute for older transactions - // that don't sture the effective cost of the transaction. - pi.estimatedAmountEffective = contractTerms.contractTermsRaw.amount; - await tx.peerPushPaymentIncoming.put(pi); - } - }); - }, - }, - { - name: "PeerPullPaymentInitiationRecord_estimatedAmountEffective_add", - async fn(tx): Promise<void> { - await tx.peerPullPaymentInitiations.iter().forEachAsync(async (pi) => { - if (pi.estimatedAmountEffective) { - return; - } - pi.estimatedAmountEffective = pi.amount; - await tx.peerPullPaymentInitiations.put(pi); - }); - }, - }, - { - name: "PeerPushPaymentInitiationRecord_ALL_removeLegacyTx", - async fn(tx): Promise<void> { - await tx.peerPushPaymentInitiations.iter().forEachAsync(async (pi) => { - // Remove legacy transactions that don't have the totalCost field yet. - if (!pi.totalCost) { - await tx.peerPushPaymentInitiations.delete(pi.pursePub); - } - }); - }, - }, - { - name: "CoinAvailabilityRecord_visibleCoinCount_add", - async fn(tx): Promise<void> { - await tx.coinAvailability.iter().forEachAsync(async (r) => { - if (r.visibleCoinCount == null) { - r.visibleCoinCount = r.freshCoinCount; - await tx.coinAvailability.put(r); - } - }); - }, - }, -]; +export const walletDbFixups: FixupDescription[] = []; const logger = new Logger("db.ts"); @@ -3196,6 +2956,17 @@ function promiseFromTransaction(transaction: IDBTransaction): Promise<void> { }); } +export function promiseFromRequest(request: IDBRequest): Promise<any> { + return new Promise((resolve, reject) => { + request.onsuccess = () => { + resolve(request.result); + }; + request.onerror = () => { + reject(request.error); + }; + }); +} + /** * Purge all data in the given database. */ |