diff options
author | Florian Dold <florian@dold.me> | 2023-08-30 18:01:18 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2023-08-30 18:01:18 +0200 |
commit | a713d90c3c564408309d92223d383ecc9225924f (patch) | |
tree | 18142a4ce3d98df6e8a4945dbca23c47715eee6a /packages/taler-wallet-core/src/operations/backup/export.ts | |
parent | 0a4782a0da631aba31dc0ecef7427df2467cc3e6 (diff) | |
download | wallet-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.ts | 586 |
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; - }); -} |