From debc2254fdf1cf748a846e429e32c2e92d557080 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 12 May 2021 13:34:49 +0200 Subject: towards backup based on add/remove set instead of clocks --- packages/taler-util/src/backupTypes.ts | 109 +++++---------------- packages/taler-wallet-core/src/db.ts | 12 +-- .../src/operations/backup/export.ts | 7 +- .../src/operations/backup/import.ts | 7 +- .../src/operations/backup/index.ts | 4 - packages/taler-wallet-core/src/operations/pay.ts | 1 + .../taler-wallet-core/src/operations/reserves.ts | 1 + packages/taler-wallet-core/src/operations/tip.ts | 2 + 8 files changed, 47 insertions(+), 96 deletions(-) (limited to 'packages') diff --git a/packages/taler-util/src/backupTypes.ts b/packages/taler-util/src/backupTypes.ts index fc6aaaad5..13ff75190 100644 --- a/packages/taler-util/src/backupTypes.ts +++ b/packages/taler-util/src/backupTypes.ts @@ -70,19 +70,13 @@ type BackupAmountString = string; */ type DeviceIdString = string; -/** - * Lamport clock timestamp. - */ -export interface ClockStamp { - deviceId: string; - value: number; -} - /** * Contract terms JSON. */ type RawContractTerms = any; +type OperationUid = string; + /** * Content of the backup. * @@ -115,16 +109,6 @@ export interface WalletBackupContentV1 { */ 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 }; - /** * Timestamp of the backup. * @@ -182,21 +166,6 @@ export interface WalletBackupContentV1 { */ recoup_groups: BackupRecoupGroup[]; - /** - * Tombstones for deleting purchases. - */ - purchase_tombstones: { - /** - * Clock when the purchase was deleted - */ - clock_deleted: ClockStamp; - - /** - * Proposal ID identifying the purchase. - */ - proposal_id: string; - }[]; - /** * Trusted auditors, either for official (3 letter) or local (4-12 letter) * currencies. @@ -224,6 +193,17 @@ export interface WalletBackupContentV1 { * Permanent error reports. */ error_reports: BackupErrorReport[]; + + /** + * Deletion tombstones. Sorted by (type, id) + * in ascending order. + */ + tombstones: Tombstone[]; +} + +export interface Tombstone { + type: string; + id: string; } /** @@ -256,17 +236,10 @@ export interface BackupTrustAuditor { 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?: ClockStamp; - - /** - * Clock for when the auditor trust has been removed. + * UIDs for the operation of adding this auditor + * as a trusted auditor. */ - clock_removed?: ClockStamp; + uids: OperationUid; } /** @@ -289,17 +262,10 @@ export interface BackupTrustExchange { 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. + * UIDs for the operation of adding this exchange + * as trusted. */ - clock_added?: ClockStamp; - - /** - * Clock for when the exchange trust has been removed. - */ - clock_removed?: ClockStamp; + uids: OperationUid; } export class BackupBackupProviderTerms { @@ -483,11 +449,6 @@ export interface BackupCoin { * be refreshed in most situations. */ fresh: boolean; - - /** - * Clock for the last update to current_amount/fresh. - */ - last_clock?: ClockStamp; } /** @@ -521,7 +482,6 @@ export interface BackupTip { timestamp_created: Timestamp; timestamp_finished?: Timestamp; - finish_clock?: ClockStamp; finish_is_failure?: boolean; /** @@ -549,7 +509,11 @@ export interface BackupTip { */ selected_denoms: BackupDenomSel; - selected_denoms_clock?: ClockStamp; + /** + * UID for the denomination selection. + * Used to disambiguate when merging. + */ + selected_denoms_uid: OperationUid; } /** @@ -640,7 +604,6 @@ export interface BackupRefreshGroup { timestamp_created: Timestamp; timestamp_finish?: Timestamp; - finish_clock?: ClockStamp; finish_is_failure?: boolean; } @@ -664,7 +627,6 @@ export interface BackupWithdrawalGroup { timestamp_created: Timestamp; timestamp_finish?: Timestamp; - finish_clock?: ClockStamp; finish_is_failure?: boolean; /** @@ -682,7 +644,7 @@ export interface BackupWithdrawalGroup { */ selected_denoms: BackupDenomSel; - selected_denoms_clock?: ClockStamp; + selected_denoms_id: OperationUid; } export enum BackupRefundState { @@ -731,8 +693,6 @@ export interface BackupRefundItemCommon { * accurately. */ total_refresh_cost_bound: BackupAmountString; - - last_clock?: ClockStamp; } /** @@ -795,9 +755,9 @@ export interface BackupPurchase { }[]; /** - * Clock when the pay coin selection was made/updated. + * Unique ID to disambiguate pay coin selection on merge. */ - pay_coins_clock?: ClockStamp; + pay_coins_uid: OperationUid; /** * Total cost initially shown to the user. @@ -841,11 +801,6 @@ export interface BackupPurchase { */ defunct?: boolean; - /** - * Clock for last update to defunct status. - */ - defunct_clock?: ClockStamp; - /** * Abort status of the payment. */ @@ -1042,7 +997,6 @@ export interface BackupReserve { withdrawal_groups: BackupWithdrawalGroup[]; defective?: boolean; - defective_clock?: ClockStamp; } /** @@ -1196,17 +1150,10 @@ export interface BackupExchange { */ tos_etag_accepted: string | undefined; - /** - * Clock value of the last update. - */ - last_clock?: ClockStamp; - /** * Should this exchange be considered defective? */ defective?: boolean; - - defective_clock?: ClockStamp; } export enum BackupProposalStatus { @@ -1284,8 +1231,6 @@ export interface BackupProposal { */ proposal_status: BackupProposalStatus; - proposal_status_clock?: ClockStamp; - /** * Proposal that this one got "redirected" to as part of * the repurchase detection. diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index eb0de5646..22cbd16cc 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -978,6 +978,8 @@ export interface TipRecord { */ denomsSel: DenomSelectionState; + denomSelUid: string; + /** * Tip ID chosen by the wallet. */ @@ -1310,6 +1312,8 @@ export interface PurchaseRecord { payCoinSelection: PayCoinSelection; + payCoinSelectionUid: string; + /** * Pending removals from pay coin selection. * @@ -1460,6 +1464,8 @@ export interface WithdrawalGroupRecord { denomsSel: DenomSelectionState; + denomSelUid: string; + /** * Retry info, always present even on completed operations so that indexing works. */ @@ -1564,12 +1570,6 @@ export interface BackupProviderRecord { */ lastBackupHash?: string; - /** - * Clock of the last backup that we already - * merged. - */ - lastBackupClock?: number; - lastBackupTimestamp?: Timestamp; /** diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts b/packages/taler-wallet-core/src/operations/backup/export.ts index 07c7b9ece..4ccaf8f42 100644 --- a/packages/taler-wallet-core/src/operations/backup/export.ts +++ b/packages/taler-wallet-core/src/operations/backup/export.ts @@ -118,6 +118,7 @@ export async function exportBackup( timestamp_finish: wg.timestampFinish, withdrawal_group_id: wg.withdrawalGroupId, secret_seed: wg.secretSeed, + selected_denoms_id: wg.denomSelUid, }); }); @@ -160,6 +161,7 @@ export async function exportBackup( timestamp_created: tip.createdTimestamp, timestamp_expiration: tip.tipExpiration, tip_amount_raw: Amounts.stringify(tip.tipAmountRaw), + selected_denoms_uid: tip.denomSelUid, }); }); @@ -363,6 +365,7 @@ export async function exportBackup( nonce_priv: purch.noncePriv, merchant_sig: purch.download.contractData.merchantSig, total_pay_cost: Amounts.stringify(purch.totalPayCost), + pay_coins_uid: purch.payCoinSelectionUid, }); }); @@ -446,13 +449,11 @@ export async function exportBackup( const backupBlob: WalletBackupContentV1 = { schema_id: "gnu-taler-wallet-backup-content", schema_version: 1, - clocks: bs.clocks, exchanges: backupExchanges, wallet_root_pub: bs.walletRootPub, backup_providers: backupBackupProviders, current_device_id: bs.deviceId, proposals: backupProposals, - purchase_tombstones: [], purchases: backupPurchases, recoup_groups: backupRecoupGroups, refresh_groups: backupRefreshGroups, @@ -462,13 +463,13 @@ export async function exportBackup( trusted_exchanges: {}, intern_table: {}, error_reports: [], + tombstones: [], }; // If the backup changed, we increment our clock. let h = encodeCrock(hash(stringToBytes(canonicalJson(backupBlob)))); if (h != bs.lastBackupPlainHash) { - backupBlob.clocks[bs.deviceId] = ++bs.clocks[bs.deviceId]; bs.lastBackupPlainHash = encodeCrock( hash(stringToBytes(canonicalJson(backupBlob))), ); diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index e0ae379ab..931e6f92b 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -302,7 +302,9 @@ export async function importBackup( denomPubHash, ]); if (!existingDenom) { - logger.info(`importing backup denomination: ${j2s(backupDenomination)}`); + logger.info( + `importing backup denomination: ${j2s(backupDenomination)}`, + ); await tx.put(Stores.denominations, { denomPub: backupDenomination.denom_pub, @@ -446,6 +448,7 @@ export async function importBackup( timestampStart: backupWg.timestamp_created, timestampFinish: backupWg.timestamp_finish, withdrawalGroupId: backupWg.withdrawal_group_id, + denomSelUid: backupWg.selected_denoms_id, }); } } @@ -695,6 +698,7 @@ export async function importBackup( coinDepositPermissions: undefined, totalPayCost: Amounts.parseOrThrow(backupPurchase.total_pay_cost), refunds, + payCoinSelectionUid: backupPurchase.pay_coins_uid, }); } } @@ -801,6 +805,7 @@ export async function importBackup( tipAmountRaw: Amounts.parseOrThrow(backupTip.tip_amount_raw), tipExpiration: backupTip.timestamp_expiration, walletTipId: backupTip.wallet_tip_id, + denomSelUid: backupTip.selected_denoms_uid, }); } } diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts b/packages/taler-wallet-core/src/operations/backup/index.ts index 49129d7de..b4c1a6c92 100644 --- a/packages/taler-wallet-core/src/operations/backup/index.ts +++ b/packages/taler-wallet-core/src/operations/backup/index.ts @@ -349,7 +349,6 @@ async function runBackupCycleForProvider( } prov.lastBackupHash = encodeCrock(currentBackupHash); prov.lastBackupTimestamp = getTimestampNow(); - prov.lastBackupClock = backupJson.clocks[backupJson.current_device_id]; prov.lastError = undefined; await tx.put(Stores.backupProviders, prov); }, @@ -372,7 +371,6 @@ async function runBackupCycleForProvider( return; } prov.lastBackupHash = encodeCrock(hash(backupEnc)); - prov.lastBackupClock = blob.clocks[blob.current_device_id]; prov.lastBackupTimestamp = getTimestampNow(); prov.lastError = undefined; await tx.put(Stores.backupProviders, prov); @@ -624,7 +622,6 @@ export async function getBackupInfo( for (const x of providerRecords) { providers.push({ active: x.active, - lastRemoteClock: x.lastBackupClock, syncProviderBaseUrl: x.baseUrl, lastBackupTimestamp: x.lastBackupTimestamp, paymentProposalIds: x.paymentProposalIds, @@ -696,7 +693,6 @@ async function backupRecoveryTheirs( for (const prov of providers) { prov.lastBackupTimestamp = undefined; prov.lastBackupHash = undefined; - prov.lastBackupClock = undefined; await tx.put(Stores.backupProviders, prov); } }, diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts index 2e8228e6e..1ed8d72b9 100644 --- a/packages/taler-wallet-core/src/operations/pay.ts +++ b/packages/taler-wallet-core/src/operations/pay.ts @@ -406,6 +406,7 @@ async function recordConfirmPay( download: d, lastSessionId: sessionId, payCoinSelection: coinSelection, + payCoinSelectionUid: encodeCrock(getRandomBytes(32)), totalPayCost: payCostInfo, coinDepositPermissions, timestampAccept: getTimestampNow(), diff --git a/packages/taler-wallet-core/src/operations/reserves.ts b/packages/taler-wallet-core/src/operations/reserves.ts index 984ce5a6b..855ee82ee 100644 --- a/packages/taler-wallet-core/src/operations/reserves.ts +++ b/packages/taler-wallet-core/src/operations/reserves.ts @@ -633,6 +633,7 @@ async function updateReserve( lastError: undefined, denomsSel: denomSelectionInfoToState(denomSelInfo), secretSeed: encodeCrock(getRandomBytes(64)), + denomSelUid: encodeCrock(getRandomBytes(32)), }; newReserve.lastError = undefined; diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts index cc5274647..6de116098 100644 --- a/packages/taler-wallet-core/src/operations/tip.ts +++ b/packages/taler-wallet-core/src/operations/tip.ts @@ -107,6 +107,7 @@ export async function prepareTip( const selectedDenoms = selectWithdrawalDenominations(amount, denoms); const secretSeed = encodeCrock(getRandomBytes(64)); + const denomSelUid = encodeCrock(getRandomBytes(32)); tipRecord = { walletTipId: walletTipId, @@ -127,6 +128,7 @@ export async function prepareTip( denomsSel: denomSelectionInfoToState(selectedDenoms), pickedUpTimestamp: undefined, secretSeed, + denomSelUid, }; await ws.db.put(Stores.tips, tipRecord); } -- cgit v1.2.3