diff options
author | Florian Dold <florian@dold.me> | 2022-03-29 21:21:57 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2022-03-29 21:22:03 +0200 |
commit | bbd6ccf1c7c0baea44234863967e640f5cb10a3a (patch) | |
tree | 219086b57e229b79e674619180200284762c3f4b /packages/taler-wallet-core/src/operations/withdraw.ts | |
parent | fdd272af203d7048f980a2f2b5d405e5c94ebec2 (diff) | |
download | wallet-core-bbd6ccf1c7c0baea44234863967e640f5cb10a3a.tar.xz |
wallet: allow forced denom selection for tests
Diffstat (limited to 'packages/taler-wallet-core/src/operations/withdraw.ts')
-rw-r--r-- | packages/taler-wallet-core/src/operations/withdraw.ts | 114 |
1 files changed, 73 insertions, 41 deletions
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index e7dcd0784..6d45599dc 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -31,6 +31,7 @@ import { durationFromSpec, ExchangeListItem, ExchangeWithdrawRequest, + ForcedDenomSel, LibtoolVersion, Logger, NotificationType, @@ -68,6 +69,7 @@ import { HttpRequestLibrary, readSuccessResponseJsonOrThrow, } from "../util/http.js"; +import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js"; import { resetRetryInfo, RetryInfo, @@ -85,21 +87,6 @@ import { guardOperationException } from "./common.js"; const logger = new Logger("operations/withdraw.ts"); /** - * FIXME: Eliminate this in favor of DenomSelectionState. - */ -interface DenominationSelectionInfo { - totalCoinValue: AmountJson; - totalWithdrawCost: AmountJson; - selectedDenoms: { - /** - * How many times do we withdraw this denomination? - */ - count: number; - denom: DenominationRecord; - }[]; -} - -/** * Information about what will happen when creating a reserve. * * Sent to the wallet frontend to be rendered and shown to the user. @@ -122,7 +109,7 @@ export interface ExchangeWithdrawDetails { /** * Selected denominations for withdraw. */ - selectedDenoms: DenominationSelectionInfo; + selectedDenoms: DenomSelectionState; /** * Does the wallet know about an auditor for @@ -213,12 +200,12 @@ export function isWithdrawableDenom(d: DenominationRecord): boolean { export function selectWithdrawalDenominations( amountAvailable: AmountJson, denoms: DenominationRecord[], -): DenominationSelectionInfo { +): DenomSelectionState { let remaining = Amounts.copy(amountAvailable); const selectedDenoms: { count: number; - denom: DenominationRecord; + denomPubHash: string; }[] = []; let totalCoinValue = Amounts.getZero(amountAvailable.currency); @@ -248,7 +235,7 @@ export function selectWithdrawalDenominations( ).amount; selectedDenoms.push({ count, - denom: d, + denomPubHash: d.denomPubHash, }); } @@ -262,9 +249,7 @@ export function selectWithdrawalDenominations( `selected withdrawal denoms for ${Amounts.stringify(totalCoinValue)}`, ); for (const sd of selectedDenoms) { - logger.trace( - `denom_pub_hash=${sd.denom.denomPubHash}, count=${sd.count}`, - ); + logger.trace(`denom_pub_hash=${sd.denomPubHash}, count=${sd.count}`); } logger.trace("(end of withdrawal denom list)"); } @@ -276,6 +261,56 @@ export function selectWithdrawalDenominations( }; } +export function selectForcedWithdrawalDenominations( + amountAvailable: AmountJson, + denoms: DenominationRecord[], + forcedDenomSel: ForcedDenomSel, +): DenomSelectionState { + let remaining = Amounts.copy(amountAvailable); + + const selectedDenoms: { + count: number; + denomPubHash: string; + }[] = []; + + let totalCoinValue = Amounts.getZero(amountAvailable.currency); + let totalWithdrawCost = Amounts.getZero(amountAvailable.currency); + + denoms = denoms.filter(isWithdrawableDenom); + denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value)); + + for (const fds of forcedDenomSel.denoms) { + const count = fds.count; + const denom = denoms.find((x) => { + return Amounts.cmp(x.value, fds.value) == 0; + }); + if (!denom) { + throw Error( + `unable to find denom for forced selection (value ${fds.value})`, + ); + } + const cost = Amounts.add(denom.value, denom.feeWithdraw).amount; + totalCoinValue = Amounts.add( + totalCoinValue, + Amounts.mult(denom.value, count).amount, + ).amount; + totalWithdrawCost = Amounts.add( + totalWithdrawCost, + Amounts.mult(cost, count).amount, + ).amount; + selectedDenoms.push({ + count, + denomPubHash: denom.denomPubHash, + }); + } + + return { + selectedDenoms, + totalCoinValue, + totalWithdrawCost, + }; +} + /** * Get information about a withdrawal from * a taler://withdraw URI by asking the bank. @@ -695,21 +730,6 @@ async function processPlanchetVerifyAndStoreCoin( } } -export function denomSelectionInfoToState( - dsi: DenominationSelectionInfo, -): DenomSelectionState { - return { - selectedDenoms: dsi.selectedDenoms.map((x) => { - return { - count: x.count, - denomPubHash: x.denom.denomPubHash, - }; - }), - totalCoinValue: dsi.totalCoinValue, - totalWithdrawCost: dsi.totalWithdrawCost, - }; -} - /** * Make sure that denominations that currently can be used for withdrawal * are validated, and the result of validation is stored in the database. @@ -1006,11 +1026,21 @@ export async function getExchangeWithdrawalInfo( exchange, ); - let earliestDepositExpiration = - selectedDenoms.selectedDenoms[0].denom.stampExpireDeposit; + let earliestDepositExpiration: TalerProtocolTimestamp | undefined; for (let i = 1; i < selectedDenoms.selectedDenoms.length; i++) { - const expireDeposit = - selectedDenoms.selectedDenoms[i].denom.stampExpireDeposit; + const ds = selectedDenoms.selectedDenoms[i]; + // FIXME: Do in one transaction! + const denom = await ws.db + .mktx((x) => ({ denominations: x.denominations })) + .runReadOnly(async (tx) => { + return ws.getDenomInfo(ws, tx, exchangeBaseUrl, ds.denomPubHash); + }); + checkDbInvariant(!!denom); + const expireDeposit = denom.stampExpireDeposit; + if (!earliestDepositExpiration) { + earliestDepositExpiration = expireDeposit; + continue; + } if ( AbsoluteTime.cmp( AbsoluteTime.fromTimestamp(expireDeposit), @@ -1021,6 +1051,8 @@ export async function getExchangeWithdrawalInfo( } } + checkLogicInvariant(!!earliestDepositExpiration); + const possibleDenoms = await ws.db .mktx((x) => ({ denominations: x.denominations })) .runReadOnly(async (tx) => { |