diff options
author | Florian Dold <florian@dold.me> | 2020-12-07 22:54:36 +0100 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2020-12-07 22:54:36 +0100 |
commit | e78df9b6b1178596fac5443e433708ff6a9657e2 (patch) | |
tree | 494d2ad0151d12e5f28ceb9d6091ba41d222fbc6 | |
parent | bd88f3f44304f0389322b99ff62572f3f1d7c5c1 (diff) |
backup schema WIP
-rw-r--r-- | packages/taler-wallet-core/src/operations/backup.ts | 82 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/types/ReserveTransaction.ts | 2 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/types/backupTypes.ts | 875 |
3 files changed, 656 insertions, 303 deletions
diff --git a/packages/taler-wallet-core/src/operations/backup.ts b/packages/taler-wallet-core/src/operations/backup.ts index 948a4e7a3..9f412dc7c 100644 --- a/packages/taler-wallet-core/src/operations/backup.ts +++ b/packages/taler-wallet-core/src/operations/backup.ts @@ -30,7 +30,7 @@ import { BackupCoinSource, BackupCoinSourceType, BackupDenomination, - BackupExchangeData, + BackupExchange, BackupExchangeWireFee, WalletBackupContentV1, } from "../types/backupTypes"; @@ -134,7 +134,7 @@ export async function exportBackup( async (tx) => { const bs = await getWalletBackupState(ws, tx); - const exchanges: BackupExchangeData[] = []; + const exchanges: BackupExchange[] = []; const coins: BackupCoin[] = []; const denominations: BackupDenomination[] = []; @@ -159,18 +159,18 @@ export async function exportBackup( Object.keys(wi.feesForType).forEach((x) => { for (const f of wi.feesForType[x]) { wireFees.push({ - wireType: x, - closingFee: Amounts.stringify(f.closingFee), - endStamp: f.endStamp, + wire_type: x, + closing_fee: Amounts.stringify(f.closingFee), + end_stamp: f.endStamp, sig: f.sig, - startStamp: f.startStamp, - wireFee: Amounts.stringify(f.wireFee), + start_stamp: f.startStamp, + wire_fee: Amounts.stringify(f.wireFee), }); } }); exchanges.push({ - baseUrl: ex.baseUrl, + base_url: ex.baseUrl, accounts: ex.wireInfo.accounts.map((x) => ({ paytoUri: x.payto_uri, })), @@ -179,38 +179,38 @@ export async function exportBackup( auditorUrl: x.auditor_url, denominationKeys: x.denomination_keys, })), - masterPublicKey: ex.details.masterPublicKey, + master_public_key: ex.details.masterPublicKey, currency: ex.details.currency, - protocolVersion: ex.details.protocolVersion, - wireFees, - signingKeys: ex.details.signingKeys.map((x) => ({ + protocol_version: ex.details.protocolVersion, + wire_fees: wireFees, + signing_keys: ex.details.signingKeys.map((x) => ({ key: x.key, masterSig: x.master_sig, stampEnd: x.stamp_end, stampExpire: x.stamp_expire, stampStart: x.stamp_start, })), - termsOfServiceAcceptedEtag: ex.termsOfServiceAcceptedEtag, - termsOfServiceLastEtag: ex.termsOfServiceLastEtag, + tos_etag_accepted: ex.termsOfServiceAcceptedEtag, + tos_etag_last: ex.termsOfServiceLastEtag, }); }); await tx.iter(Stores.denominations).forEach((denom) => { denominations.push({ - denomPub: denom.denomPub, - denomPubHash: denom.denomPubHash, + denom_pub: denom.denomPub, + denom_pub_hash: denom.denomPubHash, exchangeBaseUrl: canonicalizeBaseUrl(denom.exchangeBaseUrl), - feeDeposit: Amounts.stringify(denom.feeDeposit), - feeRefresh: Amounts.stringify(denom.feeRefresh), - feeRefund: Amounts.stringify(denom.feeRefund), - feeWithdraw: Amounts.stringify(denom.feeWithdraw), - isOffered: denom.isOffered, - isRevoked: denom.isRevoked, - masterSig: denom.masterSig, - stampExpireDeposit: denom.stampExpireDeposit, - stampExpireLegal: denom.stampExpireLegal, - stampExpireWithdraw: denom.stampExpireWithdraw, - stampStart: denom.stampStart, + fee_deposit: Amounts.stringify(denom.feeDeposit), + fee_refresh: Amounts.stringify(denom.feeRefresh), + fee_refund: Amounts.stringify(denom.feeRefund), + fee_withdraw: Amounts.stringify(denom.feeWithdraw), + is_offered: denom.isOffered, + is_revoked: denom.isRevoked, + master_sig: denom.masterSig, + stamp_expire_deposit: denom.stampExpireDeposit, + stamp_expire_legal: denom.stampExpireLegal, + stamp_expire_withdraw: denom.stampExpireWithdraw, + stamp_start: denom.stampStart, value: Amounts.stringify(denom.value), }); }); @@ -221,40 +221,40 @@ export async function exportBackup( case CoinSourceType.Refresh: bcs = { type: BackupCoinSourceType.Refresh, - oldCoinPub: coin.coinSource.oldCoinPub, + old_coin_pub: coin.coinSource.oldCoinPub, }; break; case CoinSourceType.Tip: bcs = { type: BackupCoinSourceType.Tip, - coinIndex: coin.coinSource.coinIndex, - walletTipId: coin.coinSource.walletTipId, + coin_index: coin.coinSource.coinIndex, + wallet_tip_id: coin.coinSource.walletTipId, }; break; case CoinSourceType.Withdraw: bcs = { type: BackupCoinSourceType.Withdraw, - coinIndex: coin.coinSource.coinIndex, - reservePub: coin.coinSource.reservePub, - withdrawalGroupId: coin.coinSource.withdrawalGroupId, + coin_index: coin.coinSource.coinIndex, + reserve_pub: coin.coinSource.reservePub, + withdrawal_group_id: coin.coinSource.withdrawalGroupId, }; break; } coins.push({ exchangeBaseUrl: coin.exchangeBaseUrl, - blindingKey: coin.blindingKey, - coinPriv: coin.coinPriv, - coinPub: coin.coinPub, - coinSource: bcs, - currentAmount: Amounts.stringify(coin.currentAmount), + blinding_key: coin.blindingKey, + coin_priv: coin.coinPriv, + coin_pub: coin.coinPub, + coin_source: bcs, + current_amount: Amounts.stringify(coin.currentAmount), fresh: coin.status === CoinStatus.Fresh, }); }); const backupBlob: WalletBackupContentV1 = { - schemaId: "gnu-taler-wallet-backup", - schemaVersion: 1, + schema_id: "gnu-taler-wallet-backup", + schema_version: 1, clock: bs.clock, coins: coins, exchanges: exchanges, @@ -262,7 +262,7 @@ export async function exportBackup( refreshSessions: [], reserves: [], denominations: [], - walletRootPub: bs.walletRootPub, + wallet_root_pub: bs.walletRootPub, }; // If the backup changed, we increment our clock. diff --git a/packages/taler-wallet-core/src/types/ReserveTransaction.ts b/packages/taler-wallet-core/src/types/ReserveTransaction.ts index aa98473a4..75d01dff7 100644 --- a/packages/taler-wallet-core/src/types/ReserveTransaction.ts +++ b/packages/taler-wallet-core/src/types/ReserveTransaction.ts @@ -15,6 +15,8 @@ */ /** + * Type declarations for the exchange's reserve transaction information. + * * @author Florian Dold <dold@taler.net> */ diff --git a/packages/taler-wallet-core/src/types/backupTypes.ts b/packages/taler-wallet-core/src/types/backupTypes.ts index cfbfdc49b..b8547a196 100644 --- a/packages/taler-wallet-core/src/types/backupTypes.ts +++ b/packages/taler-wallet-core/src/types/backupTypes.ts @@ -14,34 +14,41 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { Timestamp } from "../util/time"; - /** - * Type declarations for backup. + * Type declarations for the backup content format. * * Contains some redundancy with the other type declarations, - * as the backup schema must be very stable. + * as the backup schema must remain very stable and should be self-contained. * * Current limitations: - * 1. Contracts that are claimed but not accepted aren't - * exported yet. - * 2. There is no garbage collection mechanism for the export yet. - * (But this should actually become the main GC mechanism!) - * 3. Reserve history isn't backed up yet. - * 4. Recoup metadata isn't exported yet. + * 1. Deletion tomb stones aren't supported yet * * General considerations / decisions: * 1. Information about previously occurring errors and * retries is never backed up. * 2. The ToS text of an exchange is never backed up. - * 3. Public keys are always exported in the backup - * and never recomputed (this allows the import to + * 3. Public keys and other cryptographic values are always exported + * in the backup and never recomputed (this allows the import to * complete within a DB transaction that can't access * the crypto worker). * * @author Florian Dold <dold@taler.net> */ +/** + * Imports. + */ +import { Timestamp } from "../util/time"; +import { + ReserveClosingTransaction, + ReserveCreditTransaction, + ReserveRecoupTransaction, + ReserveWithdrawTransaction, +} from "./ReserveTransaction"; + +/** + * Type alias for strings that are to be treated like amounts. + */ type BackupAmountString = string; /** @@ -52,9 +59,15 @@ type BackupAmountString = string; * JSON is identical when the wallet's content is identical. */ export interface WalletBackupContentV1 { - schemaId: "gnu-taler-wallet-backup"; + /** + * Magic constant to identify that this is a backup content JSON. + */ + schema_id: "gnu-taler-wallet-backup-content"; - schemaVersion: 1; + /** + * Version of the schema. + */ + schema_version: 1; /** * Monotonically increasing clock of the wallet, @@ -62,115 +75,251 @@ export interface WalletBackupContentV1 { */ clock: number; - walletRootPub: string; + /** + * Root public key of the wallet. This field is present as + * a sanity check if the backup content JSON is loaded from file. + */ + wallet_root_pub: string; /** * Per-exchange data sorted by exchange master public key. + * + * Sorted by the exchange public key. */ - exchanges: BackupExchangeData[]; + exchanges: BackupExchange[]; - denominations: BackupDenomination[]; + /** + * Grouped refresh sessions. + * + * Sorted by the refresh group ID. + */ + refresh_groups: BackupRefreshGroup[]; + + /** + * Tips. + * + * Sorted by the wallet tip ID. + */ + tips: BackupTip[]; - reserves: BackupReserveData[]; + /** + * Proposals from merchants. The proposal may + * be deleted as soon as it has been accepted (and thus + * turned into a purchase). + * + * Sorted by the proposal ID. + */ + proposals: BackupProposal[]; - withdrawalGroups: BackupWithdrawalGroup[]; + /** + * Accepted purchases. + * + * Sorted by the proposal ID. + */ + purchases: BackupPurchase[]; - refreshGroups: BackupRefreshGroup[]; + /** + * All backup providers. + * + * Sorted by the provider base URL. + */ + backup_providers: BackupBackupProvider[]; +} - coins: BackupCoin[]; +/** + * Backup information about one backup storage provider. + */ +export class BackupBackupProvider { + /** + * Canonicalized base URL of the provider. + */ + base_url: string; - purchases: BackupPurchase[]; + /** + * Last known supported protocol version. + */ + supported_protocol_version: string; + + /** + * Last known annual fee. + */ + annual_fee: BackupAmountString; + + /** + * Last known storage limit. + */ + storage_limit_in_megabytes: number; + + /** + * Last proposal ID to pay for the backup provider. + */ + pay_proposal_id?: string; } +/** + * Status of recoup operations that were grouped together. + * + * The remaining amount of the corresponding coins must be set to + * zero when the recoup group is created/imported. + */ +export interface BackupRecoupGroup { + /** + * Unique identifier for the recoup group record. + */ + recoup_group_id: string; + + /** + * Timestamp when the recoup was started. + */ + timestamp_started: Timestamp; + + /** + * Timestamp when the recoup finished. + */ + timestamp_finished: Timestamp | undefined; + + /** + * Information about each coin being recouped. + */ + coins: { + coin_pub: string; + finished: boolean; + old_amount: BackupAmountString; + }[]; + + /** + * Public keys of coins that should be scheduled for refreshing + * after all individual recoups are done. + */ + recoup_refresh_coins: string[]; +} + +/** + * Types of coin sources. + */ export enum BackupCoinSourceType { Withdraw = "withdraw", Refresh = "refresh", Tip = "tip", } +/** + * Metadata about a coin obtained via withdrawing. + */ export interface BackupWithdrawCoinSource { type: BackupCoinSourceType.Withdraw; /** * Can be the empty string for orphaned coins. */ - withdrawalGroupId: string; + withdrawal_group_id: string; /** * Index of the coin in the withdrawal session. */ - coinIndex: number; + coin_index: number; /** * Reserve public key for the reserve we got this coin from. */ - reservePub: string; + reserve_pub: string; } +/** + * Metadata about a coin obtained from refreshing. + * + * FIXME: Currently does not link to the refreshGroupId because + * the wallet DB doesn't do this. Not really necessary, + * but would be more consistent. + */ export interface BackupRefreshCoinSource { type: BackupCoinSourceType.Refresh; - oldCoinPub: string; + + /** + * Public key of the coin that was refreshed into this coin. + */ + old_coin_pub: string; } +/** + * Metadata about a coin obtained from a tip. + */ export interface BackupTipCoinSource { type: BackupCoinSourceType.Tip; - walletTipId: string; - coinIndex: number; + + /** + * Wallet's identifier for the tip that this coin + * originates from. + */ + wallet_tip_id: string; + + /** + * Index in the tip planchets of the tip. + */ + coin_index: number; } +/** + * Metadata about a coin depending on the origin. + */ export type BackupCoinSource = | BackupWithdrawCoinSource | BackupRefreshCoinSource | BackupTipCoinSource; +/** + * Backup information about a coin. + * + * (Always part of a BackupExchange) + */ export interface BackupCoin { /** * Where did the coin come from? Used for recouping coins. */ - coinSource: BackupCoinSource; + coin_source: BackupCoinSource; /** * Public key of the coin. */ - coinPub: string; + coin_pub: string; /** * Private key to authorize operations on the coin. */ - coinPriv: string; + coin_priv: string; /** * Key used by the exchange used to sign the coin. */ - denomPub: string; + denom_pub: string; /** * Hash of the public key that signs the coin. */ - denomPubHash: string; + denom_pub_hash: string; /** * Unblinded signature by the exchange. */ - denomSig: string; + denom_sig: string; /** * Amount that's left on the coin. */ - currentAmount: BackupAmountString; - - /** - * Base URL that identifies the exchange from which we got the - * coin. - */ - exchangeBaseUrl: string; + current_amount: BackupAmountString; /** * Blinding key used when withdrawing the coin. * Potentionally used again during payback. */ - blindingKey: string; + blinding_key: string; + /** + * Does the wallet think that the coin is still fresh? + * + * FIXME: If we always refresh when importing a backup, do + * we even need this flag? + */ fresh: boolean; } @@ -181,65 +330,66 @@ export interface BackupTip { /** * Tip ID chosen by the wallet. */ - walletTipId: string; + wallet_tip_id: string; /** * The merchant's identifier for this tip. */ - merchantTipId: string; + merchant_tip_id: string; /** * Has the user accepted the tip? Only after the tip has been accepted coins * withdrawn from the tip may be used. */ - acceptedTimestamp: Timestamp | undefined; + timestamp_accepted: Timestamp | undefined; - createdTimestamp: Timestamp; + /** + * When was the tip first scanned by the wallet? + */ + timestamp_created: Timestamp; /** * Timestamp for when the wallet finished picking up the tip * from the merchant. */ - pickedUpTimestamp: Timestamp | undefined; + timestam_picked_up: Timestamp | undefined; /** * The tipped amount. */ - tipAmountRaw: BackupAmountString; - - tipAmountEffective: BackupAmountString; + tip_amount_raw: BackupAmountString; /** * Timestamp, the tip can't be picked up anymore after this deadline. */ - tipExpiration: Timestamp; + timestamp_expiration: Timestamp; /** * The exchange that will sign our coins, chosen by the merchant. */ - exchangeBaseUrl: string; + exchange_base_url: string; /** * Base URL of the merchant that is giving us the tip. */ - merchantBaseUrl: string; + merchant_base_url: string; /** * Planchets, the members included in TipPlanchetDetail will be sent to the * merchant. */ planchets?: { - blindingKey: string; - coinEv: string; - coinPriv: string; - coinPub: string; + blinding_key: string; + coin_ev: string; + coin_priv: string; + coin_pub: string; }[]; - totalCoinValue: BackupAmountString; - totalWithdrawCost: BackupAmountString; - - selectedDenoms: { - denomPubHash: string; + /** + * Selected denominations. Determines the effective tip amount. + */ + selected_denoms: { + denom_pub_hash: string; count: number; }[]; } @@ -264,73 +414,65 @@ export interface BackupRefreshPlanchet { /** * Public key for the coin. */ - publicKey: string; + public_key: string; + /** * Private key for the coin. */ - privateKey: string; + private_key: string; + /** * Blinded public key. */ - coinEv: string; + coin_ev: string; + /** * Blinding key used. */ - blindingKey: string; + blinding_key: string; } -export interface BackupRefreshSessionRecord { - /** - * Public key that's being melted in this session. - */ - meltCoinPub: string; - - /** - * How much of the coin's value is melted away - * with this refresh session? - */ - amountRefreshInput: BackupAmountString; - - /** - * Sum of the value of denominations we want - * to withdraw in this session, without fees. - */ - amountRefreshOutput: BackupAmountString; - +/** + * Information about one refresh session, always part + * of a refresh group. + * + * (Public key of the old coin is stored in the refresh group.) + */ +export interface BackupRefreshSession { /** - * Signature to confirm the melting. + * Signature made by the old coin to confirm the melting. */ - confirmSig: string; + confirm_sig: string; /** * Hased denominations of the newly requested coins. */ - newDenomHashes: string[]; + new_denom_hashes: string[]; /** * Denominations of the newly requested coins. */ - newDenoms: string[]; + new_denoms: string[]; /** * Planchets for each cut-and-choose instance. */ - planchetsForGammas: BackupRefreshPlanchet[][]; + planchets_for_gammas: BackupRefreshPlanchet[][]; /** * The transfer keys, kappa of them. */ - transferPubs: string[]; + transfer_pubs: string[]; /** * Private keys for the transfer public keys. */ - transferPrivs: string[]; + transfer_privs: string[]; /** * The no-reveal-index after we've done the melting. */ - norevealIndex?: number; + noreveal_index?: number; /** * Hash of the session. @@ -340,65 +482,97 @@ export interface BackupRefreshSessionRecord { /** * Timestamp when the refresh session finished. */ - finishedTimestamp: Timestamp | undefined; + timestamp_finished: Timestamp | undefined; /** * When has this refresh session been created? */ - timestampCreated: Timestamp; - - /** - * Base URL for the exchange we're doing the refresh with. - */ - exchangeBaseUrl: string; + timestamp_created: Timestamp; } +/** + * Information about one refresh group. + * + * May span more than one exchange, but typically doesn't + */ export interface BackupRefreshGroup { - refreshGroupId: string; + refresh_group_id: string; reason: BackupRefreshReason; - oldCoinPubs: string[]; + /** + * Details per old coin. + */ + old_coins: { + /** + * Public key of the old coin, + */ + coin_pub: string; - refreshSessionPerCoin: (BackupRefreshSessionRecord | undefined)[]; + /** + * Requested amount to refresh. Must be subtracted from the coin's remaining + * amount as soon as the coin is added to the refresh group. + */ + input_amount: BackupAmountString; - inputPerCoin: BackupAmountString[]; + /** + * Estimated output (may change if it takes a long time to create the + * actual session). + */ + estimated_output_amount: BackupAmountString; - estimatedOutputPerCoin: BackupAmountString[]; + /** + * Coin is skipped (finished without a refresh session) because + * there is not enough value left on it. + */ + skipped: boolean; + + /** + * Refresh session (if created) or undefined it not created yet. + */ + refresh_session: BackupRefreshSession | undefined; + }[]; /** - * Timestamp when the refresh session finished. + * Timestamp when the refresh group finished. */ - timestampFinished: Timestamp | undefined; + timestamp_finished: Timestamp | undefined; } +/** + * Backup information for a withdrawal group. + * + * Always part of a BackupReserve. + */ export interface BackupWithdrawalGroup { - withdrawalGroupId: string; - - reservePub: string; + withdrawal_group_id: string; /** * When was the withdrawal operation started started? * Timestamp in milliseconds. */ - timestampStart: Timestamp; + timestamp_start: Timestamp; /** * When was the withdrawal operation completed? */ - timestampFinish?: Timestamp; + timestamp_finish?: Timestamp; /** * Amount including fees (i.e. the amount subtracted from the * reserve to withdraw all coins in this withdrawal session). + * + * Note that this *includes* the amount remaining in the reserve + * that is too small to be withdrawn, and thus can't be derived + * from selectedDenoms. */ - rawWithdrawalAmount: BackupAmountString; - - totalCoinValue: BackupAmountString; - totalWithdrawCost: BackupAmountString; + raw_withdrawal_amount: BackupAmountString; - selectedDenoms: { - denomPubHash: string; + /** + * Multiset of denominations selected for withdrawal. + */ + selected_denoms: { + denom_pub_hash: string; count: number; }[]; @@ -406,9 +580,21 @@ export interface BackupWithdrawalGroup { * One planchet/coin for each selected denomination. */ planchets: { - blindingKey: string; - coinPriv: string; - coinPub: string; + blinding_key: string; + coin_priv: string; + coin_pub: string; + + /** + * Blinded coin public key. + */ + coin_ev: string; + + /** + * Hashed blinded coin public key. + * + * (Stored here for easy reserve history merging.) + */ + coin_ev_hash: string; }[]; } @@ -418,17 +604,34 @@ export enum BackupRefundState { Pending = "pending", } +/** + * Common information about a refund. + */ export interface BackupRefundItemCommon { - // Execution time as claimed by the merchant - executionTime: Timestamp; + /** + * Execution time as claimed by the merchant + */ + execution_time: Timestamp; /** * Time when the wallet became aware of the refund. */ - obtainedTime: Timestamp; + obtained_time: Timestamp; - refundAmount: BackupAmountString; - refundFee: BackupAmountString; + /** + * Amount refunded for the coin. + */ + refund_amount: BackupAmountString; + + /** + * Coin being refunded. + */ + coin_pub: string; + + /** + * The refund transaction ID for the refund. + */ + rtransaction_id: number; /** * Upper bound on the refresh cost incurred by @@ -436,8 +639,11 @@ export interface BackupRefundItemCommon { * * Might be lower in practice when two refunds on the same * coin are refreshed in the same refresh operation. + * + * Used to display fees, and stored since it's expensive to recompute + * accurately. */ - totalRefreshCostBound: BackupAmountString; + total_refresh_cost_bound: BackupAmountString; } /** @@ -469,77 +675,68 @@ export interface BackupPurchase { * Proposal ID for this purchase. Uniquely identifies the * purchase and the proposal. */ - proposalId: string; + proposal_id: string; /** * Contract terms we got from the merchant. */ - contractTermsRaw: string; - - /** - * Amount requested by the merchant. - */ - paymentAmount: BackupAmountString; + contract_terms_raw: string; - /** - * Public keys of the coins that were selected. - */ - coinPubs: string[]; - - /** - * Deposit permission signature of each coin. - */ - coinSigs: string[]; - - /** - * Amount that each coin contributes. - */ - coinContributions: BackupAmountString[]; - - /** - * How much of the wire fees is the customer paying? - */ - customerWireFees: BackupAmountString; + pay_coins: { + /** + * Public keys of the coins that were selected. + */ + coin_pubs: string[]; - /** - * How much of the deposit fees is the customer paying? - */ - customerDepositFees: BackupAmountString; + /** + * Deposit permission signature of each coin. + */ + coin_sigs: string[]; - totalPayCost: BackupAmountString; + /** + * Amount that each coin contributes. + */ + contribution: BackupAmountString; + }; /** * Timestamp of the first time that sending a payment to the merchant * for this purchase was successful. */ - timestampFirstSuccessfulPay: Timestamp | undefined; + timestamp_first_successful_pay: Timestamp | undefined; - merchantPaySig: string | undefined; + /** + * Signature by the merchant confirming the payment. + */ + merchant_pay_sig: string | undefined; /** * When was the purchase made? * Refers to the time that the user accepted. */ - timestampAccept: Timestamp; + timestamp_accept: Timestamp; /** * Pending refunds for the purchase. A refund is pending * when the merchant reports a transient error from the exchange. */ - refunds: { [refundKey: string]: BackupRefundItem }; + refunds: BackupRefundItem[]; /** * When was the last refund made? * Set to 0 if no refund was made on the purchase. */ - timestampLastRefundStatus: Timestamp | undefined; + timestamp_last_refund_status: Timestamp | undefined; - abortStatus?: "abort-refund" | "abort-finished"; + /** + * Abort status of the payment. + */ + abort_status?: "abort-refund" | "abort-finished"; /** * Continue querying the refund status until this deadline has expired. */ - autoRefundDeadline: Timestamp | undefined; + auto_refund_deadline: Timestamp | undefined; } /** @@ -556,210 +753,191 @@ export interface BackupDenomination { /** * The denomination public key. */ - denomPub: string; + denom_pub: string; /** * Hash of the denomination public key. * Stored in the database for faster lookups. */ - denomPubHash: string; + denom_pub_hash: string; /** * Fee for withdrawing. */ - feeWithdraw: BackupAmountString; + fee_withdraw: BackupAmountString; /** * Fee for depositing. */ - feeDeposit: BackupAmountString; + fee_deposit: BackupAmountString; /** * Fee for refreshing. */ - feeRefresh: BackupAmountString; + fee_refresh: BackupAmountString; /** * Fee for refunding. */ - feeRefund: BackupAmountString; + fee_refund: BackupAmountString; /** * Validity start date of the denomination. */ - stampStart: Timestamp; + stamp_start: Timestamp; /** * Date after which the currency can't be withdrawn anymore. */ - stampExpireWithdraw: Timestamp; + stamp_expire_withdraw: Timestamp; /** * Date after the denomination officially doesn't exist anymore. */ - stampExpireLegal: Timestamp; + stamp_expire_legal: Timestamp; /** * Data after which coins of this denomination can't be deposited anymore. */ - stampExpireDeposit: Timestamp; + stamp_expire_deposit: Timestamp; /** * Signature by the exchange's master key over the denomination * information. */ - masterSig: string; + master_sig: string; /** * 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; + is_offered: 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; + is_revoked: boolean; } export interface BackupReserve { - reservePub: string; - reservePriv: string; - /** - * The exchange base URL. - */ - exchangeBaseUrl: string; - - bankConfirmUrl?: string; - - /** - * Wire information (as payto URI) for the bank account that - * transfered funds for this reserve. - */ - senderWire?: string; -} - -export interface BackupReserveData { /** * The reserve public key. */ - reservePub: string; + reserve_pub: string; /** * The reserve private key. */ - reservePriv: string; - - /** - * The exchange base URL. - */ - exchangeBaseUrl: string; + reserve_priv: 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; + timestamp_created: Timestamp; /** * Wire information (as payto URI) for the bank account that * transfered funds for this reserve. */ - senderWire?: string; + sender_wire?: string; /** * Amount that was sent by the user to fund the reserve. */ - instructedAmount: BackupAmountString; + instructed_amount: BackupAmountString; /** * Extra state for when this is a withdrawal involving * a Taler-integrated bank. */ - bankInfo?: { + bank_info?: { /** * Status URL that the wallet will use to query the status * of the Taler withdrawal operation on the bank's side. */ - statusUrl: string; + status_url: string; - confirmUrl?: string; + /** + * URL that the user should be instructed to navigate to + * in order to confirm the transfer (or show instructions/help + * on how to do that at a PoS terminal). + */ + confirm_url?: string; /** * Exchange payto URI that the bank will use to fund the reserve. */ - exchangePaytoUri: string; + exchange_payto_uri: string; /** - * Do we still need to register the reserve with the bank? + * Time when the information about this reserve was posted to the bank. */ - registerPending: boolean; - }; + timestamp_reserve_info_posted: Timestamp | undefined; - initialWithdrawalGroupId: string; + /** + * Time when the reserve was confirmed by the bank. + * + * Set to undefined if not confirmed yet. + */ + timestamp_bank_confirmed: Timestamp | undefined; + }; - initialTotalCoinValue: BackupAmountString; + /** + * Pre-allocated withdrawal group ID that will be + * used for the first withdrawal. + * + * (Already created so it can be referenced in the transactions list + * before it really exists, as there'll be an entry for the withdrawal + * even before the withdrawal group really has been created). + */ + initial_withdrawal_group_id: string; - initialTotalWithdrawCost: BackupAmountString; - initialSelectedDenoms: { - denomPubHash: string; + /** + * Denominations selected for the initial withdrawal. + * Stored here to show costs before withdrawal has begun. + */ + initial_selected_denoms: { + denom_pub_hash: string; count: number; }[]; -} -export interface ExchangeBankAccount { - paytoUri: string; + /** + * Groups of withdrawal operations for this reserve. Typically just one. + */ + withdrawal_groups: BackupWithdrawalGroup[]; } /** * Wire fee for one wire method as stored in the * wallet's database. + * + * (Flattened to a list to make the declaration simpler). */ export interface BackupExchangeWireFee { - wireType: string; + wire_type: string; /** * Fee for wire transfers. */ - wireFee: string; + wire_fee: string; /** * Fees to close and refund a reserve. */ - closingFee: string; + closing_fee: string; /** * Start date of the fee. */ - startStamp: Timestamp; + start_stamp: Timestamp; /** * End date of the fee. */ - endStamp: Timestamp; + end_stamp: Timestamp; /** * Signature made by the exchange master key. @@ -771,11 +949,11 @@ export interface BackupExchangeWireFee { * Structure of one exchange signing key in the /keys response. */ export class BackupExchangeSignKey { - stampStart: Timestamp; - stampExpire: Timestamp; - stampEnd: Timestamp; + stamp_start: Timestamp; + stamp_expire: Timestamp; + stamp_end: Timestamp; key: string; - masterSig: string; + master_sig: string; } /** @@ -800,29 +978,32 @@ export class BackupExchangeAuditor { /** * Auditor's public key. */ - auditorPub: string; + auditor_pub: string; /** * Base URL of the auditor. */ - auditorUrl: string; + auditor_url: string; /** * List of signatures for denominations by the auditor. */ - denominationKeys: AuditorDenomSig[]; + denomination_keys: AuditorDenomSig[]; } -export interface BackupExchangeData { +/** + * Backup information about an exchange. + */ +export interface BackupExchange { /** - * Base url of the exchange. + * Canonicalized base url of the exchange. */ - baseUrl: string; + base_url: string; /** * Master public key of the exchange. */ - masterPublicKey: string; + master_public_key: string; /** * Auditors (partially) auditing the exchange. @@ -835,27 +1016,197 @@ export interface BackupExchangeData { currency: string; /** + * Denominations offered by the exchange. + */ + denominations: BackupDenomination[]; + + /** + * Reserves at the exchange. + */ + reserves: BackupReserve[]; + + coins: BackupCoin[]; + + /** * Last observed protocol version. */ - protocolVersion: string; + protocol_version: string; /** * Signing keys we got from the exchange, can also contain * older signing keys that are not returned by /keys anymore. */ - signingKeys: BackupExchangeSignKey[]; + signing_keys: BackupExchangeSignKey[]; - wireFees: BackupExchangeWireFee[]; + wire_fees: BackupExchangeWireFee[]; - accounts: ExchangeBankAccount[]; + /** + * Bank accounts offered by the exchange; + */ + accounts: { + payto_uri: string; + master_sig: string; + }[]; /** * ETag for last terms of service download. */ - termsOfServiceLastEtag: string | undefined; + tos_etag_last: string | undefined; /** * ETag for last terms of service download. */ - termsOfServiceAcceptedEtag: string | undefined; + tos_etag_accepted: string | undefined; +} + +export enum WalletReserveHistoryItemType { + Credit = "credit", + Withdraw = "withdraw", + Closing = "closing", + Recoup = "recoup", +} + +export interface BackupReserveHistoryCreditItem { + type: WalletReserveHistoryItemType.Credit; + + /** + * Amount we expect to see credited. + */ + expected_amount?: BackupAmountString; + + /** + * Item from the reserve transaction history that this + * wallet reserve history item matches up with. + */ + matched_exchange_transaction?: ReserveCreditTransaction; +} + +export interface BackupReserveHistoryWithdrawItem { + type: WalletReserveHistoryItemType.Withdraw; + + expected_amount?: BackupAmountString; + + /** + * Hash of the blinded coin. + * + * When this value is set, it indicates that a withdrawal is active + * in the wallet for the + */ + expected_coin_ev_hash?: string; + + /** + * Item from the reserve transaction history that this + * wallet reserve history item matches up with. + */ + matched_exchange_transaction?: ReserveWithdrawTransaction; +} + +export interface BackupReserveHistoryClosingItem { + type: WalletReserveHistoryItemType.Closing; + + /** + * Item from the reserve transaction history that this + * wallet reserve history item matches up with. + */ + matched_exchange_transaction?: ReserveClosingTransaction; +} + +export interface BackupReserveHistoryRecoupItem { + type: WalletReserveHistoryItemType.Recoup; + + /** + * Amount we expect to see recouped. + */ + expected_amount?: BackupAmountString; + + /** + * Item from the reserve transaction history that this + * wallet reserve history item matches up with. + */ + matched_exchange_transaction?: ReserveRecoupTransaction; +} + +export type BackupReserveHistoryItem = + | BackupReserveHistoryCreditItem + | BackupReserveHistoryWithdrawItem + | BackupReserveHistoryRecoupItem + | BackupReserveHistoryClosingItem; + +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. + */ + PermanentlyFailed = "permanently-failed", + /** + * Downloaded proposal was detected as a re-purchase. + */ + Repurchase = "repurchase", +} + +/** + * Proposal by a merchant. + */ +export interface BackupProposal { + /** + * Downloaded data from the merchant. + */ + contract_terms_raw?: string; + + /** + * Unique ID when the order is stored in the wallet DB. + */ + proposal_id: string; + + /** + * Timestamp of when the record + * was created. + */ + timestamp: Timestamp; + + /** + * Private key for the nonce. + */ + nonce_priv: string; + + /** + * Public key for the nonce. + */ + nonce_pub: string; + + /** + * Claim token initially given by the merchant. + */ + claim_token: string | undefined; + + /** + * Status of the proposal. + */ + proposal_status: ProposalStatus; + + /** + * Proposal that this one got "redirected" to as part of + * the repurchase detection. + */ + repurchase_proposal_id: string | undefined; + + /** + * Session ID we got when downloading the contract. + */ + download_session_id?: string; } |