diff options
author | Florian Dold <florian@dold.me> | 2024-03-07 11:33:41 +0100 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2024-03-07 11:33:41 +0100 |
commit | 5ca3c7878da9a495c67ea7faed91c8dbce04e603 (patch) | |
tree | 2a194e94bba1415f4504f119bd3beb0ad33d4c81 /packages | |
parent | 466e2b7643692aa6b7f76a193b84775008e17350 (diff) | |
download | wallet-core-5ca3c7878da9a495c67ea7faed91c8dbce04e603.tar.xz |
wallet-core: simplify/improve insufficient balance details computation
Diffstat (limited to 'packages')
-rw-r--r-- | packages/taler-util/src/wallet-types.ts | 4 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/balance.ts | 167 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/coinSelection.ts | 10 | ||||
-rw-r--r-- | packages/taler-wallet-webextension/src/cta/Payment/stories.tsx | 6 |
4 files changed, 76 insertions, 111 deletions
diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts index 8be8fc4a0..cfbed04dc 100644 --- a/packages/taler-util/src/wallet-types.ts +++ b/packages/taler-util/src/wallet-types.ts @@ -882,6 +882,10 @@ export interface PaymentInsufficientBalanceDetails { balanceAvailable: AmountString; balanceMaterial: AmountString; balanceExchangeDepositable: AmountString; + balanceAgeAcceptable: AmountString; + balanceMerchantAcceptable: AmountString; + balanceMerchantDepositable: AmountString; + maxEffectiveSpendAmount: AmountString; }; }; } diff --git a/packages/taler-wallet-core/src/balance.ts b/packages/taler-wallet-core/src/balance.ts index 4c58814a1..26d348b39 100644 --- a/packages/taler-wallet-core/src/balance.ts +++ b/packages/taler-wallet-core/src/balance.ts @@ -59,8 +59,6 @@ import { assertUnreachable, BalanceFlag, BalancesResponse, - canonicalizeBaseUrl, - checkLogicInvariant, GetBalanceDetailRequest, Logger, parsePaytoUri, @@ -487,100 +485,6 @@ export interface AcceptableExchanges { depositableExchanges: string[]; } -/** - * Get all exchanges that are acceptable for a particular payment. - */ -async function getAcceptableExchangeBaseUrlsInTx( - wex: WalletExecutionContext, - tx: WalletDbReadOnlyTransaction<["exchanges", "exchangeDetails"]>, - req: PaymentRestrictionsForBalance, -): Promise<AcceptableExchanges> { - const acceptableExchangeUrls = new Set<string>(); - const depositableExchangeUrls = new Set<string>(); - // FIXME: We should have a DB index to look up all exchanges - // for a particular auditor ... - - const canonExchanges = new Set<string>(); - const canonAuditors = new Set<string>(); - - if (req.restrictExchanges) { - for (const exchangeHandle of req.restrictExchanges.exchanges) { - const normUrl = canonicalizeBaseUrl(exchangeHandle.exchangeBaseUrl); - canonExchanges.add(normUrl); - } - - for (const auditorHandle of req.restrictExchanges.auditors) { - const normUrl = canonicalizeBaseUrl(auditorHandle.auditorBaseUrl); - canonAuditors.add(normUrl); - } - } - - await tx.exchanges.iter().forEachAsync(async (exchange) => { - const dp = exchange.detailsPointer; - if (!dp) { - return; - } - const { currency, masterPublicKey } = dp; - const exchangeDetails = await tx.exchangeDetails.indexes.byPointer.get([ - exchange.baseUrl, - currency, - masterPublicKey, - ]); - if (!exchangeDetails) { - return; - } - - let acceptable = false; - - if (canonExchanges.has(exchange.baseUrl)) { - acceptableExchangeUrls.add(exchange.baseUrl); - acceptable = true; - } - for (const exchangeAuditor of exchangeDetails.auditors) { - if (canonAuditors.has(exchangeAuditor.auditor_url)) { - acceptableExchangeUrls.add(exchange.baseUrl); - acceptable = true; - break; - } - } - - if (!acceptable) { - return; - } - // FIXME: Also consider exchange and auditor public key - // instead of just base URLs? - - let wireMethodSupported = false; - - if (req.restrictWireMethods) { - for (const acc of exchangeDetails.wireInfo.accounts) { - const pp = parsePaytoUri(acc.payto_uri); - checkLogicInvariant(!!pp); - for (const wm of req.restrictWireMethods) { - if (pp.targetType === wm) { - wireMethodSupported = true; - break; - } - if (wireMethodSupported) { - break; - } - } - } - } else { - wireMethodSupported = true; - } - - acceptableExchangeUrls.add(exchange.baseUrl); - if (wireMethodSupported) { - depositableExchangeUrls.add(exchange.baseUrl); - } - }); - return { - acceptableExchanges: [...acceptableExchangeUrls], - depositableExchanges: [...depositableExchangeUrls], - }; -} - export interface PaymentBalanceDetails { /** * Balance of type "available" (see balance.ts for definition). @@ -648,8 +552,6 @@ export async function getPaymentBalanceDetailsInTx( >, req: PaymentRestrictionsForBalance, ): Promise<PaymentBalanceDetails> { - const acceptability = await getAcceptableExchangeBaseUrlsInTx(wex, tx, req); - const d: PaymentBalanceDetails = { balanceAvailable: Amounts.zeroOfCurrency(req.currency), balanceMaterial: Amounts.zeroOfCurrency(req.currency), @@ -691,38 +593,64 @@ export async function getPaymentBalanceDetailsInTx( ca.freshCoinCount, ).amount; - d.maxEffectiveSpendAmount = Amounts.add( - d.maxEffectiveSpendAmount, - Amounts.mult(ca.value, ca.freshCoinCount).amount, - ).amount; - - d.maxEffectiveSpendAmount = Amounts.sub( - d.maxEffectiveSpendAmount, - Amounts.mult(denom.feeDeposit, ca.freshCoinCount).amount, - ).amount; - let wireOkay = false; if (req.restrictWireMethods == null) { wireOkay = true; } else { for (const wm of req.restrictWireMethods) { const wmf = findMatchingWire(wm, req.depositPaytoUri, wireDetails); + if (wmf) { + wireOkay = true; + break; + } + } + } + + if (wireOkay) { + d.balanceExchangeDepositable = Amounts.add( + d.balanceExchangeDepositable, + coinAmount, + ).amount; + } + + let ageOkay = ca.maxAge === 0 || ca.maxAge > req.minAge; + + let merchantExchangeAcceptable = false; + + if (!req.restrictExchanges) { + merchantExchangeAcceptable = true; + } else { + for (const ex of req.restrictExchanges.exchanges) { + if (ex.exchangeBaseUrl === ca.exchangeBaseUrl) { + merchantExchangeAcceptable = true; + break; + } + } + for (const acceptedAuditor of req.restrictExchanges.auditors) { + for (const exchangeAuditor of wireDetails.auditors) { + if (acceptedAuditor.auditorBaseUrl === exchangeAuditor.auditor_url) { + merchantExchangeAcceptable = true; + break; + } + } } } + const merchantExchangeDepositable = merchantExchangeAcceptable && wireOkay; + d.balanceAvailable = Amounts.add(d.balanceAvailable, coinAmount).amount; d.balanceMaterial = Amounts.add(d.balanceMaterial, coinAmount).amount; - if (ca.maxAge === 0 || ca.maxAge > req.minAge) { + if (ageOkay) { d.balanceAgeAcceptable = Amounts.add( d.balanceAgeAcceptable, coinAmount, ).amount; - if (acceptability.acceptableExchanges.includes(ca.exchangeBaseUrl)) { + if (merchantExchangeAcceptable) { d.balanceMerchantAcceptable = Amounts.add( d.balanceMerchantAcceptable, coinAmount, ).amount; - if (acceptability.depositableExchanges.includes(ca.exchangeBaseUrl)) { + if (merchantExchangeDepositable) { d.balanceMerchantDepositable = Amounts.add( d.balanceMerchantDepositable, coinAmount, @@ -730,6 +658,23 @@ export async function getPaymentBalanceDetailsInTx( } } } + + if ( + ageOkay && + wireOkay && + merchantExchangeAcceptable && + merchantExchangeDepositable + ) { + d.maxEffectiveSpendAmount = Amounts.add( + d.maxEffectiveSpendAmount, + Amounts.mult(ca.value, ca.freshCoinCount).amount, + ).amount; + + d.maxEffectiveSpendAmount = Amounts.sub( + d.maxEffectiveSpendAmount, + Amounts.mult(denom.feeDeposit, ca.freshCoinCount).amount, + ).amount; + } } await tx.refreshGroups.iter().forEach((r) => { diff --git a/packages/taler-wallet-core/src/coinSelection.ts b/packages/taler-wallet-core/src/coinSelection.ts index 695be79ac..87b29a998 100644 --- a/packages/taler-wallet-core/src/coinSelection.ts +++ b/packages/taler-wallet-core/src/coinSelection.ts @@ -425,6 +425,16 @@ export async function reportInsufficientBalanceDetails( balanceExchangeDepositable: Amounts.stringify( exchDet.balanceExchangeDepositable, ), + balanceAgeAcceptable: Amounts.stringify(exchDet.balanceAgeAcceptable), + balanceMerchantAcceptable: Amounts.stringify( + exchDet.balanceMerchantAcceptable, + ), + balanceMerchantDepositable: Amounts.stringify( + exchDet.balanceMerchantDepositable, + ), + maxEffectiveSpendAmount: Amounts.stringify( + exchDet.maxEffectiveSpendAmount, + ), }; } diff --git a/packages/taler-wallet-webextension/src/cta/Payment/stories.tsx b/packages/taler-wallet-webextension/src/cta/Payment/stories.tsx index 567b5c177..93b96c836 100644 --- a/packages/taler-wallet-webextension/src/cta/Payment/stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Payment/stories.tsx @@ -60,6 +60,7 @@ export const NoEnoughBalanceAvailable = tests.createExample(BaseView, { balanceMerchantAcceptable: "USD:9" as AmountString, balanceMerchantDepositable: "USD:9" as AmountString, maxEffectiveSpendAmount: "USD:9.5" as AmountString, + balanceExchangeDepositable: "USD:9.5" as AmountString, perExchange: {}, }, talerUri: "taler://pay/..", @@ -101,6 +102,7 @@ export const NoEnoughBalanceMaterial = tests.createExample(BaseView, { balanceMerchantAcceptable: "USD:9" as AmountString, balanceMerchantDepositable: "USD:0" as AmountString, maxEffectiveSpendAmount: "USD:9.5" as AmountString, + balanceExchangeDepositable: "USD:9.5" as AmountString, perExchange: {}, }, talerUri: "taler://pay/..", @@ -142,6 +144,7 @@ export const NoEnoughBalanceAgeAcceptable = tests.createExample(BaseView, { balanceMerchantAcceptable: "USD:9" as AmountString, balanceMerchantDepositable: "USD:9" as AmountString, maxEffectiveSpendAmount: "USD:9.5" as AmountString, + balanceExchangeDepositable: "USD:9.5" as AmountString, perExchange: {}, }, talerUri: "taler://pay/..", @@ -184,6 +187,7 @@ export const NoEnoughBalanceMerchantAcceptable = tests.createExample(BaseView, { balanceMerchantAcceptable: "USD:9" as AmountString, balanceMerchantDepositable: "USD:9" as AmountString, maxEffectiveSpendAmount: "USD:9.5" as AmountString, + balanceExchangeDepositable: "USD:9.5" as AmountString, perExchange: {}, }, talerUri: "taler://pay/..", @@ -227,6 +231,7 @@ export const NoEnoughBalanceMerchantDepositable = tests.createExample( balanceMerchantAcceptable: "USD:10" as AmountString, balanceMerchantDepositable: "USD:9" as AmountString, maxEffectiveSpendAmount: "USD:9.5" as AmountString, + balanceExchangeDepositable: "USD:9.5" as AmountString, perExchange: {}, }, talerUri: "taler://pay/..", @@ -269,6 +274,7 @@ export const NoEnoughBalanceFeeGap = tests.createExample(BaseView, { balanceMerchantAcceptable: "USD:10" as AmountString, balanceMerchantDepositable: "USD:10" as AmountString, maxEffectiveSpendAmount: "USD:9.5" as AmountString, + balanceExchangeDepositable: "USD:9.5" as AmountString, perExchange: {}, }, talerUri: "taler://pay/..", |