backup schema

This commit is contained in:
Florian Dold 2020-12-10 17:50:17 +01:00
parent c27a7abfba
commit 80a0fab126
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
3 changed files with 147 additions and 58 deletions

View File

@ -54,7 +54,7 @@ import {
stringToBytes, stringToBytes,
} from "../crypto/talerCrypto"; } from "../crypto/talerCrypto";
import { canonicalizeBaseUrl, canonicalJson, j2s } from "../util/helpers"; import { canonicalizeBaseUrl, canonicalJson, j2s } from "../util/helpers";
import { Timestamp } from "../util/time"; import { getTimestampNow, Timestamp } from "../util/time";
import { URL } from "../util/url"; import { URL } from "../util/url";
import { AmountString } from "../types/talerTypes"; import { AmountString } from "../types/talerTypes";
import { import {
@ -77,6 +77,16 @@ interface WalletBackupConfState {
walletRootPriv: string; walletRootPriv: string;
clocks: { [device_id: string]: number }; clocks: { [device_id: string]: number };
lastBackupHash?: string; lastBackupHash?: string;
/**
* Timestamp stored in the last backup.
*/
lastBackupTimestamp?: Timestamp;
/**
* Last time we tried to do a backup.
*/
lastBackupCheckTimestamp?: Timestamp;
lastBackupNonce?: string; lastBackupNonce?: string;
} }
@ -237,6 +247,7 @@ export async function exportBackup(
base_url: ex.baseUrl, base_url: ex.baseUrl,
accounts: ex.wireInfo.accounts.map((x) => ({ accounts: ex.wireInfo.accounts.map((x) => ({
payto_uri: x.payto_uri, payto_uri: x.payto_uri,
master_sig: x.master_sig,
})), })),
auditors: ex.details.auditors.map((x) => ({ auditors: ex.details.auditors.map((x) => ({
auditor_pub: x.auditor_pub, auditor_pub: x.auditor_pub,
@ -261,6 +272,10 @@ export async function exportBackup(
}); });
}); });
if (!bs.lastBackupTimestamp) {
bs.lastBackupTimestamp = getTimestampNow();
}
const backupBlob: WalletBackupContentV1 = { const backupBlob: WalletBackupContentV1 = {
schema_id: "gnu-taler-wallet-backup-content", schema_id: "gnu-taler-wallet-backup-content",
schema_version: 1, schema_version: 1,
@ -275,6 +290,10 @@ export async function exportBackup(
recoup_groups: [], recoup_groups: [],
refresh_groups: [], refresh_groups: [],
tips: [], tips: [],
timestamp: bs.lastBackupTimestamp,
trusted_auditors: {},
trusted_exchanges: {},
intern_table: {},
}; };
// If the backup changed, we increment our clock. // If the backup changed, we increment our clock.

View File

@ -50,6 +50,21 @@ import {
*/ */
type BackupAmountString = string; 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. * Content of the backup.
* *
@ -80,7 +95,7 @@ export interface WalletBackupContentV1 {
* This identifier allows one wallet to notice when another * This identifier allows one wallet to notice when another
* wallet is "alive" and connected to the same sync provider. * wallet is "alive" and connected to the same sync provider.
*/ */
current_device_id: string; current_device_id: DeviceIdString;
/** /**
* Monotonically increasing clock of the wallet, * Monotonically increasing clock of the wallet,
@ -90,7 +105,15 @@ export interface WalletBackupContentV1 {
* tombstones in the hopefully rare case that multiple wallets * tombstones in the hopefully rare case that multiple wallets
* are connected to the same sync server. * 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. * Per-exchange data sorted by exchange master public key.
@ -155,6 +178,94 @@ export interface WalletBackupContentV1 {
*/ */
proposal_id: string; 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; 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", 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 * Information about one refresh session, always part
* of a refresh group. * of a refresh group.
@ -443,45 +539,21 @@ export interface BackupRefreshPlanchet {
* (Public key of the old coin is stored in the refresh group.) * (Public key of the old coin is stored in the refresh group.)
*/ */
export interface BackupRefreshSession { export interface BackupRefreshSession {
/**
* Signature made by the old coin to confirm the melting.
*/
confirm_sig: string;
/** /**
* Hased denominations of the newly requested coins. * Hased denominations of the newly requested coins.
*/ */
new_denom_hashes: string[]; 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[][]; session_secret_seed: string;
/**
* Private keys for the transfer public keys.
*/
transfer_privs: string[];
/** /**
* The no-reveal-index after we've done the melting. * The no-reveal-index after we've done the melting.
*/ */
noreveal_index?: number; 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; estimated_output_amount: BackupAmountString;
/** /**
* Coin is skipped (finished without a refresh session) because * Did the refresh session finish (or was it unnecessary/impossible to create
* there is not enough value left on it. * one)
*/ */
skipped: boolean; finished: boolean;
/** /**
* Refresh session (if created) or undefined it not created yet. * Refresh session (if created) or undefined it not created yet.
@ -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. * wallet's database.
* *
* (Flattened to a list to make the declaration simpler). * (Flattened to a list to make the declaration simpler).
@ -1023,10 +1095,7 @@ export interface BackupExchange {
*/ */
accounts: { accounts: {
payto_uri: string; payto_uri: string;
/** master_sig: string;
* Optional, since older wallets don't store this.
*/
master_sig?: string;
}[]; }[];
/** /**

View File

@ -507,6 +507,7 @@ export enum ExchangeUpdateStatus {
export interface ExchangeBankAccount { export interface ExchangeBankAccount {
payto_uri: string; payto_uri: string;
master_sig: string;
} }
export interface ExchangeWireInfo { export interface ExchangeWireInfo {