diff options
Diffstat (limited to 'packages/taler-wallet-core/src/operations')
4 files changed, 172 insertions, 27 deletions
diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts b/packages/taler-wallet-core/src/operations/backup/export.ts index 35d5e6ef7..b39e6dc27 100644 --- a/packages/taler-wallet-core/src/operations/backup/export.ts +++ b/packages/taler-wallet-core/src/operations/backup/export.ts @@ -25,6 +25,7 @@ * Imports. */ import { + AbsoluteTime, Amounts, BackupBackupProvider, BackupBackupProviderTerms, @@ -35,6 +36,7 @@ import { BackupExchange, BackupExchangeDetails, BackupExchangeWireFee, + BackupOperationStatus, BackupProposal, BackupProposalStatus, BackupPurchase, @@ -44,30 +46,35 @@ import { BackupRefreshSession, BackupRefundItem, BackupRefundState, - BackupReserve, BackupTip, + BackupWgInfo, + BackupWgType, BackupWithdrawalGroup, + BACKUP_VERSION_MAJOR, + BACKUP_VERSION_MINOR, canonicalizeBaseUrl, canonicalJson, - Logger, - WalletBackupContentV1, - hash, encodeCrock, getRandomBytes, + hash, + Logger, stringToBytes, - AbsoluteTime, + WalletBackupContentV1, } from "@gnu-taler/taler-util"; -import { InternalWalletState } from "../../internal-wallet-state.js"; import { AbortStatus, CoinSourceType, CoinStatus, DenominationRecord, + OperationStatus, ProposalStatus, RefreshCoinStatus, RefundState, WALLET_BACKUP_STATE_KEY, + WithdrawalRecordType, } from "../../db.js"; +import { InternalWalletState } from "../../internal-wallet-state.js"; +import { assertUnreachable } from "../../util/assertUnreachable.js"; import { getWalletBackupState, provideBackupState } from "./state.js"; const logger = new Logger("backup/export.ts"); @@ -100,31 +107,75 @@ export async function exportBackup( const backupDenominationsByExchange: { [url: string]: BackupDenomination[]; } = {}; - const backupReservesByExchange: { [url: string]: BackupReserve[] } = {}; const backupPurchases: BackupPurchase[] = []; const backupProposals: BackupProposal[] = []; const backupRefreshGroups: BackupRefreshGroup[] = []; const backupBackupProviders: BackupBackupProvider[] = []; const backupTips: BackupTip[] = []; const backupRecoupGroups: BackupRecoupGroup[] = []; - const withdrawalGroupsByReserve: { - [reservePub: string]: BackupWithdrawalGroup[]; - } = {}; + const backupWithdrawalGroups: BackupWithdrawalGroup[] = []; await tx.withdrawalGroups.iter().forEachAsync(async (wg) => { - const withdrawalGroups = (withdrawalGroupsByReserve[wg.reservePub] ??= - []); - withdrawalGroups.push({ + 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), - selected_denoms: wg.denomsSel.selectedDenoms.map((x) => ({ - count: x.count, - denom_pub_hash: x.denomPubHash, - })), + info, timestamp_created: wg.timestampStart, timestamp_finish: wg.timestampFinish, withdrawal_group_id: wg.withdrawalGroupId, secret_seed: wg.secretSeed, - selected_denoms_id: wg.denomSelUid, + exchange_base_url: wg.exchangeBaseUrl, + instructed_amount: Amounts.stringify(wg.instructedAmount), + reserve_priv: wg.reservePriv, + restrict_age: wg.restrictAge, + operation_status: + wg.operationStatus == OperationStatus.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, + })), }); }); @@ -299,7 +350,6 @@ export async function exportBackup( tos_accepted_timestamp: ex.termsOfServiceAcceptedTimestamp, denominations: backupDenominationsByExchange[ex.exchangeBaseUrl] ?? [], - reserves: backupReservesByExchange[ex.exchangeBaseUrl] ?? [], }); }); @@ -439,7 +489,8 @@ export async function exportBackup( const backupBlob: WalletBackupContentV1 = { schema_id: "gnu-taler-wallet-backup-content", - schema_version: 1, + schema_version: BACKUP_VERSION_MAJOR, + minor_version: BACKUP_VERSION_MINOR, exchanges: backupExchanges, exchange_details: backupExchangeDetails, wallet_root_pub: bs.walletRootPub, @@ -456,6 +507,8 @@ export async function exportBackup( intern_table: {}, error_reports: [], tombstones: [], + // FIXME! + withdrawal_groups: backupWithdrawalGroups, }; // If the backup changed, we change our nonce and timestamp. diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index be09952cd..507a6cf10 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -24,6 +24,7 @@ import { BackupPurchase, BackupRefreshReason, BackupRefundState, + BackupWgType, codecForContractTerms, DenomKeyType, j2s, @@ -53,8 +54,11 @@ import { WalletContractData, WalletRefundItem, WalletStoresV1, + WgInfo, + WithdrawalRecordType, } from "../../db.js"; import { InternalWalletState } from "../../internal-wallet-state.js"; +import { assertUnreachable } from "../../util/assertUnreachable.js"; import { checkDbInvariant, checkLogicInvariant, @@ -444,6 +448,91 @@ export async function importBackup( } } + for (const backupWg of backupBlob.withdrawal_groups) { + const reservePub = cryptoComp.reservePrivToPub[backupWg.reserve_priv]; + checkLogicInvariant(!!reservePub); + const ts = makeEventId(TombstoneTag.DeleteReserve, reservePub); + if (tombstoneSet.has(ts)) { + continue; + } + const existingWg = await tx.withdrawalGroups.get( + backupWg.withdrawal_group_id, + ); + if (existingWg) { + continue; + } + let wgInfo: WgInfo; + switch (backupWg.info.type) { + case BackupWgType.BankIntegrated: + wgInfo = { + withdrawalType: WithdrawalRecordType.BankIntegrated, + bankInfo: { + exchangePaytoUri: backupWg.info.exchange_payto_uri, + talerWithdrawUri: backupWg.info.taler_withdraw_uri, + confirmUrl: backupWg.info.confirm_url, + timestampBankConfirmed: + backupWg.info.timestamp_bank_confirmed, + timestampReserveInfoPosted: + backupWg.info.timestamp_reserve_info_posted, + }, + }; + break; + case BackupWgType.BankManual: + wgInfo = { + withdrawalType: WithdrawalRecordType.BankManual, + }; + break; + case BackupWgType.PeerPullCredit: + wgInfo = { + withdrawalType: WithdrawalRecordType.PeerPullCredit, + contractTerms: backupWg.info.contract_terms, + contractPriv: backupWg.info.contract_priv, + }; + break; + case BackupWgType.PeerPushCredit: + wgInfo = { + withdrawalType: WithdrawalRecordType.PeerPushCredit, + contractTerms: backupWg.info.contract_terms, + }; + break; + case BackupWgType.Recoup: + wgInfo = { + withdrawalType: WithdrawalRecordType.Recoup, + }; + break; + default: + assertUnreachable(backupWg.info); + } + await tx.withdrawalGroups.put({ + withdrawalGroupId: backupWg.withdrawal_group_id, + exchangeBaseUrl: backupWg.exchange_base_url, + instructedAmount: Amounts.parseOrThrow(backupWg.instructed_amount), + secretSeed: backupWg.secret_seed, + operationStatus: backupWg.timestamp_finish + ? OperationStatus.Finished + : OperationStatus.Pending, + denomsSel: await getDenomSelStateFromBackup( + tx, + backupWg.exchange_base_url, + backupWg.selected_denoms, + ), + denomSelUid: backupWg.selected_denoms_uid, + rawWithdrawalAmount: Amounts.parseOrThrow( + backupWg.raw_withdrawal_amount, + ), + reservePriv: backupWg.reserve_priv, + reservePub, + reserveStatus: backupWg.timestamp_finish + ? ReserveRecordStatus.Dormant + : ReserveRecordStatus.QueryingStatus, // FIXME! + timestampStart: backupWg.timestamp_created, + wgInfo, + restrictAge: backupWg.restrict_age, + senderWire: undefined, // FIXME! + timestampFinish: backupWg.timestamp_finish, + }); + } + // FIXME: import reserves with new schema // for (const backupReserve of backupExchangeDetails.reserves) { diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts b/packages/taler-wallet-core/src/operations/backup/index.ts index c7c93e909..b69c0b7b7 100644 --- a/packages/taler-wallet-core/src/operations/backup/index.ts +++ b/packages/taler-wallet-core/src/operations/backup/index.ts @@ -187,11 +187,11 @@ async function computeBackupCryptoData( cryptoData.rsaDenomPubToHash[backupDenom.denom_pub.rsa_public_key] = encodeCrock(hashDenomPub(backupDenom.denom_pub)); } - for (const backupReserve of backupExchangeDetails.reserves) { - cryptoData.reservePrivToPub[backupReserve.reserve_priv] = encodeCrock( - eddsaGetPublic(decodeCrock(backupReserve.reserve_priv)), - ); - } + } + for (const backupWg of backupContent.withdrawal_groups) { + cryptoData.reservePrivToPub[backupWg.reserve_priv] = encodeCrock( + eddsaGetPublic(decodeCrock(backupWg.reserve_priv)), + ); } for (const prop of backupContent.proposals) { const { h: contractTermsHash } = await cryptoApi.hashString({ diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index de9721f3d..7dd874f49 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -96,12 +96,13 @@ import { DbAccess, GetReadOnlyAccess } from "../util/query.js"; import { OperationAttemptResult, OperationAttemptResultType, + RetryTags, } from "../util/retries.js"; import { WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, WALLET_EXCHANGE_PROTOCOL_VERSION, } from "../versions.js"; -import { makeCoinAvailable } from "../wallet.js"; +import { makeCoinAvailable, storeOperationPending } from "../wallet.js"; import { getExchangeDetails, getExchangePaytoUri, @@ -1099,6 +1100,7 @@ export async function processWithdrawalGroup( ); if (withdrawalGroup.denomsSel.selectedDenoms.length === 0) { + logger.warn("Finishing empty withdrawal group (no denoms)"); await ws.db .mktx((x) => [x.withdrawalGroups]) .runReadWrite(async (tx) => { @@ -1107,6 +1109,7 @@ export async function processWithdrawalGroup( return; } wg.operationStatus = OperationStatus.Finished; + wg.timestampFinish = TalerProtocolTimestamp.now(); await tx.withdrawalGroups.put(wg); }); return { @@ -1185,7 +1188,7 @@ export async function processWithdrawalGroup( errorsPerCoin[x.coinIdx] = x.lastError; } }); - logger.trace(`now withdrawn ${numFinished} of ${numTotalCoins} coins`); + logger.info(`now withdrawn ${numFinished} of ${numTotalCoins} coins`); if (wg.timestampFinish === undefined && numFinished === numTotalCoins) { finishedForFirstTime = true; wg.timestampFinish = TalerProtocolTimestamp.now(); |