From d50294f76e0aa357d690a933bb6d696a2f6aef1b Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 2 Nov 2022 17:42:14 +0100 Subject: wallet-core: DB FIXMEs (amount format) --- .../src/crypto/cryptoImplementation.ts | 16 ++-- .../taler-wallet-core/src/crypto/cryptoTypes.ts | 4 +- packages/taler-wallet-core/src/db.ts | 59 ++++++------ packages/taler-wallet-core/src/dbless.ts | 6 +- .../src/operations/backup/import.ts | 80 +++++++--------- .../taler-wallet-core/src/operations/balance.ts | 16 ++-- .../taler-wallet-core/src/operations/common.ts | 2 +- .../taler-wallet-core/src/operations/deposits.ts | 30 +++--- .../taler-wallet-core/src/operations/exchanges.ts | 18 ++-- .../src/operations/pay-merchant.ts | 103 +++++++++++---------- .../taler-wallet-core/src/operations/pay-peer.ts | 12 +-- .../taler-wallet-core/src/operations/recoup.ts | 2 +- .../taler-wallet-core/src/operations/refresh.ts | 32 ++++--- packages/taler-wallet-core/src/operations/tip.ts | 4 +- .../src/operations/transactions.ts | 41 +++++--- .../src/operations/withdraw.test.ts | 96 +++++++++---------- .../taler-wallet-core/src/operations/withdraw.ts | 26 +++--- .../taler-wallet-core/src/util/coinSelection.ts | 2 +- .../src/util/denominations.test.ts | 5 +- .../taler-wallet-core/src/util/denominations.ts | 11 ++- packages/taler-wallet-core/src/wallet.ts | 2 +- 21 files changed, 297 insertions(+), 270 deletions(-) (limited to 'packages/taler-wallet-core') diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts index 8ba7d9298..d239270c8 100644 --- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts +++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts @@ -28,6 +28,7 @@ import { AgeCommitmentProof, AgeRestriction, AmountJson, + AmountLike, Amounts, AmountString, BlindedDenominationSignature, @@ -1155,8 +1156,8 @@ export const nativeCryptoR: TalerCryptoInterfaceR = { sessionSecretSeed: refreshSessionSecretSeed, } = req; - const currency = newCoinDenoms[0].value.currency; - let valueWithFee = Amounts.getZero(currency); + const currency = Amounts.currencyOf(newCoinDenoms[0].value); + let valueWithFee = Amounts.zeroOfCurrency(currency); for (const ncd of newCoinDenoms) { const t = Amounts.add(ncd.value, ncd.feeWithdraw).amount; @@ -1627,21 +1628,22 @@ export const nativeCryptoR: TalerCryptoInterfaceR = { }, }; -function amountToBuffer(amount: AmountJson): Uint8Array { +function amountToBuffer(amount: AmountLike): Uint8Array { + const amountJ = Amounts.jsonifyAmount(amount); const buffer = new ArrayBuffer(8 + 4 + 12); const dvbuf = new DataView(buffer); const u8buf = new Uint8Array(buffer); - const curr = stringToBytes(amount.currency); + const curr = stringToBytes(amountJ.currency); if (typeof dvbuf.setBigUint64 !== "undefined") { - dvbuf.setBigUint64(0, BigInt(amount.value)); + dvbuf.setBigUint64(0, BigInt(amountJ.value)); } else { - const arr = bigint(amount.value).toArray(2 ** 8).value; + const arr = bigint(amountJ.value).toArray(2 ** 8).value; let offset = 8 - arr.length; for (let i = 0; i < arr.length; i++) { dvbuf.setUint8(offset++, arr[i]); } } - dvbuf.setUint32(8, amount.fraction); + dvbuf.setUint32(8, amountJ.fraction); u8buf.set(curr, 8 + 4); return u8buf; diff --git a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts index 0858cffa9..a083f453c 100644 --- a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts +++ b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts @@ -44,8 +44,8 @@ import { export interface RefreshNewDenomInfo { count: number; - value: AmountJson; - feeWithdraw: AmountJson; + value: AmountString; + feeWithdraw: AmountString; denomPub: DenominationPubKey; denomPubHash: string; } diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 12e7820c2..0b27b82dd 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -47,6 +47,7 @@ import { UnblindedSignature, WireInfo, HashCodeString, + Amounts, } from "@gnu-taler/taler-util"; import { describeContents, @@ -276,22 +277,22 @@ export interface DenomFees { /** * Fee for withdrawing. */ - feeWithdraw: AmountJson; + feeWithdraw: AmountString; /** * Fee for depositing. */ - feeDeposit: AmountJson; + feeDeposit: AmountString; /** * Fee for refreshing. */ - feeRefresh: AmountJson; + feeRefresh: AmountString; /** * Fee for refunding. */ - feeRefund: AmountJson; + feeRefund: AmountString; } /** @@ -393,15 +394,15 @@ export namespace DenominationRecord { return { denomPub: d.denomPub, denomPubHash: d.denomPubHash, - feeDeposit: d.fees.feeDeposit, - feeRefresh: d.fees.feeRefresh, - feeRefund: d.fees.feeRefund, - feeWithdraw: d.fees.feeWithdraw, + feeDeposit: Amounts.stringify(d.fees.feeDeposit), + feeRefresh: Amounts.stringify(d.fees.feeRefresh), + feeRefund: Amounts.stringify(d.fees.feeRefund), + feeWithdraw: Amounts.stringify(d.fees.feeWithdraw), stampExpireDeposit: d.stampExpireDeposit, stampExpireLegal: d.stampExpireLegal, stampExpireWithdraw: d.stampExpireWithdraw, stampStart: d.stampStart, - value: DenominationRecord.getValue(d), + value: Amounts.stringify(DenominationRecord.getValue(d)), exchangeBaseUrl: d.exchangeBaseUrl, }; } @@ -527,7 +528,7 @@ export interface ExchangeRecord { * currency. * * We could use a rowID here, but having the currency in the - * details pointer lets us do fewer DB queries sometimes. + * details pointer lets us do fewer DB queries */ detailsPointer: ExchangeDetailsPointer | undefined; @@ -752,12 +753,12 @@ export interface TipRecord { /** * The tipped amount. */ - tipAmountRaw: AmountJson; + tipAmountRaw: AmountString; /** * Effect on the balance (including fees etc). */ - tipAmountEffective: AmountJson; + tipAmountEffective: AmountString; /** * Timestamp, the tip can't be picked up anymore after this deadline. @@ -854,9 +855,9 @@ export interface RefreshGroupRecord { // object store for faster updates? refreshSessionPerCoin: (RefreshSessionRecord | undefined)[]; - inputPerCoin: AmountJson[]; + inputPerCoin: AmountString[]; - estimatedOutputPerCoin: AmountJson[]; + estimatedOutputPerCoin: AmountString[]; /** * Flag for each coin whether refreshing finished. @@ -888,7 +889,7 @@ export interface RefreshSessionRecord { * Sum of the value of denominations we want * to withdraw in this session, without fees. */ - amountRefreshOutput: AmountJson; + amountRefreshOutput: AmountString; /** * Hashed denominations of the newly requested coins. @@ -927,9 +928,9 @@ export interface WalletRefundItemCommon { */ obtainedTime: TalerProtocolTimestamp; - refundAmount: AmountJson; + refundAmount: AmountString; - refundFee: AmountJson; + refundFee: AmountString; /** * Upper bound on the refresh cost incurred by @@ -938,7 +939,7 @@ export interface WalletRefundItemCommon { * Might be lower in practice when two refunds on the same * coin are refreshed in the same refresh operation. */ - totalRefreshCostBound: AmountJson; + totalRefreshCostBound: AmountString; coinPub: string; @@ -1003,12 +1004,12 @@ export interface WalletContractData { merchantSig: string; merchantPub: string; merchant: MerchantInfo; - amount: AmountJson; + amount: AmountString; orderId: string; merchantBaseUrl: string; summary: string; autoRefund: TalerProtocolDuration | undefined; - maxWireFee: AmountJson; + maxWireFee: AmountString; wireFeeAmortization: number; payDeadline: TalerProtocolTimestamp; refundDeadline: TalerProtocolTimestamp; @@ -1017,7 +1018,7 @@ export interface WalletContractData { timestamp: TalerProtocolTimestamp; wireMethod: string; wireInfoHash: string; - maxDepositFee: AmountJson; + maxDepositFee: AmountString; minimumAge?: number; deliveryDate: TalerProtocolTimestamp | undefined; deliveryLocation: Location | undefined; @@ -1099,7 +1100,7 @@ export interface ProposalDownloadInfo { export interface PurchasePayInfo { payCoinSelection: PayCoinSelection; - totalPayCost: AmountJson; + totalPayCost: AmountString; payCoinSelectionUid: string; } @@ -1216,7 +1217,7 @@ export interface PurchaseRecord { * How much merchant has refund to be taken but the wallet * did not picked up yet */ - refundAmountAwaiting: AmountJson | undefined; + refundAmountAwaiting: AmountString | undefined; } export enum ConfigRecordKey { @@ -1379,7 +1380,7 @@ export interface WithdrawalGroupRecord { /** * Amount that was sent by the user to fund the reserve. */ - instructedAmount: AmountJson; + instructedAmount: AmountString; /** * Amount that was observed when querying the reserve that @@ -1387,7 +1388,7 @@ export interface WithdrawalGroupRecord { * * Useful for diagnostics. */ - reserveBalanceAmount?: AmountJson; + reserveBalanceAmount?: AmountString; /** * Amount including fees (i.e. the amount subtracted from the @@ -1396,7 +1397,7 @@ export interface WithdrawalGroupRecord { * (Initial amount confirmed by the user, might differ with denomSel * on reselection.) */ - rawWithdrawalAmount: AmountJson; + rawWithdrawalAmount: AmountString; /** * Amount that will be added to the balance when the withdrawal succeeds. @@ -1404,7 +1405,7 @@ export interface WithdrawalGroupRecord { * (Initial amount confirmed by the user, might differ with denomSel * on reselection.) */ - effectiveWithdrawalAmount: AmountJson; + effectiveWithdrawalAmount: AmountString; /** * Denominations selected for withdrawal. @@ -1587,9 +1588,9 @@ export interface DepositGroupRecord { payCoinSelectionUid: string; - totalPayCost: AmountJson; + totalPayCost: AmountString; - effectiveDepositAmount: AmountJson; + effectiveDepositAmount: AmountString; depositedPerCoin: boolean[]; diff --git a/packages/taler-wallet-core/src/dbless.ts b/packages/taler-wallet-core/src/dbless.ts index 076e5f215..544e2d458 100644 --- a/packages/taler-wallet-core/src/dbless.ts +++ b/packages/taler-wallet-core/src/dbless.ts @@ -160,7 +160,7 @@ export async function withdrawCoin(args: { const planchet = await cryptoApi.createPlanchet({ coinIndex: 0, denomPub: denom.denomPub, - feeWithdraw: denom.fees.feeWithdraw, + feeWithdraw: Amounts.parseOrThrow(denom.fees.feeWithdraw), reservePriv: reserveKeyPair.reservePriv, reservePub: reserveKeyPair.reservePub, secretSeed: encodeCrock(getRandomBytes(32)), @@ -294,11 +294,11 @@ export async function refreshCoin(req: { denomPub: x.denomPub, denomPubHash: x.denomPubHash, feeWithdraw: x.fees.feeWithdraw, - value: { + value: Amounts.stringify({ currency: x.currency, fraction: x.amountFrac, value: x.amountVal, - }, + }), })), meltCoinMaxAge: oldCoin.maxAge, }); diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index 133699647..3159c60af 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -104,10 +104,8 @@ async function recoverPayCoinSelection( const coveredExchanges: Set = new Set(); - let totalWireFee: AmountJson = Amounts.getZero(contractData.amount.currency); - let totalDepositFees: AmountJson = Amounts.getZero( - contractData.amount.currency, - ); + let totalWireFee: AmountJson = Amounts.zeroOfAmount(contractData.amount); + let totalDepositFees: AmountJson = Amounts.zeroOfAmount(contractData.amount); for (const coinPub of coinPubs) { const coinRecord = await tx.coins.get(coinPub); @@ -136,7 +134,7 @@ async function recoverPayCoinSelection( fee.startStamp <= contractData.timestamp && fee.endStamp >= contractData.timestamp ) { - wireFee = fee.wireFee; + wireFee = Amounts.parseOrThrow(fee.wireFee); break; } } @@ -156,7 +154,7 @@ async function recoverPayCoinSelection( if (Amounts.cmp(contractData.maxWireFee, amortizedWireFee) < 0) { customerWireFee = amortizedWireFee; } else { - customerWireFee = Amounts.getZero(contractData.amount.currency); + customerWireFee = Amounts.zeroOfAmount(contractData.amount); } const customerDepositFees = Amounts.sub( @@ -166,10 +164,10 @@ async function recoverPayCoinSelection( return { coinPubs, - coinContributions, - paymentAmount: contractData.amount, - customerWireFees: customerWireFee, - customerDepositFees, + coinContributions: coinContributions.map((x) => Amounts.stringify(x)), + paymentAmount: Amounts.stringify(contractData.amount), + customerWireFees: Amounts.stringify(customerWireFee), + customerDepositFees: Amounts.stringify(customerDepositFees), }; } @@ -183,8 +181,8 @@ async function getDenomSelStateFromBackup( denomPubHash: string; count: number; }[] = []; - let totalCoinValue = Amounts.getZero(currency); - let totalWithdrawCost = Amounts.getZero(currency); + let totalCoinValue = Amounts.zeroOfCurrency(currency); + let totalWithdrawCost = Amounts.zeroOfCurrency(currency); for (const s of sel) { const d = await tx.denominations.get([exchangeBaseUrl, s.denom_pub_hash]); checkBackupInvariant(!!d); @@ -200,8 +198,8 @@ async function getDenomSelStateFromBackup( } return { selectedDenoms, - totalCoinValue, - totalWithdrawCost, + totalCoinValue: Amounts.stringify(totalCoinValue), + totalWithdrawCost: Amounts.stringify(totalCoinValue), }; } @@ -380,11 +378,11 @@ export async function importBackup( for (const fee of backupExchangeDetails.wire_fees) { const w = (wireInfo.feesForType[fee.wire_type] ??= []); w.push({ - closingFee: Amounts.parseOrThrow(fee.closing_fee), + closingFee: Amounts.stringify(fee.closing_fee), endStamp: fee.end_stamp, sig: fee.sig, startStamp: fee.start_stamp, - wireFee: Amounts.parseOrThrow(fee.wire_fee), + wireFee: Amounts.stringify(fee.wire_fee), }); } let tosAccepted = undefined; @@ -412,9 +410,9 @@ export async function importBackup( tosCurrentEtag: backupExchangeDetails.tos_accepted_etag || "", tosAccepted, globalFees: backupExchangeDetails.global_fees.map((x) => ({ - accountFee: Amounts.parseOrThrow(x.accountFee), - historyFee: Amounts.parseOrThrow(x.historyFee), - purseFee: Amounts.parseOrThrow(x.purseFee), + accountFee: Amounts.stringify(x.accountFee), + historyFee: Amounts.stringify(x.historyFee), + purseFee: Amounts.stringify(x.purseFee), endDate: x.endDate, historyTimeout: x.historyTimeout, signature: x.signature, @@ -447,16 +445,10 @@ export async function importBackup( exchangeBaseUrl: backupExchangeDetails.base_url, exchangeMasterPub: backupExchangeDetails.master_public_key, fees: { - feeDeposit: Amounts.parseOrThrow( - backupDenomination.fee_deposit, - ), - feeRefresh: Amounts.parseOrThrow( - backupDenomination.fee_refresh, - ), - feeRefund: Amounts.parseOrThrow(backupDenomination.fee_refund), - feeWithdraw: Amounts.parseOrThrow( - backupDenomination.fee_withdraw, - ), + feeDeposit: Amounts.stringify(backupDenomination.fee_deposit), + feeRefresh: Amounts.stringify(backupDenomination.fee_refresh), + feeRefund: Amounts.stringify(backupDenomination.fee_refund), + feeWithdraw: Amounts.stringify(backupDenomination.fee_withdraw), }, isOffered: backupDenomination.is_offered, isRevoked: backupDenomination.is_revoked, @@ -542,7 +534,7 @@ export async function importBackup( await tx.withdrawalGroups.put({ withdrawalGroupId: backupWg.withdrawal_group_id, exchangeBaseUrl: backupWg.exchange_base_url, - instructedAmount, + instructedAmount: Amounts.stringify(instructedAmount), secretSeed: backupWg.secret_seed, denomsSel: await getDenomSelStateFromBackup( tx, @@ -551,10 +543,10 @@ export async function importBackup( backupWg.selected_denoms, ), denomSelUid: backupWg.selected_denoms_uid, - rawWithdrawalAmount: Amounts.parseOrThrow( + rawWithdrawalAmount: Amounts.stringify( backupWg.raw_withdrawal_amount, ), - effectiveWithdrawalAmount: Amounts.parseOrThrow( + effectiveWithdrawalAmount: Amounts.stringify( backupWg.effective_withdrawal_amount, ), reservePriv: backupWg.reserve_priv, @@ -618,10 +610,10 @@ export async function importBackup( coinPub: backupRefund.coin_pub, executionTime: backupRefund.execution_time, obtainedTime: backupRefund.obtained_time, - refundAmount: Amounts.parseOrThrow(backupRefund.refund_amount), - refundFee: denom.fees.feeRefund, + refundAmount: Amounts.stringify(backupRefund.refund_amount), + refundFee: Amounts.stringify(denom.fees.feeRefund), rtransactionId: backupRefund.rtransaction_id, - totalRefreshCostBound: Amounts.parseOrThrow( + totalRefreshCostBound: Amounts.stringify( backupRefund.total_refresh_cost_bound, ), }; @@ -658,7 +650,7 @@ export async function importBackup( if (parsedContractTerms.max_wire_fee) { maxWireFee = Amounts.parseOrThrow(parsedContractTerms.max_wire_fee); } else { - maxWireFee = Amounts.getZero(amount.currency); + maxWireFee = Amounts.zeroOfCurrency(amount.currency); } const download: ProposalDownloadInfo = { contractTermsHash, @@ -682,7 +674,7 @@ export async function importBackup( backupPurchase.pay_info, ), payCoinSelectionUid: backupPurchase.pay_info.pay_coins_uid, - totalPayCost: Amounts.parseOrThrow( + totalPayCost: Amounts.stringify( backupPurchase.pay_info.total_pay_cost, ), }; @@ -776,7 +768,7 @@ export async function importBackup( count: x.count, denomPubHash: x.denom_pub_hash, })), - amountRefreshOutput: denomSel.totalCoinValue, + amountRefreshOutput: Amounts.stringify(denomSel.totalCoinValue), }); } else { refreshSessionPerCoin.push(undefined); @@ -797,11 +789,11 @@ export async function importBackup( operationStatus: backupRefreshGroup.timestamp_finish ? RefreshOperationStatus.Finished : RefreshOperationStatus.Pending, - inputPerCoin: backupRefreshGroup.old_coins.map((x) => - Amounts.parseOrThrow(x.input_amount), + inputPerCoin: backupRefreshGroup.old_coins.map( + (x) => x.input_amount, ), - estimatedOutputPerCoin: backupRefreshGroup.old_coins.map((x) => - Amounts.parseOrThrow(x.estimated_output_amount), + estimatedOutputPerCoin: backupRefreshGroup.old_coins.map( + (x) => x.estimated_output_amount, ), refreshSessionPerCoin, }); @@ -834,8 +826,8 @@ export async function importBackup( merchantTipId: backupTip.merchant_tip_id, pickedUpTimestamp: backupTip.timestamp_finished, secretSeed: backupTip.secret_seed, - tipAmountEffective: denomsSel.totalCoinValue, - tipAmountRaw, + tipAmountEffective: Amounts.stringify(denomsSel.totalCoinValue), + tipAmountRaw: Amounts.stringify(tipAmountRaw), tipExpiration: backupTip.timestamp_expiration, walletTipId: backupTip.wallet_tip_id, denomSelUid: backupTip.selected_denoms_uid, diff --git a/packages/taler-wallet-core/src/operations/balance.ts b/packages/taler-wallet-core/src/operations/balance.ts index 3db66b5d9..cd78b0360 100644 --- a/packages/taler-wallet-core/src/operations/balance.ts +++ b/packages/taler-wallet-core/src/operations/balance.ts @@ -57,9 +57,9 @@ export async function getBalancesInsideTransaction( const b = balanceStore[currency]; if (!b) { balanceStore[currency] = { - available: Amounts.getZero(currency), - pendingIncoming: Amounts.getZero(currency), - pendingOutgoing: Amounts.getZero(currency), + available: Amounts.zeroOfCurrency(currency), + pendingIncoming: Amounts.zeroOfCurrency(currency), + pendingOutgoing: Amounts.zeroOfCurrency(currency), }; } return balanceStore[currency]; @@ -85,7 +85,10 @@ export async function getBalancesInsideTransaction( for (let i = 0; i < r.oldCoinPubs.length; i++) { const session = r.refreshSessionPerCoin[i]; if (session) { - const b = initBalance(session.amountRefreshOutput.currency); + const currency = Amounts.parseOrThrow( + session.amountRefreshOutput, + ).currency; + const b = initBalance(currency); // We are always assuming the refresh will succeed, thus we // report the output as available balance. b.available = Amounts.add( @@ -93,7 +96,8 @@ export async function getBalancesInsideTransaction( session.amountRefreshOutput, ).amount; } else { - const b = initBalance(r.inputPerCoin[i].currency); + const currency = Amounts.parseOrThrow(r.inputPerCoin[i]).currency; + const b = initBalance(currency); b.available = Amounts.add( b.available, r.estimatedOutputPerCoin[i], @@ -106,7 +110,7 @@ export async function getBalancesInsideTransaction( if (wds.timestampFinish) { return; } - const b = initBalance(wds.denomsSel.totalWithdrawCost.currency); + const b = initBalance(Amounts.currencyOf(wds.denomsSel.totalWithdrawCost)); b.pendingIncoming = Amounts.add( b.pendingIncoming, wds.denomsSel.totalCoinValue, diff --git a/packages/taler-wallet-core/src/operations/common.ts b/packages/taler-wallet-core/src/operations/common.ts index f35556736..73d1ee4b0 100644 --- a/packages/taler-wallet-core/src/operations/common.ts +++ b/packages/taler-wallet-core/src/operations/common.ts @@ -160,7 +160,7 @@ export async function spendCoins( throw Error("not enough remaining balance on coin for payment"); } refreshCoinPubs.push({ - amount: remaining.amount, + amount: Amounts.stringify(remaining.amount), coinPub: coin.coinPub, }); checkDbInvariant(!!coinAvailability); diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts index b2bd18260..6ac4f3986 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -348,10 +348,10 @@ export async function prepareDepositGroup( auditors: contractData.allowedAuditors, exchanges: contractData.allowedExchanges, wireMethod: contractData.wireMethod, - contractTermsAmount: contractData.amount, - depositFeeLimit: contractData.maxDepositFee, + contractTermsAmount: Amounts.parseOrThrow(contractData.amount), + depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee), wireFeeAmortization: contractData.wireFeeAmortization ?? 1, - wireFeeLimit: contractData.maxWireFee, + wireFeeLimit: Amounts.parseOrThrow(contractData.maxWireFee), prevPayCoins: [], }); @@ -445,10 +445,10 @@ export async function createDepositGroup( auditors: contractData.allowedAuditors, exchanges: contractData.allowedExchanges, wireMethod: contractData.wireMethod, - contractTermsAmount: contractData.amount, - depositFeeLimit: contractData.maxDepositFee, + contractTermsAmount: Amounts.parseOrThrow(contractData.amount), + depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee), wireFeeAmortization: contractData.wireFeeAmortization ?? 1, - wireFeeLimit: contractData.maxWireFee, + wireFeeLimit: Amounts.parseOrThrow(contractData.maxWireFee), prevPayCoins: [], }); @@ -479,8 +479,8 @@ export async function createDepositGroup( depositedPerCoin: payCoinSel.coinPubs.map(() => false), merchantPriv: merchantPair.priv, merchantPub: merchantPair.pub, - totalPayCost: totalDepositCost, - effectiveDepositAmount, + totalPayCost: Amounts.stringify(totalDepositCost), + effectiveDepositAmount: Amounts.stringify(effectiveDepositAmount), wire: { payto_uri: req.depositPaytoUri, salt: wireSalt, @@ -501,7 +501,9 @@ export async function createDepositGroup( await spendCoins(ws, tx, { allocationId: `txn:deposit:${depositGroup.depositGroupId}`, coinPubs: payCoinSel.coinPubs, - contributions: payCoinSel.coinContributions, + contributions: payCoinSel.coinContributions.map((x) => + Amounts.parseOrThrow(x), + ), refreshReason: RefreshReason.PayDeposit, }); await tx.depositGroups.put(depositGroup); @@ -543,8 +545,8 @@ export async function getEffectiveDepositAmount( if (!denom) { throw Error("can't find denomination to calculate deposit amount"); } - amt.push(pcs.coinContributions[i]); - fees.push(denom.feeDeposit); + amt.push(Amounts.parseOrThrow(pcs.coinContributions[i])); + fees.push(Amounts.parseOrThrow(denom.feeDeposit)); exchangeSet.add(coin.exchangeBaseUrl); } @@ -565,7 +567,7 @@ export async function getEffectiveDepositAmount( ); })?.wireFee; if (fee) { - fees.push(fee); + fees.push(Amounts.parseOrThrow(fee)); } } }); @@ -604,7 +606,7 @@ export async function getTotalFeesForDepositAmount( if (!denom) { throw Error("can't find denomination to calculate deposit amount"); } - coinFee.push(denom.feeDeposit); + coinFee.push(Amounts.parseOrThrow(denom.feeDeposit)); exchangeSet.add(coin.exchangeBaseUrl); const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl @@ -638,7 +640,7 @@ export async function getTotalFeesForDepositAmount( }, )?.wireFee; if (fee) { - wireFee.push(fee); + wireFee.push(Amounts.parseOrThrow(fee)); } } }); diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index 23ff1479e..b6e2a9d73 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -98,10 +98,10 @@ function denominationRecordFromKeys( exchangeBaseUrl, exchangeMasterPub, fees: { - feeDeposit: Amounts.parseOrThrow(denomIn.fee_deposit), - feeRefresh: Amounts.parseOrThrow(denomIn.fee_refresh), - feeRefund: Amounts.parseOrThrow(denomIn.fee_refund), - feeWithdraw: Amounts.parseOrThrow(denomIn.fee_withdraw), + feeDeposit: Amounts.stringify(denomIn.fee_deposit), + feeRefresh: Amounts.stringify(denomIn.fee_refresh), + feeRefund: Amounts.stringify(denomIn.fee_refund), + feeWithdraw: Amounts.stringify(denomIn.fee_withdraw), }, isOffered: true, isRevoked: false, @@ -267,11 +267,11 @@ async function validateWireInfo( const startStamp = x.start_date; const endStamp = x.end_date; const fee: WireFee = { - closingFee: Amounts.parseOrThrow(x.closing_fee), + closingFee: Amounts.stringify(x.closing_fee), endStamp, sig: x.sig, startStamp, - wireFee: Amounts.parseOrThrow(x.wire_fee), + wireFee: Amounts.stringify(x.wire_fee), }; let isValid = false; if (ws.insecureTrustExchange) { @@ -321,9 +321,9 @@ async function validateGlobalFees( throw Error("exchange global fees signature invalid: " + gf.master_sig); } egf.push({ - accountFee: Amounts.parseOrThrow(gf.account_fee), - historyFee: Amounts.parseOrThrow(gf.history_fee), - purseFee: Amounts.parseOrThrow(gf.purse_fee), + accountFee: Amounts.stringify(gf.account_fee), + historyFee: Amounts.stringify(gf.history_fee), + purseFee: Amounts.stringify(gf.purse_fee), startDate: gf.start_date, endDate: gf.end_date, signature: gf.master_sig, diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts b/packages/taler-wallet-core/src/operations/pay-merchant.ts index 2eb5b18e9..4483a57c0 100644 --- a/packages/taler-wallet-core/src/operations/pay-merchant.ts +++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts @@ -182,10 +182,10 @@ export async function getTotalPaymentCost( DenominationRecord.toDenomInfo(denom), amountLeft, ); - costs.push(pcs.coinContributions[i]); + costs.push(Amounts.parseOrThrow(pcs.coinContributions[i])); costs.push(refreshCost); } - const zero = Amounts.getZero(pcs.paymentAmount.currency); + const zero = Amounts.zeroOfAmount(pcs.paymentAmount); return Amounts.sum([zero, ...costs]).amount; }); } @@ -307,10 +307,10 @@ export function extractContractData( if (parsedContractTerms.max_wire_fee) { maxWireFee = Amounts.parseOrThrow(parsedContractTerms.max_wire_fee); } else { - maxWireFee = Amounts.getZero(amount.currency); + maxWireFee = Amounts.zeroOfCurrency(amount.currency); } return { - amount, + amount: Amounts.stringify(amount), contractTermsHash: contractTermsHash, fulfillmentUrl: parsedContractTerms.fulfillment_url ?? "", merchantBaseUrl: parsedContractTerms.merchant_base_url, @@ -319,7 +319,7 @@ export function extractContractData( orderId: parsedContractTerms.order_id, summary: parsedContractTerms.summary, autoRefund: parsedContractTerms.auto_refund, - maxWireFee, + maxWireFee: Amounts.stringify(maxWireFee), payDeadline: parsedContractTerms.pay_deadline, refundDeadline: parsedContractTerms.refund_deadline, wireFeeAmortization: parsedContractTerms.wire_fee_amortization || 1, @@ -334,7 +334,7 @@ export function extractContractData( timestamp: parsedContractTerms.timestamp, wireMethod: parsedContractTerms.wire_method, wireInfoHash: parsedContractTerms.h_wire, - maxDepositFee: Amounts.parseOrThrow(parsedContractTerms.max_fee), + maxDepositFee: Amounts.stringify(parsedContractTerms.max_fee), merchant: parsedContractTerms.merchant, products: parsedContractTerms.products, summaryI18n: parsedContractTerms.summary_i18n, @@ -539,7 +539,7 @@ export async function processDownloadProposal( p.download = { contractTermsHash, contractTermsMerchantSig: contractData.merchantSig, - currency: contractData.amount.currency, + currency: Amounts.currencyOf(contractData.amount), fulfillmentUrl: contractData.fulfillmentUrl, }; await tx.contractTerms.put({ @@ -825,9 +825,9 @@ async function handleInsufficientFunds( } prevPayCoins.push({ coinPub, - contribution: contrib, + contribution: Amounts.parseOrThrow(contrib), exchangeBaseUrl: coin.exchangeBaseUrl, - feeDeposit: denom.fees.feeDeposit, + feeDeposit: Amounts.parseOrThrow(denom.fees.feeDeposit), }); } }); @@ -836,10 +836,10 @@ async function handleInsufficientFunds( auditors: contractData.allowedAuditors, exchanges: contractData.allowedExchanges, wireMethod: contractData.wireMethod, - contractTermsAmount: contractData.amount, - depositFeeLimit: contractData.maxDepositFee, + contractTermsAmount: Amounts.parseOrThrow(contractData.amount), + depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee), wireFeeAmortization: contractData.wireFeeAmortization ?? 1, - wireFeeLimit: contractData.maxWireFee, + wireFeeLimit: Amounts.parseOrThrow(contractData.maxWireFee), prevPayCoins, requiredMinimumAge: contractData.minimumAge, }); @@ -875,7 +875,9 @@ async function handleInsufficientFunds( await spendCoins(ws, tx, { allocationId: `txn:proposal:${p.proposalId}`, coinPubs: payInfo.payCoinSelection.coinPubs, - contributions: payInfo.payCoinSelection.coinContributions, + contributions: payInfo.payCoinSelection.coinContributions.map((x) => + Amounts.parseOrThrow(x), + ), refreshReason: RefreshReason.PayMerchant, }); }); @@ -1068,7 +1070,7 @@ export function selectGreedy( wireFeesPerExchange, wireFeeAmortization, aci.exchangeBaseUrl, - aci.feeDeposit, + Amounts.parseOrThrow(aci.feeDeposit), ); let coinSpend = Amounts.max( @@ -1190,8 +1192,8 @@ export async function selectPayCoinsNew( amountPayRemaining: contractTermsAmount, amountWireFeeLimitRemaining: wireFeeLimit, amountDepositFeeLimitRemaining: depositFeeLimit, - customerDepositFees: Amounts.getZero(currency), - customerWireFees: Amounts.getZero(currency), + customerDepositFees: Amounts.zeroOfCurrency(currency), + customerWireFees: Amounts.zeroOfCurrency(currency), wireFeeCoveredForExchange: new Set(), }; @@ -1269,11 +1271,11 @@ export async function selectPayCoinsNew( }); return { - paymentAmount: contractTermsAmount, - coinContributions, + paymentAmount: Amounts.stringify(contractTermsAmount), + coinContributions: coinContributions.map((x) => Amounts.stringify(x)), coinPubs, - customerDepositFees: tally.customerDepositFees, - customerWireFees: tally.customerWireFees, + customerDepositFees: Amounts.stringify(tally.customerDepositFees), + customerWireFees: Amounts.stringify(tally.customerWireFees), }; } @@ -1326,10 +1328,10 @@ export async function checkPaymentByProposalId( const res = await selectPayCoinsNew(ws, { auditors: contractData.allowedAuditors, exchanges: contractData.allowedExchanges, - contractTermsAmount: contractData.amount, - depositFeeLimit: contractData.maxDepositFee, + contractTermsAmount: Amounts.parseOrThrow(contractData.amount), + depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee), wireFeeAmortization: contractData.wireFeeAmortization ?? 1, - wireFeeLimit: contractData.maxWireFee, + wireFeeLimit: Amounts.parseOrThrow(contractData.maxWireFee), prevPayCoins: [], requiredMinimumAge: contractData.minimumAge, wireMethod: contractData.wireMethod, @@ -1531,10 +1533,10 @@ export async function generateDepositPermissions( denomKeyType: denom.denomPub.cipher, denomSig: coin.denomSig, exchangeBaseUrl: coin.exchangeBaseUrl, - feeDeposit: denom.fees.feeDeposit, + feeDeposit: Amounts.parseOrThrow(denom.fees.feeDeposit), merchantPub: contractData.merchantPub, refundDeadline: contractData.refundDeadline, - spendAmount: payCoinSel.coinContributions[i], + spendAmount: Amounts.parseOrThrow(payCoinSel.coinContributions[i]), timestamp: contractData.timestamp, wireInfoHash, ageCommitmentProof: coin.ageCommitmentProof, @@ -1684,10 +1686,10 @@ export async function confirmPay( auditors: contractData.allowedAuditors, exchanges: contractData.allowedExchanges, wireMethod: contractData.wireMethod, - contractTermsAmount: contractData.amount, - depositFeeLimit: contractData.maxDepositFee, + contractTermsAmount: Amounts.parseOrThrow(contractData.amount), + depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee), wireFeeAmortization: contractData.wireFeeAmortization ?? 1, - wireFeeLimit: contractData.maxWireFee, + wireFeeLimit: Amounts.parseOrThrow(contractData.maxWireFee), prevPayCoins: [], requiredMinimumAge: contractData.minimumAge, forcedSelection: forcedCoinSel, @@ -1742,7 +1744,7 @@ export async function confirmPay( p.payInfo = { payCoinSelection: coinSelection, payCoinSelectionUid: encodeCrock(getRandomBytes(16)), - totalPayCost: payCostInfo, + totalPayCost: Amounts.stringify(payCostInfo), }; p.lastSessionId = sessionId; p.timestampAccept = TalerProtocolTimestamp.now(); @@ -1751,7 +1753,9 @@ export async function confirmPay( await spendCoins(ws, tx, { allocationId: `txn:proposal:${p.proposalId}`, coinPubs: coinSelection.coinPubs, - contributions: coinSelection.coinContributions, + contributions: coinSelection.coinContributions.map((x) => + Amounts.parseOrThrow(x), + ), refreshReason: RefreshReason.PayMerchant, }); break; @@ -2131,15 +2135,18 @@ async function applySuccessfulRefund( amountLeft, ); - refreshCoinsMap[coin.coinPub] = { coinPub: coin.coinPub, amount: amountLeft }; + refreshCoinsMap[coin.coinPub] = { + coinPub: coin.coinPub, + amount: Amounts.stringify(amountLeft), + }; p.refunds[refundKey] = { type: RefundState.Applied, obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()), executionTime: r.execution_time, - refundAmount: Amounts.parseOrThrow(r.refund_amount), - refundFee: denom.fees.feeRefund, - totalRefreshCostBound, + refundAmount: Amounts.stringify(r.refund_amount), + refundFee: Amounts.stringify(denom.fees.feeRefund), + totalRefreshCostBound: Amounts.stringify(totalRefreshCostBound), coinPub: r.coin_pub, rtransactionId: r.rtransaction_id, }; @@ -2189,9 +2196,9 @@ async function storePendingRefund( type: RefundState.Pending, obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()), executionTime: r.execution_time, - refundAmount: Amounts.parseOrThrow(r.refund_amount), - refundFee: denom.fees.feeRefund, - totalRefreshCostBound, + refundAmount: Amounts.stringify(r.refund_amount), + refundFee: Amounts.stringify(denom.fees.feeRefund), + totalRefreshCostBound: Amounts.stringify(totalRefreshCostBound), coinPub: r.coin_pub, rtransactionId: r.rtransaction_id, }; @@ -2241,9 +2248,9 @@ async function storeFailedRefund( type: RefundState.Failed, obtainedTime: TalerProtocolTimestamp.now(), executionTime: r.execution_time, - refundAmount: Amounts.parseOrThrow(r.refund_amount), - refundFee: denom.fees.feeRefund, - totalRefreshCostBound, + refundAmount: Amounts.stringify(r.refund_amount), + refundFee: Amounts.stringify(denom.fees.feeRefund), + totalRefreshCostBound: Amounts.stringify(totalRefreshCostBound), coinPub: r.coin_pub, rtransactionId: r.rtransaction_id, }; @@ -2274,13 +2281,13 @@ async function storeFailedRefund( let contrib: AmountJson | undefined; for (let i = 0; i < payCoinSelection.coinPubs.length; i++) { if (payCoinSelection.coinPubs[i] === r.coin_pub) { - contrib = payCoinSelection.coinContributions[i]; + contrib = Amounts.parseOrThrow(payCoinSelection.coinContributions[i]); } } // FIXME: Is this case tested?! refreshCoinsMap[coin.coinPub] = { coinPub: coin.coinPub, - amount: amountLeft, + amount: Amounts.stringify(amountLeft), }; await tx.coins.put(coin); } @@ -2417,10 +2424,8 @@ async function calculateRefundSummary( p: PurchaseRecord, ): Promise { const download = await expectProposalDownload(ws, p); - let amountRefundGranted = Amounts.getZero( - download.contractData.amount.currency, - ); - let amountRefundGone = Amounts.getZero(download.contractData.amount.currency); + let amountRefundGranted = Amounts.zeroOfAmount(download.contractData.amount); + let amountRefundGone = Amounts.zeroOfAmount(download.contractData.amount); let pendingAtExchange = false; @@ -2454,7 +2459,7 @@ async function calculateRefundSummary( } }); return { - amountEffectivePaid: payInfo.totalPayCost, + amountEffectivePaid: Amounts.parseOrThrow(payInfo.totalPayCost), amountRefundGone, amountRefundGranted, pendingAtExchange, @@ -2598,7 +2603,7 @@ async function queryAndSaveAwaitingRefund( ); if (!orderStatus.refunded) { // Wait for retry ... - return Amounts.getZero(download.contractData.amount.currency); + return Amounts.zeroOfAmount(download.contractData.amount); } const refundAwaiting = Amounts.sub( @@ -2618,7 +2623,7 @@ async function queryAndSaveAwaitingRefund( logger.warn("purchase does not exist anymore"); return; } - p.refundAmountAwaiting = refundAwaiting; + p.refundAmountAwaiting = Amounts.stringify(refundAwaiting); await tx.purchases.put(p); }); } diff --git a/packages/taler-wallet-core/src/operations/pay-peer.ts b/packages/taler-wallet-core/src/operations/pay-peer.ts index 2eb6fe20d..b6acef2dc 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer.ts @@ -158,8 +158,8 @@ export async function selectPeerCoins( } coinInfos.push({ coinPub: coin.coinPub, - feeDeposit: denom.feeDeposit, - value: denom.value, + feeDeposit: Amounts.parseOrThrow(denom.feeDeposit), + value: Amounts.parseOrThrow(denom.value), denomPubHash: denom.denomPubHash, coinPriv: coin.coinPriv, denomSig: coin.denomSig, @@ -175,8 +175,8 @@ export async function selectPeerCoins( -Amounts.cmp(o1.value, o2.value) || strcmp(o1.denomPubHash, o2.denomPubHash), ); - let amountAcc = Amounts.getZero(instructedAmount.currency); - let depositFeesAcc = Amounts.getZero(instructedAmount.currency); + let amountAcc = Amounts.zeroOfCurrency(instructedAmount.currency); + let depositFeesAcc = Amounts.zeroOfCurrency(instructedAmount.currency); const resCoins: { coinPub: string; coinPriv: string; @@ -553,7 +553,7 @@ export async function acceptPeerPushPayment( mergeTimestamp: mergeTimestamp, purseAmount: Amounts.stringify(amount), purseExpiration: contractTerms.purse_expiration, - purseFee: Amounts.stringify(Amounts.getZero(amount.currency)), + purseFee: Amounts.stringify(Amounts.zeroOfCurrency(amount.currency)), pursePub: peerInc.pursePub, reservePayto, reservePriv: mergeReserveInfo.reservePriv, @@ -796,7 +796,7 @@ export async function initiatePeerPullPayment( const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms); const purseFee = Amounts.stringify( - Amounts.getZero(Amounts.parseOrThrow(req.amount).currency), + Amounts.zeroOfCurrency(Amounts.parseOrThrow(req.amount).currency), ); const sigRes = await ws.cryptoApi.signReservePurseCreate({ diff --git a/packages/taler-wallet-core/src/operations/recoup.ts b/packages/taler-wallet-core/src/operations/recoup.ts index e92c805bd..4feb4430d 100644 --- a/packages/taler-wallet-core/src/operations/recoup.ts +++ b/packages/taler-wallet-core/src/operations/recoup.ts @@ -291,7 +291,7 @@ async function recoupRefreshCoin( ).amount; recoupGroup.scheduleRefreshCoins.push({ coinPub: oldCoin.coinPub, - amount: residualAmount, + amount: Amounts.stringify(residualAmount), }); } await tx.coins.put(revokedCoin); diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index ea0fae8bb..c2f0f0360 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -110,7 +110,7 @@ export function getTotalRefreshCost( const denomMap = Object.fromEntries(denoms.map((x) => [x.denomPubHash, x])); const withdrawDenoms = selectWithdrawalDenominations(withdrawAmount, denoms); const resultingAmount = Amounts.add( - Amounts.getZero(withdrawAmount.currency), + Amounts.zeroOfCurrency(withdrawAmount.currency), ...withdrawDenoms.selectedDenoms.map( (d) => Amounts.mult( @@ -273,7 +273,7 @@ async function refreshCreateSession( count: x.count, denomPubHash: x.denomPubHash, })), - amountRefreshOutput: newCoinDenoms.totalCoinValue, + amountRefreshOutput: Amounts.stringify(newCoinDenoms.totalCoinValue), }; await tx.refreshGroups.put(rg); }); @@ -340,7 +340,7 @@ async function refreshMelt( denomPub: newDenom.denomPub, denomPubHash: newDenom.denomPubHash, feeWithdraw: newDenom.feeWithdraw, - value: newDenom.value, + value: Amounts.stringify(newDenom.value), }); } return { newCoinDenoms, oldCoin, oldDenom, refreshGroup, refreshSession }; @@ -368,7 +368,7 @@ async function refreshMelt( meltCoinDenomPubHash: oldCoin.denomPubHash, meltCoinPriv: oldCoin.coinPriv, meltCoinPub: oldCoin.coinPub, - feeRefresh: oldDenom.feeRefresh, + feeRefresh: Amounts.parseOrThrow(oldDenom.feeRefresh), meltCoinMaxAge: oldCoin.maxAge, meltCoinAgeCommitmentProof: oldCoin.ageCommitmentProof, newCoinDenoms, @@ -584,7 +584,7 @@ async function refreshReveal( denomPub: newDenom.denomPub, denomPubHash: newDenom.denomPubHash, feeWithdraw: newDenom.feeWithdraw, - value: newDenom.value, + value: Amounts.stringify(newDenom.value), }); } return { @@ -626,7 +626,7 @@ async function refreshReveal( meltCoinDenomPubHash: oldCoin.denomPubHash, meltCoinPriv: oldCoin.coinPriv, meltCoinPub: oldCoin.coinPub, - feeRefresh: oldDenom.feeRefresh, + feeRefresh: Amounts.parseOrThrow(oldDenom.feeRefresh), newCoinDenoms, meltCoinMaxAge: oldCoin.maxAge, meltCoinAgeCommitmentProof: oldCoin.ageCommitmentProof, @@ -922,10 +922,14 @@ export async function createRefreshGroup( assertUnreachable(coin.status); } const refreshAmount = ocp.amount; - inputPerCoin.push(refreshAmount); + inputPerCoin.push(Amounts.parseOrThrow(refreshAmount)); await tx.coins.put(coin); const denoms = await getDenoms(coin.exchangeBaseUrl); - const cost = getTotalRefreshCost(denoms, denom, refreshAmount); + const cost = getTotalRefreshCost( + denoms, + denom, + Amounts.parseOrThrow(refreshAmount), + ); const output = Amounts.sub(refreshAmount, cost).amount; estimatedOutputPerCoin.push(output); } @@ -934,13 +938,15 @@ export async function createRefreshGroup( operationStatus: RefreshOperationStatus.Pending, timestampFinished: undefined, statusPerCoin: oldCoinPubs.map(() => RefreshCoinStatus.Pending), - lastErrorPerCoin: {}, oldCoinPubs: oldCoinPubs.map((x) => x.coinPub), + lastErrorPerCoin: {}, reason, refreshGroupId, refreshSessionPerCoin: oldCoinPubs.map(() => undefined), - inputPerCoin, - estimatedOutputPerCoin, + inputPerCoin: inputPerCoin.map((x) => Amounts.stringify(x)), + estimatedOutputPerCoin: estimatedOutputPerCoin.map((x) => + Amounts.stringify(x), + ), timestampCreated: TalerProtocolTimestamp.now(), }; @@ -1037,11 +1043,11 @@ export async function autoRefresh( if (AbsoluteTime.isExpired(executeThreshold)) { refreshCoins.push({ coinPub: coin.coinPub, - amount: { + amount: Amounts.stringify({ value: denom.amountVal, fraction: denom.amountFrac, currency: denom.currency, - }, + }), }); } else { const checkThreshold = getAutoRefreshCheckThreshold(denom); diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts index f98d69e26..f9d20fa03 100644 --- a/packages/taler-wallet-core/src/operations/tip.ts +++ b/packages/taler-wallet-core/src/operations/tip.ts @@ -127,13 +127,13 @@ export async function prepareTip( const newTipRecord: TipRecord = { walletTipId: walletTipId, acceptedTimestamp: undefined, - tipAmountRaw: amount, + tipAmountRaw: Amounts.stringify(amount), tipExpiration: tipPickupStatus.expiration, exchangeBaseUrl: tipPickupStatus.exchange_url, merchantBaseUrl: res.merchantBaseUrl, createdTimestamp: TalerProtocolTimestamp.now(), merchantTipId: res.merchantTipId, - tipAmountEffective: selectedDenoms.totalCoinValue, + tipAmountEffective: Amounts.stringify(selectedDenoms.totalCoinValue), denomsSel: selectedDenoms, pickedUpTimestamp: undefined, secretSeed, diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 54cb84926..fd0a343e5 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -26,6 +26,7 @@ import { Logger, OrderShortInfo, PaymentStatus, + PeerContractTerms, RefundInfoShort, TalerProtocolTimestamp, Transaction, @@ -49,6 +50,8 @@ import { WithdrawalGroupRecord, WithdrawalRecordType, WalletContractData, + PeerPushPaymentInitiationStatus, + PeerPullPaymentIncomingStatus, } from "../db.js"; import { InternalWalletState } from "../internal-wallet-state.js"; import { assertUnreachable } from "../util/assertUnreachable.js"; @@ -222,7 +225,7 @@ export async function getTransactionById( const contractData = download.contractData; const refunds = mergeRefundByExecutionTime( cleanRefunds, - Amounts.getZero(contractData.amount.currency), + Amounts.zeroOfAmount(contractData.amount), ); const payOpId = RetryTags.forPay(purchase); @@ -296,7 +299,7 @@ export async function getTransactionById( const contractData = download.contractData; const refunds = mergeRefundByExecutionTime( [theRefund], - Amounts.getZero(contractData.amount.currency), + Amounts.zeroOfAmount(contractData.amount), ); return buildTransactionForRefund( @@ -320,11 +323,13 @@ export async function getTransactionById( } else if (type === TransactionType.PeerPushDebit) { const pursePub = rest[0]; return await ws.db - .mktx((x) => [x.peerPushPaymentInitiations]) + .mktx((x) => [x.peerPushPaymentInitiations, x.contractTerms]) .runReadWrite(async (tx) => { const debit = await tx.peerPushPaymentInitiations.get(pursePub); if (!debit) throw Error("not found"); - return buildTransactionForPushPaymentDebit(debit); + const ct = await tx.contractTerms.get(debit.contractTermsHash); + checkDbInvariant(!!ct); + return buildTransactionForPushPaymentDebit(debit, ct.contractTermsRaw); }); } else { const unknownTxType: never = type; @@ -334,6 +339,7 @@ export async function getTransactionById( function buildTransactionForPushPaymentDebit( pi: PeerPushPaymentInitiationRecord, + contractTerms: PeerContractTerms, ort?: OperationRetryRecord, ): Transaction { return { @@ -342,11 +348,11 @@ function buildTransactionForPushPaymentDebit( amountRaw: pi.amount, exchangeBaseUrl: pi.exchangeBaseUrl, info: { - expiration: pi.contractTerms.purse_expiration, - summary: pi.contractTerms.summary, + expiration: contractTerms.purse_expiration, + summary: contractTerms.summary, }, frozen: false, - pending: !pi.purseCreated, + pending: pi.status != PeerPushPaymentInitiationStatus.PurseCreated, timestamp: pi.timestampCreated, talerUri: constructPayPushUri({ exchangeBaseUrl: pi.exchangeBaseUrl, @@ -586,7 +592,7 @@ function mergeRefundByExecutionTime( prev.set(key, { executionTime: refund.executionTime, amountAppliedEffective: effective, - amountAppliedRaw: raw, + amountAppliedRaw: Amounts.parseOrThrow(raw), firstTimestamp: refund.obtainedTime, }); } else { @@ -659,7 +665,7 @@ async function buildTransactionForPurchase( refundsInfo: MergedRefundInfo[], ort?: OperationRetryRecord, ): Promise { - const zero = Amounts.getZero(contractData.amount.currency); + const zero = Amounts.zeroOfAmount(contractData.amount); const info: OrderShortInfo = { merchant: contractData.merchant, @@ -769,7 +775,11 @@ export async function getTransactions( if (shouldSkipSearch(transactionsRequest, [])) { return; } - transactions.push(buildTransactionForPushPaymentDebit(pi)); + const ct = await tx.contractTerms.get(pi.contractTermsHash); + checkDbInvariant(!!ct); + transactions.push( + buildTransactionForPushPaymentDebit(pi, ct.contractTermsRaw), + ); }); tx.peerPullPaymentIncoming.iter().forEachAsync(async (pi) => { @@ -780,7 +790,10 @@ export async function getTransactions( if (shouldSkipSearch(transactionsRequest, [])) { return; } - if (!pi.accepted) { + if ( + pi.status !== PeerPullPaymentIncomingStatus.Accepted && + pi.status !== PeerPullPaymentIncomingStatus.Paid + ) { return; } @@ -791,7 +804,7 @@ export async function getTransactions( if ( shouldSkipCurrency( transactionsRequest, - wsr.rawWithdrawalAmount.currency, + Amounts.currencyOf(wsr.rawWithdrawalAmount), ) ) { return; @@ -899,7 +912,7 @@ export async function getTransactions( const refunds = mergeRefundByExecutionTime( cleanRefunds, - Amounts.getZero(download.currency), + Amounts.zeroOfCurrency(download.currency), ); refunds.forEach(async (refundInfo) => { @@ -929,7 +942,7 @@ export async function getTransactions( if ( shouldSkipCurrency( transactionsRequest, - tipRecord.tipAmountRaw.currency, + Amounts.parseOrThrow(tipRecord.tipAmountRaw).currency, ) ) { return; diff --git a/packages/taler-wallet-core/src/operations/withdraw.test.ts b/packages/taler-wallet-core/src/operations/withdraw.test.ts index 70b4f73c0..c77f75b9d 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.test.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.test.ts @@ -39,26 +39,26 @@ test("withdrawal selection bug repro", (t) => { exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeMasterPub: "", fees: { - feeDeposit: { + feeDeposit: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeRefresh: { + }), + feeRefresh: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeRefund: { + }), + feeRefund: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeWithdraw: { + }), + feeWithdraw: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, + }), }, isOffered: true, isRevoked: false, @@ -95,26 +95,26 @@ test("withdrawal selection bug repro", (t) => { exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeMasterPub: "", fees: { - feeDeposit: { + feeDeposit: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeRefresh: { + }), + feeRefresh: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeRefund: { + }), + feeRefund: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeWithdraw: { + }), + feeWithdraw: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, + }), }, isOffered: true, isRevoked: false, @@ -150,26 +150,26 @@ test("withdrawal selection bug repro", (t) => { exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeMasterPub: "", fees: { - feeDeposit: { + feeDeposit: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeRefresh: { + }), + feeRefresh: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeRefund: { + }), + feeRefund: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeWithdraw: { + }), + feeWithdraw: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, + }), }, isOffered: true, isRevoked: false, @@ -206,26 +206,26 @@ test("withdrawal selection bug repro", (t) => { exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeMasterPub: "", fees: { - feeDeposit: { + feeDeposit: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeRefresh: { + }), + feeRefresh: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeRefund: { + }), + feeRefund: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeWithdraw: { + }), + feeWithdraw: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, + }), }, isOffered: true, isRevoked: false, @@ -261,26 +261,26 @@ test("withdrawal selection bug repro", (t) => { exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeMasterPub: "", fees: { - feeDeposit: { + feeDeposit: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeRefresh: { + }), + feeRefresh: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeRefund: { + }), + feeRefund: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeWithdraw: { + }), + feeWithdraw: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, + }), }, isOffered: true, isRevoked: false, @@ -316,26 +316,26 @@ test("withdrawal selection bug repro", (t) => { exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeMasterPub: "", fees: { - feeDeposit: { + feeDeposit: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeRefresh: { + }), + feeRefresh: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeRefund: { + }), + feeRefund: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, - feeWithdraw: { + }), + feeWithdraw: Amounts.stringify({ currency: "KUDOS", fraction: 1000000, value: 0, - }, + }), }, isOffered: true, isRevoked: false, diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index a9ecdf369..76bbec416 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -167,8 +167,8 @@ export function selectWithdrawalDenominations( denomPubHash: string; }[] = []; - let totalCoinValue = Amounts.getZero(amountAvailable.currency); - let totalWithdrawCost = Amounts.getZero(amountAvailable.currency); + let totalCoinValue = Amounts.zeroOfCurrency(amountAvailable.currency); + let totalWithdrawCost = Amounts.zeroOfCurrency(amountAvailable.currency); denoms = denoms.filter(isWithdrawableDenom); denoms.sort((d1, d2) => @@ -223,8 +223,8 @@ export function selectWithdrawalDenominations( return { selectedDenoms, - totalCoinValue, - totalWithdrawCost, + totalCoinValue: Amounts.stringify(totalCoinValue), + totalWithdrawCost: Amounts.stringify(totalCoinValue), }; } @@ -238,8 +238,8 @@ export function selectForcedWithdrawalDenominations( denomPubHash: string; }[] = []; - let totalCoinValue = Amounts.getZero(amountAvailable.currency); - let totalWithdrawCost = Amounts.getZero(amountAvailable.currency); + let totalCoinValue = Amounts.zeroOfCurrency(amountAvailable.currency); + let totalWithdrawCost = Amounts.zeroOfCurrency(amountAvailable.currency); denoms = denoms.filter(isWithdrawableDenom); denoms.sort((d1, d2) => @@ -279,8 +279,8 @@ export function selectForcedWithdrawalDenominations( return { selectedDenoms, - totalCoinValue, - totalWithdrawCost, + totalCoinValue: Amounts.stringify(totalCoinValue), + totalWithdrawCost: Amounts.stringify(totalWithdrawCost), }; } @@ -416,10 +416,10 @@ async function processPlanchetGenerate( checkDbInvariant(!!denom); const r = await ws.cryptoApi.createPlanchet({ denomPub: denom.denomPub, - feeWithdraw: denom.feeWithdraw, + feeWithdraw: Amounts.parseOrThrow(denom.feeWithdraw), reservePriv: withdrawalGroup.reservePriv, reservePub: withdrawalGroup.reservePub, - value: denom.value, + value: Amounts.parseOrThrow(denom.value), coinIndex: coinIdx, secretSeed: withdrawalGroup.secretSeed, restrictAge: withdrawalGroup.restrictAge, @@ -950,7 +950,7 @@ async function queryReserve( return; } wg.status = WithdrawalGroupStatus.Ready; - wg.reserveBalanceAmount = Amounts.parse(result.response.balance); + wg.reserveBalanceAmount = Amounts.stringify(result.response.balance); await tx.withdrawalGroups.put(wg); }); @@ -1427,7 +1427,7 @@ export async function getFundingPaytoUrisTx( export function augmentPaytoUrisForWithdrawal( plainPaytoUris: string[], reservePub: string, - instructedAmount: AmountJson, + instructedAmount: AmountLike, ): string[] { return plainPaytoUris.map((x) => addPaytoQueryParams(x, { @@ -1732,7 +1732,7 @@ export async function internalCreateWithdrawalGroup( denomSelUid, denomsSel: initialDenomSel, exchangeBaseUrl: canonExchange, - instructedAmount: amount, + instructedAmount: Amounts.stringify(amount), timestampStart: now, rawWithdrawalAmount: initialDenomSel.totalWithdrawCost, effectiveWithdrawalAmount: initialDenomSel.totalCoinValue, diff --git a/packages/taler-wallet-core/src/util/coinSelection.ts b/packages/taler-wallet-core/src/util/coinSelection.ts index 12f87a920..cadf8d829 100644 --- a/packages/taler-wallet-core/src/util/coinSelection.ts +++ b/packages/taler-wallet-core/src/util/coinSelection.ts @@ -139,7 +139,7 @@ export function tallyFees( if (!tally.wireFeeCoveredForExchange.has(exchangeBaseUrl)) { const wf = - wireFeesPerExchange[exchangeBaseUrl] ?? Amounts.getZero(currency); + wireFeesPerExchange[exchangeBaseUrl] ?? Amounts.zeroOfCurrency(currency); const wfForgiven = Amounts.min(amountWireFeeLimitRemaining, wf); amountWireFeeLimitRemaining = Amounts.sub( amountWireFeeLimitRemaining, diff --git a/packages/taler-wallet-core/src/util/denominations.test.ts b/packages/taler-wallet-core/src/util/denominations.test.ts index 9c93331a3..551e06a33 100644 --- a/packages/taler-wallet-core/src/util/denominations.test.ts +++ b/packages/taler-wallet-core/src/util/denominations.test.ts @@ -25,6 +25,7 @@ import { FeeDescriptionPair, Amounts, DenominationInfo, + AmountString, } from "@gnu-taler/taler-util"; // import { expect } from "chai"; import { @@ -37,8 +38,8 @@ import test, { ExecutionContext } from "ava"; /** * Create some constants to be used as reference in the tests */ -const VALUES = Array.from({ length: 10 }).map((undef, t) => - Amounts.parseOrThrow(`USD:${t}`), +const VALUES: AmountString[] = Array.from({ length: 10 }).map( + (undef, t) => `USD:${t}`, ); const TIMESTAMPS = Array.from({ length: 20 }).map((undef, t_s) => ({ t_s })); const ABS_TIME = TIMESTAMPS.map((m) => AbsoluteTime.fromTimestamp(m)); diff --git a/packages/taler-wallet-core/src/util/denominations.ts b/packages/taler-wallet-core/src/util/denominations.ts index 9cd931acd..c05df6c6e 100644 --- a/packages/taler-wallet-core/src/util/denominations.ts +++ b/packages/taler-wallet-core/src/util/denominations.ts @@ -18,6 +18,7 @@ import { AbsoluteTime, AmountJson, Amounts, + AmountString, DenominationInfo, FeeDescription, FeeDescriptionPair, @@ -51,7 +52,7 @@ export function selectBestForOverlappingDenominations< return minDeposit; } -export function selectMinimumFee( +export function selectMinimumFee( list: T[], ): T | undefined { let minFee: T | undefined = undefined; @@ -285,7 +286,7 @@ export function createTimeline( idProp: PropsWithReturnType, periodStartProp: PropsWithReturnType, periodEndProp: PropsWithReturnType, - feeProp: PropsWithReturnType, + feeProp: PropsWithReturnType, groupProp: PropsWithReturnType | undefined, selectBestForOverlapping: (l: Type[]) => Type | undefined, ): FeeDescription[] { @@ -312,7 +313,7 @@ export function createTimeline( } ps.push({ type: "start", - fee, + fee: Amounts.stringify(fee), group, id, moment: AbsoluteTime.fromTimestamp(stampStart), @@ -320,7 +321,7 @@ export function createTimeline( }); ps.push({ type: "end", - fee, + fee: Amounts.stringify(fee), group, id, moment: AbsoluteTime.fromTimestamp(stampEnd), @@ -416,7 +417,7 @@ export function createTimeline( group: cursor.group, from: cursor.moment, until: AbsoluteTime.never(), //not yet known - fee: currentFee, + fee: Amounts.stringify(currentFee), }); } else { prev.until = cursor.moment; diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index f800b68f8..9339b2f8e 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -771,7 +771,7 @@ async function getExchangeDetailedInfo( const feesByGroup = [ ...infoForType.map((w) => ({ ...w, - fee: w.closingFee, + fee: Amounts.stringify(w.closingFee), group: "closing", })), ...infoForType.map((w) => ({ ...w, fee: w.wireFee, group: "wire" })), -- cgit v1.2.3