diff options
Diffstat (limited to 'packages/taler-wallet-core/src/operations/backup')
3 files changed, 100 insertions, 75 deletions
diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts b/packages/taler-wallet-core/src/operations/backup/export.ts index 70d249ab8..fa0af1b07 100644 --- a/packages/taler-wallet-core/src/operations/backup/export.ts +++ b/packages/taler-wallet-core/src/operations/backup/export.ts @@ -47,6 +47,7 @@ import { BackupProposalStatus, BackupRefreshOldCoin, BackupRefreshSession, + BackupExchangeDetails, } from "@gnu-taler/taler-util"; import { InternalWalletState } from "../state"; import { @@ -65,6 +66,7 @@ import { } from "../../db.js"; import { encodeCrock, stringToBytes, getRandomBytes } from "../../index.js"; import { canonicalizeBaseUrl, canonicalJson } from "@gnu-taler/taler-util"; +import { getExchangeDetails } from "../exchanges.js"; export async function exportBackup( ws: InternalWalletState, @@ -74,6 +76,7 @@ export async function exportBackup( [ Stores.config, Stores.exchanges, + Stores.exchangeDetails, Stores.coins, Stores.denominations, Stores.purchases, @@ -88,6 +91,7 @@ export async function exportBackup( async (tx) => { const bs = await getWalletBackupState(ws, tx); + const backupExchangeDetails: BackupExchangeDetails[] = []; const backupExchanges: BackupExchange[] = []; const backupCoinsByDenom: { [dph: string]: BackupCoin[] } = {}; const backupDenominationsByExchange: { @@ -254,21 +258,22 @@ export async function exportBackup( }); }); - await tx.iter(Stores.exchanges).forEach((ex) => { - // Only back up permanently added exchanges. - - if (!ex.details) { - return; - } - if (!ex.wireInfo) { - return; - } - if (!ex.addComplete) { - return; - } - if (!ex.permanent) { + await tx.iter(Stores.exchanges).forEachAsync(async (ex) => { + const dp = ex.detailsPointer; + if (!dp) { return; } + backupExchanges.push({ + base_url: ex.baseUrl, + currency: dp.currency, + master_public_key: dp.masterPublicKey, + update_clock: dp.updateClock, + }); + }); + + await tx.iter(Stores.exchangeDetails).forEachAsync(async (ex) => { + // Only back up permanently added exchanges. + const wi = ex.wireInfo; const wireFees: BackupExchangeWireFee[] = []; @@ -285,23 +290,23 @@ export async function exportBackup( } }); - backupExchanges.push({ - base_url: ex.baseUrl, - reserve_closing_delay: ex.details.reserveClosingDelay, + backupExchangeDetails.push({ + base_url: ex.exchangeBaseUrl, + reserve_closing_delay: ex.reserveClosingDelay, accounts: ex.wireInfo.accounts.map((x) => ({ payto_uri: x.payto_uri, master_sig: x.master_sig, })), - auditors: ex.details.auditors.map((x) => ({ + auditors: ex.auditors.map((x) => ({ auditor_pub: x.auditor_pub, auditor_url: x.auditor_url, denomination_keys: x.denomination_keys, })), - master_public_key: ex.details.masterPublicKey, - currency: ex.details.currency, - protocol_version: ex.details.protocolVersion, + master_public_key: ex.masterPublicKey, + currency: ex.currency, + protocol_version: ex.protocolVersion, wire_fees: wireFees, - signing_keys: ex.details.signingKeys.map((x) => ({ + signing_keys: ex.signingKeys.map((x) => ({ key: x.key, master_sig: x.master_sig, stamp_end: x.stamp_end, @@ -310,8 +315,9 @@ export async function exportBackup( })), tos_etag_accepted: ex.termsOfServiceAcceptedEtag, tos_etag_last: ex.termsOfServiceLastEtag, - denominations: backupDenominationsByExchange[ex.baseUrl] ?? [], - reserves: backupReservesByExchange[ex.baseUrl] ?? [], + denominations: + backupDenominationsByExchange[ex.exchangeBaseUrl] ?? [], + reserves: backupReservesByExchange[ex.exchangeBaseUrl] ?? [], }); }); @@ -451,6 +457,7 @@ export async function exportBackup( schema_id: "gnu-taler-wallet-backup-content", schema_version: 1, exchanges: backupExchanges, + exchange_details: backupExchangeDetails, wallet_root_pub: bs.walletRootPub, backup_providers: backupBackupProviders, current_device_id: bs.deviceId, diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index 1bbba6e26..f0a944a22 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -32,7 +32,6 @@ import { Stores, WalletContractData, DenomSelectionState, - ExchangeWireInfo, ExchangeUpdateStatus, DenominationStatus, CoinSource, @@ -46,6 +45,7 @@ import { RefundState, AbortStatus, RefreshSessionRecord, + WireInfo, } from "../../db.js"; import { TransactionHandle } from "../../index.js"; import { PayCoinSelection } from "../../util/coinSelection"; @@ -56,6 +56,7 @@ import { initRetryInfo } from "../../util/retries"; import { InternalWalletState } from "../state"; import { provideBackupState } from "./state"; import { makeEventId, TombstoneTag } from "../transactions.js"; +import { getExchangeDetails } from "../exchanges.js"; const logger = new Logger("operations/backup/import.ts"); @@ -102,13 +103,13 @@ async function recoverPayCoinSelection( totalDepositFees = Amounts.add(totalDepositFees, denom.feeDeposit).amount; if (!coveredExchanges.has(coinRecord.exchangeBaseUrl)) { - const exchange = await tx.get( - Stores.exchanges, + const exchangeDetails = await getExchangeDetails( + tx, coinRecord.exchangeBaseUrl, ); - checkBackupInvariant(!!exchange); + checkBackupInvariant(!!exchangeDetails); let wireFee: AmountJson | undefined; - const feesForType = exchange.wireInfo?.feesForType; + const feesForType = exchangeDetails.wireInfo.feesForType; checkBackupInvariant(!!feesForType); for (const fee of feesForType[contractData.wireMethod] || []) { if ( @@ -218,6 +219,7 @@ export async function importBackup( [ Stores.config, Stores.exchanges, + Stores.exchangeDetails, Stores.coins, Stores.denominations, Stores.purchases, @@ -245,21 +247,46 @@ export async function importBackup( const tombstoneSet = new Set(backupBlob.tombstones); + // FIXME: Validate that the "details pointer" is correct + for (const backupExchange of backupBlob.exchanges) { const existingExchange = await tx.get( Stores.exchanges, backupExchange.base_url, ); + if (existingExchange) { + continue; + } + await tx.put(Stores.exchanges, { + baseUrl: backupExchange.base_url, + detailsPointer: { + currency: backupExchange.currency, + masterPublicKey: backupExchange.master_public_key, + updateClock: backupExchange.update_clock, + }, + permanent: true, + retryInfo: initRetryInfo(false), + updateStarted: { t_ms: "never" }, + updateStatus: ExchangeUpdateStatus.Finished, + }); + } + + for (const backupExchangeDetails of backupBlob.exchange_details) { + const existingExchangeDetails = await tx.get(Stores.exchangeDetails, [ + backupExchangeDetails.base_url, + backupExchangeDetails.currency, + backupExchangeDetails.master_public_key, + ]); - if (!existingExchange) { - const wireInfo: ExchangeWireInfo = { - accounts: backupExchange.accounts.map((x) => ({ + if (!existingExchangeDetails) { + const wireInfo: WireInfo = { + accounts: backupExchangeDetails.accounts.map((x) => ({ master_sig: x.master_sig, payto_uri: x.payto_uri, })), feesForType: {}, }; - for (const fee of backupExchange.wire_fees) { + for (const fee of backupExchangeDetails.wire_fees) { const w = (wireInfo.feesForType[fee.wire_type] ??= []); w.push({ closingFee: Amounts.parseOrThrow(fee.closing_fee), @@ -269,48 +296,39 @@ export async function importBackup( wireFee: Amounts.parseOrThrow(fee.wire_fee), }); } - await tx.put(Stores.exchanges, { - addComplete: true, - baseUrl: backupExchange.base_url, - builtIn: false, - updateReason: undefined, - permanent: true, - retryInfo: initRetryInfo(), - termsOfServiceAcceptedEtag: backupExchange.tos_etag_accepted, + await tx.put(Stores.exchangeDetails, { + exchangeBaseUrl: backupExchangeDetails.base_url, + termsOfServiceAcceptedEtag: backupExchangeDetails.tos_etag_accepted, termsOfServiceText: undefined, - termsOfServiceLastEtag: backupExchange.tos_etag_last, - updateStarted: getTimestampNow(), - updateStatus: ExchangeUpdateStatus.FetchKeys, + termsOfServiceLastEtag: backupExchangeDetails.tos_etag_last, wireInfo, - details: { - currency: backupExchange.currency, - reserveClosingDelay: backupExchange.reserve_closing_delay, - auditors: backupExchange.auditors.map((x) => ({ - auditor_pub: x.auditor_pub, - auditor_url: x.auditor_url, - denomination_keys: x.denomination_keys, - })), - lastUpdateTime: { t_ms: "never" }, - masterPublicKey: backupExchange.master_public_key, - nextUpdateTime: { t_ms: "never" }, - protocolVersion: backupExchange.protocol_version, - signingKeys: backupExchange.signing_keys.map((x) => ({ - key: x.key, - master_sig: x.master_sig, - stamp_end: x.stamp_end, - stamp_expire: x.stamp_expire, - stamp_start: x.stamp_start, - })), - }, + currency: backupExchangeDetails.currency, + auditors: backupExchangeDetails.auditors.map((x) => ({ + auditor_pub: x.auditor_pub, + auditor_url: x.auditor_url, + denomination_keys: x.denomination_keys, + })), + lastUpdateTime: { t_ms: "never" }, + masterPublicKey: backupExchangeDetails.master_public_key, + nextUpdateTime: { t_ms: "never" }, + protocolVersion: backupExchangeDetails.protocol_version, + reserveClosingDelay: backupExchangeDetails.reserve_closing_delay, + signingKeys: backupExchangeDetails.signing_keys.map((x) => ({ + key: x.key, + master_sig: x.master_sig, + stamp_end: x.stamp_end, + stamp_expire: x.stamp_expire, + stamp_start: x.stamp_start, + })), }); } - for (const backupDenomination of backupExchange.denominations) { + for (const backupDenomination of backupExchangeDetails.denominations) { const denomPubHash = cryptoComp.denomPubToHash[backupDenomination.denom_pub]; checkLogicInvariant(!!denomPubHash); const existingDenom = await tx.get(Stores.denominations, [ - backupExchange.base_url, + backupExchangeDetails.base_url, denomPubHash, ]); if (!existingDenom) { @@ -321,7 +339,7 @@ export async function importBackup( await tx.put(Stores.denominations, { denomPub: backupDenomination.denom_pub, denomPubHash: denomPubHash, - exchangeBaseUrl: backupExchange.base_url, + exchangeBaseUrl: backupExchangeDetails.base_url, feeDeposit: Amounts.parseOrThrow(backupDenomination.fee_deposit), feeRefresh: Amounts.parseOrThrow(backupDenomination.fee_refresh), feeRefund: Amounts.parseOrThrow(backupDenomination.fee_refund), @@ -378,7 +396,7 @@ export async function importBackup( denomSig: backupCoin.denom_sig, coinPub: compCoin.coinPub, suspended: false, - exchangeBaseUrl: backupExchange.base_url, + exchangeBaseUrl: backupExchangeDetails.base_url, denomPub: backupDenomination.denom_pub, denomPubHash, status: backupCoin.fresh @@ -390,7 +408,7 @@ export async function importBackup( } } - for (const backupReserve of backupExchange.reserves) { + for (const backupReserve of backupExchangeDetails.reserves) { const reservePub = cryptoComp.reservePrivToPub[backupReserve.reserve_priv]; const ts = makeEventId(TombstoneTag.DeleteReserve, reservePub); @@ -414,7 +432,7 @@ export async function importBackup( await tx.put(Stores.reserves, { currency: instructedAmount.currency, instructedAmount, - exchangeBaseUrl: backupExchange.base_url, + exchangeBaseUrl: backupExchangeDetails.base_url, reservePub, reservePriv: backupReserve.reserve_priv, requestedQuery: false, @@ -436,7 +454,7 @@ export async function importBackup( reserveStatus: ReserveRecordStatus.QUERYING_STATUS, initialDenomSel: await getDenomSelStateFromBackup( tx, - backupExchange.base_url, + backupExchangeDetails.base_url, backupReserve.initial_selected_denoms, ), }); @@ -457,10 +475,10 @@ export async function importBackup( await tx.put(Stores.withdrawalGroups, { denomsSel: await getDenomSelStateFromBackup( tx, - backupExchange.base_url, + backupExchangeDetails.base_url, backupWg.selected_denoms, ), - exchangeBaseUrl: backupExchange.base_url, + exchangeBaseUrl: backupExchangeDetails.base_url, lastError: undefined, rawWithdrawalAmount: Amounts.parseOrThrow( backupWg.raw_withdrawal_amount, diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts b/packages/taler-wallet-core/src/operations/backup/index.ts index 110e76596..2314c730d 100644 --- a/packages/taler-wallet-core/src/operations/backup/index.ts +++ b/packages/taler-wallet-core/src/operations/backup/index.ts @@ -155,8 +155,8 @@ async function computeBackupCryptoData( proposalNoncePrivToPub: {}, reservePrivToPub: {}, }; - for (const backupExchange of backupContent.exchanges) { - for (const backupDenom of backupExchange.denominations) { + for (const backupExchangeDetails of backupContent.exchange_details) { + for (const backupDenom of backupExchangeDetails.denominations) { for (const backupCoin of backupDenom.coins) { const coinPub = encodeCrock( eddsaGetPublic(decodeCrock(backupCoin.coin_priv)), @@ -175,7 +175,7 @@ async function computeBackupCryptoData( hash(decodeCrock(backupDenom.denom_pub)), ); } - for (const backupReserve of backupExchange.reserves) { + for (const backupReserve of backupExchangeDetails.reserves) { cryptoData.reservePrivToPub[backupReserve.reserve_priv] = encodeCrock( eddsaGetPublic(decodeCrock(backupReserve.reserve_priv)), ); |