restore denom selection on import

This commit is contained in:
Florian Dold 2021-01-07 15:01:23 +01:00
parent 3773a4cdff
commit e319e99ef9
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
7 changed files with 79 additions and 16 deletions

View File

@ -36,9 +36,6 @@ import {
NodeThreadCryptoWorkerFactory, NodeThreadCryptoWorkerFactory,
CryptoApi, CryptoApi,
rsaBlind, rsaBlind,
encodeCrock,
rsaUnblind,
rsaVerify,
} from "taler-wallet-core"; } from "taler-wallet-core";
import * as clk from "./clk"; import * as clk from "./clk";
import { deepStrictEqual } from "assert"; import { deepStrictEqual } from "assert";
@ -401,6 +398,25 @@ exchangesCli
}); });
}); });
const backupCli = walletCli.subcommand("backupArgs", "backup", {
help: "Subcommands for backups",
});
backupCli.subcommand("exportPlain", "export-plain").action(async (args) => {
await withWallet(args, async (wallet) => {
const backup = await wallet.exportBackupPlain();
console.log(JSON.stringify(backup, undefined, 2));
});
});
backupCli.subcommand("importPlain", "import-plain").action(async (args) => {
await withWallet(args, async (wallet) => {
const data = JSON.parse(await read(process.stdin));
await wallet.importBackupPlain(data);
});
});
const advancedCli = walletCli.subcommand("advancedArgs", "advanced", { const advancedCli = walletCli.subcommand("advancedArgs", "advanced", {
help: help:
"Subcommands for advanced operations (only use if you know what you're doing!).", "Subcommands for advanced operations (only use if you know what you're doing!).",

View File

@ -588,10 +588,6 @@ export async function exportBackup(
); );
} }
export interface BackupRequest {
backupBlob: any;
}
export async function encryptBackup( export async function encryptBackup(
config: WalletBackupConfState, config: WalletBackupConfState,
blob: WalletBackupContentV1, blob: WalletBackupContentV1,
@ -650,7 +646,7 @@ async function computeBackupCryptoData(
cryptoData.coinPrivToCompletedCoin[backupCoin.coin_priv] = { cryptoData.coinPrivToCompletedCoin[backupCoin.coin_priv] = {
coinEvHash: encodeCrock(hash(blindedCoin)), coinEvHash: encodeCrock(hash(blindedCoin)),
coinPub, coinPub,
} };
} }
cryptoData.denomPubToHash[backupDenom.denom_pub] = encodeCrock( cryptoData.denomPubToHash[backupDenom.denom_pub] = encodeCrock(
hash(decodeCrock(backupDenom.denom_pub)), hash(decodeCrock(backupDenom.denom_pub)),
@ -777,16 +773,35 @@ async function recoverPayCoinSelection(
}; };
} }
function getDenomSelStateFromBackup( async function getDenomSelStateFromBackup(
tx: TransactionHandle<typeof Stores.denominations>, tx: TransactionHandle<typeof Stores.denominations>,
exchangeBaseUrl: string,
sel: BackupDenomSel, sel: BackupDenomSel,
): Promise<DenomSelectionState> { ): Promise<DenomSelectionState> {
throw Error("not implemented"); const d0 = await tx.get(Stores.denominations, [exchangeBaseUrl, sel[0].denom_pub_hash]);
checkBackupInvariant(!!d0);
const selectedDenoms: {
denomPubHash: string;
count: number;
}[] = [];
let totalCoinValue = Amounts.getZero(d0.value.currency);
let totalWithdrawCost = Amounts.getZero(d0.value.currency);
for (const s of sel) {
const d = await tx.get(Stores.denominations, [exchangeBaseUrl, s.denom_pub_hash]);
checkBackupInvariant(!!d);
totalCoinValue = Amounts.add(totalCoinValue, d.value).amount;
totalWithdrawCost = Amounts.add(totalWithdrawCost, d.value, d.feeWithdraw).amount;
}
return {
selectedDenoms,
totalCoinValue,
totalWithdrawCost,
}
} }
export async function importBackup( export async function importBackup(
ws: InternalWalletState, ws: InternalWalletState,
backupRequest: BackupRequest, backupBlobArg: any,
cryptoComp: BackupCryptoPrecomputedData, cryptoComp: BackupCryptoPrecomputedData,
): Promise<void> { ): Promise<void> {
await provideBackupState(ws); await provideBackupState(ws);
@ -807,7 +822,7 @@ export async function importBackup(
], ],
async (tx) => { async (tx) => {
// FIXME: validate schema! // FIXME: validate schema!
const backupBlob = backupRequest.backupBlob as WalletBackupContentV1; const backupBlob = backupBlobArg as WalletBackupContentV1;
// FIXME: validate version // FIXME: validate version
@ -993,6 +1008,7 @@ export async function importBackup(
reserveStatus: ReserveRecordStatus.QUERYING_STATUS, reserveStatus: ReserveRecordStatus.QUERYING_STATUS,
initialDenomSel: await getDenomSelStateFromBackup( initialDenomSel: await getDenomSelStateFromBackup(
tx, tx,
backupExchange.base_url,
backupReserve.initial_selected_denoms, backupReserve.initial_selected_denoms,
), ),
}); });
@ -1006,6 +1022,7 @@ export async function importBackup(
await tx.put(Stores.withdrawalGroups, { await tx.put(Stores.withdrawalGroups, {
denomsSel: await getDenomSelStateFromBackup( denomsSel: await getDenomSelStateFromBackup(
tx, tx,
backupExchange.base_url,
backupWg.selected_denoms, backupWg.selected_denoms,
), ),
exchangeBaseUrl: backupExchange.base_url, exchangeBaseUrl: backupExchange.base_url,
@ -1300,9 +1317,12 @@ export async function importBackup(
| undefined | undefined
)[] = []; )[] = [];
for (const oldCoin of backupRefreshGroup.old_coins) { for (const oldCoin of backupRefreshGroup.old_coins) {
const c = await tx.get(Stores.coins, oldCoin.coin_pub);
checkBackupInvariant(!!c);
if (oldCoin.refresh_session) { if (oldCoin.refresh_session) {
const denomSel = await getDenomSelStateFromBackup( const denomSel = await getDenomSelStateFromBackup(
tx, tx,
c.exchangeBaseUrl,
oldCoin.refresh_session.new_denoms, oldCoin.refresh_session.new_denoms,
); );
refreshSessionPerCoin.push({ refreshSessionPerCoin.push({
@ -1346,6 +1366,7 @@ export async function importBackup(
if (!existingTip) { if (!existingTip) {
const denomsSel = await getDenomSelStateFromBackup( const denomsSel = await getDenomSelStateFromBackup(
tx, tx,
backupTip.exchange_base_url,
backupTip.selected_denoms, backupTip.selected_denoms,
); );
await tx.put(Stores.tips, { await tx.put(Stores.tips, {
@ -1530,6 +1551,18 @@ export interface BackupInfo {
providers: ProviderInfo[]; providers: ProviderInfo[];
} }
export async function importBackupPlain(
ws: InternalWalletState,
blob: any,
): Promise<void> {
// FIXME: parse
const backup: WalletBackupContentV1 = blob;
const cryptoData = await computeBackupCryptoData(ws.cryptoApi, backup);
await importBackup(ws, blob, cryptoData);
}
/** /**
* Get information about the current state of wallet backups. * Get information about the current state of wallet backups.
*/ */

View File

@ -55,6 +55,10 @@ export class InternalWalletState {
private resourceLocks: Set<string> = new Set(); private resourceLocks: Set<string> = new Set();
constructor( constructor(
// FIXME: Make this a getter and make
// the actual value nullable.
// Check if we are in a DB migration / garbage collection
// and throw an error in that case.
public db: Database, public db: Database,
public http: HttpRequestLibrary, public http: HttpRequestLibrary,
cryptoWorkerFactory: CryptoWorkerFactory, cryptoWorkerFactory: CryptoWorkerFactory,

View File

@ -39,6 +39,7 @@
* 10. Re-denominated payments/refreshes are not shown properly in the total * 10. Re-denominated payments/refreshes are not shown properly in the total
* payment cost. * payment cost.
* 11. Failed refunds do not have any information about why they failed. * 11. Failed refunds do not have any information about why they failed.
* => This should go into the general "error reports"
* *
* Questions: * Questions:
* 1. What happens when two backups are merged that have * 1. What happens when two backups are merged that have

View File

@ -161,6 +161,7 @@ import {
codecForAddBackupProviderRequest, codecForAddBackupProviderRequest,
runBackupCycle, runBackupCycle,
exportBackup, exportBackup,
importBackupPlain,
} from "./operations/backup"; } from "./operations/backup";
const builtinCurrencies: CurrencyRecord[] = [ const builtinCurrencies: CurrencyRecord[] = [
@ -933,6 +934,14 @@ export class Wallet {
return testPay(this.ws.http, this, args); return testPay(this.ws.http, this, args);
} }
async exportBackupPlain() {
return exportBackup(this.ws);
}
async importBackupPlain(backup: any) {
return importBackupPlain(this.ws, backup);
}
/** /**
* Implementation of the "wallet-core" API. * Implementation of the "wallet-core" API.
*/ */

View File

@ -4,8 +4,8 @@
"name": "GNU Taler Wallet (git)", "name": "GNU Taler Wallet (git)",
"description": "Privacy preserving and transparent payments", "description": "Privacy preserving and transparent payments",
"author": "GNU Taler Developers", "author": "GNU Taler Developers",
"version": "0.8.0.5", "version": "0.8.0.6",
"version_name": "0.8.1-dev.1", "version_name": "0.8.1-dev.2",
"minimum_chrome_version": "51", "minimum_chrome_version": "51",
"minimum_opera_version": "36", "minimum_opera_version": "36",

View File

@ -1,6 +1,6 @@
{ {
"name": "taler-wallet-webextension", "name": "taler-wallet-webextension",
"version": "0.8.1-dev.1", "version": "0.8.1-dev.2",
"description": "GNU Taler Wallet browser extension", "description": "GNU Taler Wallet browser extension",
"main": "./build/index.js", "main": "./build/index.js",
"types": "./build/index.d.ts", "types": "./build/index.d.ts",