diff --git a/packages/taler-wallet-core/src/operations/backup.ts b/packages/taler-wallet-core/src/operations/backup.ts index cf73de62d..6c497b305 100644 --- a/packages/taler-wallet-core/src/operations/backup.ts +++ b/packages/taler-wallet-core/src/operations/backup.ts @@ -54,7 +54,7 @@ import { stringToBytes, } from "../crypto/talerCrypto"; import { canonicalizeBaseUrl, canonicalJson, j2s } from "../util/helpers"; -import { Timestamp } from "../util/time"; +import { getTimestampNow, Timestamp } from "../util/time"; import { URL } from "../util/url"; import { AmountString } from "../types/talerTypes"; import { @@ -77,6 +77,16 @@ interface WalletBackupConfState { walletRootPriv: string; clocks: { [device_id: string]: number }; lastBackupHash?: string; + + /** + * Timestamp stored in the last backup. + */ + lastBackupTimestamp?: Timestamp; + + /** + * Last time we tried to do a backup. + */ + lastBackupCheckTimestamp?: Timestamp; lastBackupNonce?: string; } @@ -237,6 +247,7 @@ export async function exportBackup( base_url: ex.baseUrl, accounts: ex.wireInfo.accounts.map((x) => ({ payto_uri: x.payto_uri, + master_sig: x.master_sig, })), auditors: ex.details.auditors.map((x) => ({ auditor_pub: x.auditor_pub, @@ -261,6 +272,10 @@ export async function exportBackup( }); }); + if (!bs.lastBackupTimestamp) { + bs.lastBackupTimestamp = getTimestampNow(); + } + const backupBlob: WalletBackupContentV1 = { schema_id: "gnu-taler-wallet-backup-content", schema_version: 1, @@ -275,6 +290,10 @@ export async function exportBackup( recoup_groups: [], refresh_groups: [], tips: [], + timestamp: bs.lastBackupTimestamp, + trusted_auditors: {}, + trusted_exchanges: {}, + intern_table: {}, }; // If the backup changed, we increment our clock. diff --git a/packages/taler-wallet-core/src/types/backupTypes.ts b/packages/taler-wallet-core/src/types/backupTypes.ts index 2b0e74968..247a4d398 100644 --- a/packages/taler-wallet-core/src/types/backupTypes.ts +++ b/packages/taler-wallet-core/src/types/backupTypes.ts @@ -50,6 +50,21 @@ import { */ type BackupAmountString = string; +/** + * A human-recognizable identifier here that is + * reasonable unique and assigned the first time the wallet is + * started/installed, such as: + * + * `${wallet-implementation} ${os} ${hostname} (${short-uid})` + * => e.g. "GNU Taler Android iceking ABC123" + */ +type DeviceIdString = string; + +/** + * Integer-valued clock. + */ +type ClockValue = number; + /** * Content of the backup. * @@ -76,21 +91,29 @@ export interface WalletBackupContentV1 { /** * Current device identifier that "owns" the backup. - * + * * This identifier allows one wallet to notice when another * wallet is "alive" and connected to the same sync provider. */ - current_device_id: string; + current_device_id: DeviceIdString; /** * Monotonically increasing clock of the wallet, * used to determine causality when merging backups. - * + * * Information about other clocks, used to delete * tombstones in the hopefully rare case that multiple wallets * are connected to the same sync server. */ - clocks: { [device_id: string]: number }; + clocks: { [device_id: string]: ClockValue }; + + /** + * Timestamp of the backup. + * + * This timestamp should only be advanced if the content + * of the backup changes. + */ + timestamp: Timestamp; /** * Per-exchange data sorted by exchange master public key. @@ -155,6 +178,94 @@ export interface WalletBackupContentV1 { */ proposal_id: string; }[]; + + /** + * Trusted auditors, either for official (3 letter) or local (4-12 letter) + * currencies. + * + * Auditors are sorted by their canonicalized base URL. + */ + trusted_auditors: { [currency: string]: BackupTrustAuditor[] }; + + /** + * Trusted exchange. Only applicable for local currencies (4-12 letter currency code). + * + * Exchanges are sorted by their canonicalized base URL. + */ + trusted_exchanges: { [currency: string]: BackupTrustExchange[] }; + + /** + * Interning table for forgettable values of contract terms. + * + * Used to reduce storage space, as many forgettable items (product image, + * addresses, etc.) might be shared among many contract terms. + */ + intern_table: { [hash: string]: any }; +} + +/** + * Trust declaration for an auditor. + * + * The trust applies based on the public key of + * the auditor, irrespective of what base URL the exchange + * is referencing. + */ +export interface BackupTrustAuditor { + /** + * Base URL of the auditor. + */ + auditor_base_url: string; + + /** + * Public key of the auditor. + */ + auditor_pub: string; + + /** + * Clock when the auditor trust has been added. + * + * Can be undefined if this entry represents a removal delta + * from the wallet's defaults. + */ + clock_added?: ClockValue; + + /** + * Clock for when the auditor trust has been removed. + */ + clock_removed?: ClockValue; +} + +/** + * Trust declaration for an exchange. + * + * The trust only applies for the combination of base URL + * and public key. If the master public key changes while the base + * URL stays the same, the exchange has to be re-added by a wallet update + * or by the user. + */ +export interface BackupTrustExchange { + /** + * Canonicalized exchange base URL. + */ + exchange_base_url: string; + + /** + * Master public key of the exchange. + */ + exchange_master_pub: string; + + /** + * Clock when the exchange trust has been added. + * + * Can be undefined if this entry represents a removal delta + * from the wallet's defaults. + */ + clock_added?: ClockValue; + + /** + * Clock for when the exchange trust has been removed. + */ + clock_removed?: ClockValue; } /** @@ -182,9 +293,9 @@ export class BackupBackupProvider { storage_limit_in_megabytes: number; /** - * Last proposal ID to pay for the backup provider. + * Proposal IDs for payments to this provider. */ - pay_proposal_id?: string; + pay_proposal_ids: string[]; } /** @@ -421,21 +532,6 @@ export enum BackupRefreshReason { Scheduled = "scheduled", } -/** - * Planchet for a coin during refresh. - */ -export interface BackupRefreshPlanchet { - /** - * Private key for the coin. - */ - private_key: string; - - /** - * Blinding key used. - */ - blinding_key: string; -} - /** * Information about one refresh session, always part * of a refresh group. @@ -443,45 +539,21 @@ export interface BackupRefreshPlanchet { * (Public key of the old coin is stored in the refresh group.) */ export interface BackupRefreshSession { - /** - * Signature made by the old coin to confirm the melting. - */ - confirm_sig: string; - /** * Hased denominations of the newly requested coins. */ new_denom_hashes: string[]; /** - * Planchets for each cut-and-choose instance. + * Seed used to derive the planchets and + * transfer private keys for this refresh session. */ - planchets_for_gammas: BackupRefreshPlanchet[][]; - - /** - * Private keys for the transfer public keys. - */ - transfer_privs: string[]; + session_secret_seed: string; /** * The no-reveal-index after we've done the melting. */ noreveal_index?: number; - - /** - * Hash of the session. - */ - hash: string; - - /** - * Timestamp when the refresh session finished. - */ - timestamp_finished: Timestamp | undefined; - - /** - * When has this refresh session been created? - */ - timestamp_created: Timestamp; } /** @@ -516,10 +588,10 @@ export interface BackupRefreshGroup { estimated_output_amount: BackupAmountString; /** - * Coin is skipped (finished without a refresh session) because - * there is not enough value left on it. + * Did the refresh session finish (or was it unnecessary/impossible to create + * one) */ - skipped: boolean; + finished: boolean; /** * Refresh session (if created) or undefined it not created yet. @@ -620,7 +692,7 @@ 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. */ @@ -891,7 +963,7 @@ export interface BackupReserve { } /** - * Wire fee for one wire method as stored in the + * Wire fee for one wire payment target type as stored in the * wallet's database. * * (Flattened to a list to make the declaration simpler). @@ -1023,10 +1095,7 @@ export interface BackupExchange { */ accounts: { payto_uri: string; - /** - * Optional, since older wallets don't store this. - */ - master_sig?: string; + master_sig: string; }[]; /** diff --git a/packages/taler-wallet-core/src/types/dbTypes.ts b/packages/taler-wallet-core/src/types/dbTypes.ts index dc1ee1046..a4f14ff0c 100644 --- a/packages/taler-wallet-core/src/types/dbTypes.ts +++ b/packages/taler-wallet-core/src/types/dbTypes.ts @@ -507,6 +507,7 @@ export enum ExchangeUpdateStatus { export interface ExchangeBankAccount { payto_uri: string; + master_sig: string; } export interface ExchangeWireInfo {