aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/operations/backup/export.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2023-08-30 18:01:18 +0200
committerFlorian Dold <florian@dold.me>2023-08-30 18:01:18 +0200
commita713d90c3c564408309d92223d383ecc9225924f (patch)
tree18142a4ce3d98df6e8a4945dbca23c47715eee6a /packages/taler-wallet-core/src/operations/backup/export.ts
parent0a4782a0da631aba31dc0ecef7427df2467cc3e6 (diff)
downloadwallet-core-a713d90c3c564408309d92223d383ecc9225924f.tar.xz
wallet-core: remove old sync code, add stored backups skeleton
Diffstat (limited to 'packages/taler-wallet-core/src/operations/backup/export.ts')
-rw-r--r--packages/taler-wallet-core/src/operations/backup/export.ts586
1 files changed, 0 insertions, 586 deletions
diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts b/packages/taler-wallet-core/src/operations/backup/export.ts
deleted file mode 100644
index c9446a05f..000000000
--- a/packages/taler-wallet-core/src/operations/backup/export.ts
+++ /dev/null
@@ -1,586 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2020 Taler Systems SA
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Implementation of wallet backups (export/import/upload) and sync
- * server management.
- *
- * @author Florian Dold <dold@taler.net>
- */
-
-/**
- * Imports.
- */
-import {
- AbsoluteTime,
- Amounts,
- BackupBackupProvider,
- BackupBackupProviderTerms,
- BackupCoin,
- BackupCoinSource,
- BackupCoinSourceType,
- BackupDenomination,
- BackupExchange,
- BackupExchangeDetails,
- BackupExchangeSignKey,
- BackupExchangeWireFee,
- BackupOperationStatus,
- BackupPayInfo,
- BackupProposalStatus,
- BackupPurchase,
- BackupRecoupGroup,
- BackupRefreshGroup,
- BackupRefreshOldCoin,
- BackupRefreshSession,
- BackupRefundItem,
- BackupRefundState,
- BackupTip,
- BackupWgInfo,
- BackupWgType,
- BackupWithdrawalGroup,
- BACKUP_VERSION_MAJOR,
- BACKUP_VERSION_MINOR,
- canonicalizeBaseUrl,
- canonicalJson,
- CoinStatus,
- encodeCrock,
- getRandomBytes,
- hash,
- Logger,
- stringToBytes,
- WalletBackupContentV1,
- TalerPreciseTimestamp,
-} from "@gnu-taler/taler-util";
-import {
- CoinSourceType,
- ConfigRecordKey,
- DenominationRecord,
- PurchaseStatus,
- RefreshCoinStatus,
- WithdrawalGroupStatus,
- WithdrawalRecordType,
-} from "../../db.js";
-import { InternalWalletState } from "../../internal-wallet-state.js";
-import { assertUnreachable } from "../../util/assertUnreachable.js";
-import { checkDbInvariant } from "../../util/invariants.js";
-import { getWalletBackupState, provideBackupState } from "./state.js";
-
-const logger = new Logger("backup/export.ts");
-
-export async function exportBackup(
- ws: InternalWalletState,
-): Promise<WalletBackupContentV1> {
- await provideBackupState(ws);
- return ws.db
- .mktx((x) => [
- x.config,
- x.exchanges,
- x.exchangeDetails,
- x.exchangeSignKeys,
- x.coins,
- x.contractTerms,
- x.denominations,
- x.purchases,
- x.refreshGroups,
- x.backupProviders,
- x.rewards,
- x.recoupGroups,
- x.withdrawalGroups,
- ])
- .runReadWrite(async (tx) => {
- const bs = await getWalletBackupState(ws, tx);
-
- const backupExchangeDetails: BackupExchangeDetails[] = [];
- const backupExchanges: BackupExchange[] = [];
- const backupCoinsByDenom: { [dph: string]: BackupCoin[] } = {};
- const backupDenominationsByExchange: {
- [url: string]: BackupDenomination[];
- } = {};
- const backupPurchases: BackupPurchase[] = [];
- const backupRefreshGroups: BackupRefreshGroup[] = [];
- const backupBackupProviders: BackupBackupProvider[] = [];
- const backupTips: BackupTip[] = [];
- const backupRecoupGroups: BackupRecoupGroup[] = [];
- const backupWithdrawalGroups: BackupWithdrawalGroup[] = [];
-
- await tx.withdrawalGroups.iter().forEachAsync(async (wg) => {
- let info: BackupWgInfo;
- switch (wg.wgInfo.withdrawalType) {
- case WithdrawalRecordType.BankIntegrated:
- info = {
- type: BackupWgType.BankIntegrated,
- exchange_payto_uri: wg.wgInfo.bankInfo.exchangePaytoUri,
- taler_withdraw_uri: wg.wgInfo.bankInfo.talerWithdrawUri,
- confirm_url: wg.wgInfo.bankInfo.confirmUrl,
- timestamp_bank_confirmed:
- wg.wgInfo.bankInfo.timestampBankConfirmed,
- timestamp_reserve_info_posted:
- wg.wgInfo.bankInfo.timestampReserveInfoPosted,
- };
- break;
- case WithdrawalRecordType.BankManual:
- info = {
- type: BackupWgType.BankManual,
- };
- break;
- case WithdrawalRecordType.PeerPullCredit:
- info = {
- type: BackupWgType.PeerPullCredit,
- contract_priv: wg.wgInfo.contractPriv,
- contract_terms: wg.wgInfo.contractTerms,
- };
- break;
- case WithdrawalRecordType.PeerPushCredit:
- info = {
- type: BackupWgType.PeerPushCredit,
- contract_terms: wg.wgInfo.contractTerms,
- };
- break;
- case WithdrawalRecordType.Recoup:
- info = {
- type: BackupWgType.Recoup,
- };
- break;
- default:
- assertUnreachable(wg.wgInfo);
- }
- backupWithdrawalGroups.push({
- raw_withdrawal_amount: Amounts.stringify(wg.rawWithdrawalAmount),
- info,
- timestamp_created: wg.timestampStart,
- timestamp_finish: wg.timestampFinish,
- withdrawal_group_id: wg.withdrawalGroupId,
- secret_seed: wg.secretSeed,
- exchange_base_url: wg.exchangeBaseUrl,
- instructed_amount: Amounts.stringify(wg.instructedAmount),
- effective_withdrawal_amount: Amounts.stringify(
- wg.effectiveWithdrawalAmount,
- ),
- reserve_priv: wg.reservePriv,
- restrict_age: wg.restrictAge,
- // FIXME: proper status conversion!
- operation_status:
- wg.status == WithdrawalGroupStatus.Finished
- ? BackupOperationStatus.Finished
- : BackupOperationStatus.Pending,
- selected_denoms_uid: wg.denomSelUid,
- selected_denoms: wg.denomsSel.selectedDenoms.map((x) => ({
- count: x.count,
- denom_pub_hash: x.denomPubHash,
- })),
- });
- });
-
- await tx.rewards.iter().forEach((tip) => {
- backupTips.push({
- exchange_base_url: tip.exchangeBaseUrl,
- merchant_base_url: tip.merchantBaseUrl,
- merchant_tip_id: tip.merchantRewardId,
- wallet_tip_id: tip.walletRewardId,
- next_url: tip.next_url,
- secret_seed: tip.secretSeed,
- selected_denoms: tip.denomsSel.selectedDenoms.map((x) => ({
- count: x.count,
- denom_pub_hash: x.denomPubHash,
- })),
- timestamp_finished: tip.pickedUpTimestamp,
- timestamp_accepted: tip.acceptedTimestamp,
- timestamp_created: tip.createdTimestamp,
- timestamp_expiration: tip.rewardExpiration,
- tip_amount_raw: Amounts.stringify(tip.rewardAmountRaw),
- selected_denoms_uid: tip.denomSelUid,
- });
- });
-
- await tx.recoupGroups.iter().forEach((recoupGroup) => {
- backupRecoupGroups.push({
- recoup_group_id: recoupGroup.recoupGroupId,
- timestamp_created: recoupGroup.timestampStarted,
- timestamp_finish: recoupGroup.timestampFinished,
- coins: recoupGroup.coinPubs.map((x, i) => ({
- coin_pub: x,
- recoup_finished: recoupGroup.recoupFinishedPerCoin[i],
- })),
- });
- });
-
- await tx.backupProviders.iter().forEach((bp) => {
- let terms: BackupBackupProviderTerms | undefined;
- if (bp.terms) {
- terms = {
- annual_fee: Amounts.stringify(bp.terms.annualFee),
- storage_limit_in_megabytes: bp.terms.storageLimitInMegabytes,
- supported_protocol_version: bp.terms.supportedProtocolVersion,
- };
- }
- backupBackupProviders.push({
- terms,
- base_url: canonicalizeBaseUrl(bp.baseUrl),
- pay_proposal_ids: bp.paymentProposalIds,
- uids: bp.uids,
- });
- });
-
- await tx.coins.iter().forEach((coin) => {
- let bcs: BackupCoinSource;
- switch (coin.coinSource.type) {
- case CoinSourceType.Refresh:
- bcs = {
- type: BackupCoinSourceType.Refresh,
- old_coin_pub: coin.coinSource.oldCoinPub,
- refresh_group_id: coin.coinSource.refreshGroupId,
- };
- break;
- case CoinSourceType.Reward:
- bcs = {
- type: BackupCoinSourceType.Reward,
- coin_index: coin.coinSource.coinIndex,
- wallet_tip_id: coin.coinSource.walletRewardId,
- };
- break;
- case CoinSourceType.Withdraw:
- bcs = {
- type: BackupCoinSourceType.Withdraw,
- coin_index: coin.coinSource.coinIndex,
- reserve_pub: coin.coinSource.reservePub,
- withdrawal_group_id: coin.coinSource.withdrawalGroupId,
- };
- break;
- }
-
- const coins = (backupCoinsByDenom[coin.denomPubHash] ??= []);
- coins.push({
- blinding_key: coin.blindingKey,
- coin_priv: coin.coinPriv,
- coin_source: bcs,
- fresh: coin.status === CoinStatus.Fresh,
- spend_allocation: coin.spendAllocation
- ? {
- amount: coin.spendAllocation.amount,
- id: coin.spendAllocation.id,
- }
- : undefined,
- denom_sig: coin.denomSig,
- });
- });
-
- await tx.denominations.iter().forEach((denom) => {
- const backupDenoms = (backupDenominationsByExchange[
- denom.exchangeBaseUrl
- ] ??= []);
- backupDenoms.push({
- coins: backupCoinsByDenom[denom.denomPubHash] ?? [],
- denom_pub: denom.denomPub,
- fee_deposit: Amounts.stringify(denom.fees.feeDeposit),
- fee_refresh: Amounts.stringify(denom.fees.feeRefresh),
- fee_refund: Amounts.stringify(denom.fees.feeRefund),
- fee_withdraw: Amounts.stringify(denom.fees.feeWithdraw),
- is_offered: denom.isOffered,
- is_revoked: denom.isRevoked,
- master_sig: denom.masterSig,
- stamp_expire_deposit: denom.stampExpireDeposit,
- stamp_expire_legal: denom.stampExpireLegal,
- stamp_expire_withdraw: denom.stampExpireWithdraw,
- stamp_start: denom.stampStart,
- value: Amounts.stringify(DenominationRecord.getValue(denom)),
- list_issue_date: denom.listIssueDate,
- });
- });
-
- await tx.exchanges.iter().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.exchangeDetails.iter().forEachAsync(async (ex) => {
- // Only back up permanently added exchanges.
-
- const wi = ex.wireInfo;
- const wireFees: BackupExchangeWireFee[] = [];
-
- Object.keys(wi.feesForType).forEach((x) => {
- for (const f of wi.feesForType[x]) {
- wireFees.push({
- wire_type: x,
- closing_fee: Amounts.stringify(f.closingFee),
- end_stamp: f.endStamp,
- sig: f.sig,
- start_stamp: f.startStamp,
- wire_fee: Amounts.stringify(f.wireFee),
- });
- }
- });
- checkDbInvariant(ex.rowId != null);
- const exchangeSk =
- await tx.exchangeSignKeys.indexes.byExchangeDetailsRowId.getAll(
- ex.rowId,
- );
- let signingKeys: BackupExchangeSignKey[] = exchangeSk.map((x) => ({
- key: x.signkeyPub,
- master_sig: x.masterSig,
- stamp_end: x.stampEnd,
- stamp_expire: x.stampExpire,
- stamp_start: x.stampStart,
- }));
-
- 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.auditors.map((x) => ({
- auditor_pub: x.auditor_pub,
- auditor_url: x.auditor_url,
- denomination_keys: x.denomination_keys,
- })),
- master_public_key: ex.masterPublicKey,
- currency: ex.currency,
- protocol_version: ex.protocolVersionRange,
- wire_fees: wireFees,
- signing_keys: signingKeys,
- global_fees: ex.globalFees.map((x) => ({
- accountFee: Amounts.stringify(x.accountFee),
- historyFee: Amounts.stringify(x.historyFee),
- purseFee: Amounts.stringify(x.purseFee),
- endDate: x.endDate,
- historyTimeout: x.historyTimeout,
- signature: x.signature,
- purseLimit: x.purseLimit,
- purseTimeout: x.purseTimeout,
- startDate: x.startDate,
- })),
- tos_accepted_etag: ex.tosAccepted?.etag,
- tos_accepted_timestamp: ex.tosAccepted?.timestamp,
- denominations:
- backupDenominationsByExchange[ex.exchangeBaseUrl] ?? [],
- });
- });
-
- const purchaseProposalIdSet = new Set<string>();
-
- await tx.purchases.iter().forEachAsync(async (purch) => {
- const refunds: BackupRefundItem[] = [];
- purchaseProposalIdSet.add(purch.proposalId);
- // for (const refundKey of Object.keys(purch.refunds)) {
- // const ri = purch.refunds[refundKey];
- // const common = {
- // coin_pub: ri.coinPub,
- // execution_time: ri.executionTime,
- // obtained_time: ri.obtainedTime,
- // refund_amount: Amounts.stringify(ri.refundAmount),
- // rtransaction_id: ri.rtransactionId,
- // total_refresh_cost_bound: Amounts.stringify(
- // ri.totalRefreshCostBound,
- // ),
- // };
- // switch (ri.type) {
- // case RefundState.Applied:
- // refunds.push({ type: BackupRefundState.Applied, ...common });
- // break;
- // case RefundState.Failed:
- // refunds.push({ type: BackupRefundState.Failed, ...common });
- // break;
- // case RefundState.Pending:
- // refunds.push({ type: BackupRefundState.Pending, ...common });
- // break;
- // }
- // }
-
- let propStatus: BackupProposalStatus;
- switch (purch.purchaseStatus) {
- case PurchaseStatus.Done:
- case PurchaseStatus.PendingQueryingAutoRefund:
- case PurchaseStatus.PendingQueryingRefund:
- propStatus = BackupProposalStatus.Paid;
- break;
- case PurchaseStatus.PendingPayingReplay:
- case PurchaseStatus.PendingDownloadingProposal:
- case PurchaseStatus.DialogProposed:
- case PurchaseStatus.PendingPaying:
- propStatus = BackupProposalStatus.Proposed;
- break;
- case PurchaseStatus.DialogShared:
- propStatus = BackupProposalStatus.Shared;
- break;
- case PurchaseStatus.FailedClaim:
- case PurchaseStatus.AbortedIncompletePayment:
- propStatus = BackupProposalStatus.PermanentlyFailed;
- break;
- case PurchaseStatus.AbortingWithRefund:
- case PurchaseStatus.AbortedProposalRefused:
- propStatus = BackupProposalStatus.Refused;
- break;
- case PurchaseStatus.RepurchaseDetected:
- propStatus = BackupProposalStatus.Repurchase;
- break;
- default: {
- const error = purch.purchaseStatus;
- throw Error(`purchase status ${error} is not handled`);
- }
- }
-
- const payInfo = purch.payInfo;
- let backupPayInfo: BackupPayInfo | undefined = undefined;
- if (payInfo) {
- backupPayInfo = {
- pay_coins: payInfo.payCoinSelection.coinPubs.map((x, i) => ({
- coin_pub: x,
- contribution: Amounts.stringify(
- payInfo.payCoinSelection.coinContributions[i],
- ),
- })),
- total_pay_cost: Amounts.stringify(payInfo.totalPayCost),
- pay_coins_uid: payInfo.payCoinSelectionUid,
- };
- }
-
- let contractTermsRaw = undefined;
- if (purch.download) {
- const contractTermsRecord = await tx.contractTerms.get(
- purch.download.contractTermsHash,
- );
- if (contractTermsRecord) {
- contractTermsRaw = contractTermsRecord.contractTermsRaw;
- }
- }
-
- backupPurchases.push({
- contract_terms_raw: contractTermsRaw,
- auto_refund_deadline: purch.autoRefundDeadline,
- merchant_pay_sig: purch.merchantPaySig,
- pos_confirmation: purch.posConfirmation,
- pay_info: backupPayInfo,
- proposal_id: purch.proposalId,
- refunds,
- timestamp_accepted: purch.timestampAccept,
- timestamp_first_successful_pay: purch.timestampFirstSuccessfulPay,
- nonce_priv: purch.noncePriv,
- merchant_sig: purch.download?.contractTermsMerchantSig,
- claim_token: purch.claimToken,
- merchant_base_url: purch.merchantBaseUrl,
- order_id: purch.orderId,
- proposal_status: propStatus,
- repurchase_proposal_id: purch.repurchaseProposalId,
- download_session_id: purch.downloadSessionId,
- timestamp_proposed: purch.timestamp,
- shared: purch.shared,
- });
- });
-
- await tx.refreshGroups.iter().forEach((rg) => {
- const oldCoins: BackupRefreshOldCoin[] = [];
-
- for (let i = 0; i < rg.oldCoinPubs.length; i++) {
- let refreshSession: BackupRefreshSession | undefined;
- const s = rg.refreshSessionPerCoin[i];
- if (s) {
- refreshSession = {
- new_denoms: s.newDenoms.map((x) => ({
- count: x.count,
- denom_pub_hash: x.denomPubHash,
- })),
- session_secret_seed: s.sessionSecretSeed,
- noreveal_index: s.norevealIndex,
- };
- }
- oldCoins.push({
- coin_pub: rg.oldCoinPubs[i],
- estimated_output_amount: Amounts.stringify(
- rg.estimatedOutputPerCoin[i],
- ),
- finished: rg.statusPerCoin[i] === RefreshCoinStatus.Finished,
- input_amount: Amounts.stringify(rg.inputPerCoin[i]),
- refresh_session: refreshSession,
- });
- }
-
- backupRefreshGroups.push({
- reason: rg.reason as any,
- refresh_group_id: rg.refreshGroupId,
- timestamp_created: rg.timestampCreated,
- timestamp_finish: rg.timestampFinished,
- old_coins: oldCoins,
- });
- });
-
- const ts = TalerPreciseTimestamp.now();
-
- if (!bs.lastBackupTimestamp) {
- bs.lastBackupTimestamp = ts;
- }
-
- const backupBlob: WalletBackupContentV1 = {
- schema_id: "gnu-taler-wallet-backup-content",
- schema_version: BACKUP_VERSION_MAJOR,
- minor_version: BACKUP_VERSION_MINOR,
- exchanges: backupExchanges,
- exchange_details: backupExchangeDetails,
- wallet_root_pub: bs.walletRootPub,
- backup_providers: backupBackupProviders,
- current_device_id: bs.deviceId,
- purchases: backupPurchases,
- recoup_groups: backupRecoupGroups,
- refresh_groups: backupRefreshGroups,
- tips: backupTips,
- timestamp: bs.lastBackupTimestamp,
- trusted_auditors: {},
- trusted_exchanges: {},
- intern_table: {},
- error_reports: [],
- tombstones: [],
- // FIXME!
- withdrawal_groups: backupWithdrawalGroups,
- };
-
- // If the backup changed, we change our nonce and timestamp.
-
- let h = encodeCrock(hash(stringToBytes(canonicalJson(backupBlob))));
- if (h !== bs.lastBackupPlainHash) {
- logger.trace(
- `plain backup hash changed (from ${bs.lastBackupPlainHash}to ${h})`,
- );
- bs.lastBackupTimestamp = ts;
- backupBlob.timestamp = ts;
- bs.lastBackupPlainHash = encodeCrock(
- hash(stringToBytes(canonicalJson(backupBlob))),
- );
- bs.lastBackupNonce = encodeCrock(getRandomBytes(32));
- logger.trace(
- `setting timestamp to ${AbsoluteTime.toIsoString(
- AbsoluteTime.fromPreciseTimestamp(ts),
- )} and nonce to ${bs.lastBackupNonce}`,
- );
- await tx.config.put({
- key: ConfigRecordKey.WalletBackupState,
- value: bs,
- });
- } else {
- logger.trace("backup hash did not change");
- }
-
- return backupBlob;
- });
-}