taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit f401ab076bb037e46c4b12e371dfe13a777bf582
parent c93889d3c84f2019a58b078eeea86ab523d16139
Author: Florian Dold <florian@dold.me>
Date:   Wed, 11 Sep 2024 16:29:50 +0200

wallet-core: support restricting scope for peer push payments

Diffstat:
Mpackages/taler-util/src/types-taler-wallet.ts | 13+++++++++++++
Mpackages/taler-wallet-core/src/coinSelection.ts | 22++++++++++++++++++++++
Mpackages/taler-wallet-core/src/db.ts | 7+++++++
Mpackages/taler-wallet-core/src/exchanges.ts | 11+++++++++++
Mpackages/taler-wallet-core/src/pay-peer-push-debit.ts | 7++++++-
5 files changed, 59 insertions(+), 1 deletion(-)

diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts @@ -2750,6 +2750,12 @@ export interface CheckPeerPushDebitRequest { amount: AmountString; /** + * Restrict the scope of funds that can be spent via the given + * scope info. + */ + restrictScope?: ScopeInfo; + + /** * ID provided by the client to cancel the request. * * If the same request is made again with the same clientCancellationId, @@ -2789,6 +2795,13 @@ export interface CheckPeerPushDebitResponse { export interface InitiatePeerPushDebitRequest { exchangeBaseUrl?: string; + + /** + * Restrict the scope of funds that can be spent via the given + * scope info. + */ + restrictScope?: ScopeInfo; + partialContractTerms: PeerContractTerms; } diff --git a/packages/taler-wallet-core/src/coinSelection.ts b/packages/taler-wallet-core/src/coinSelection.ts @@ -46,6 +46,8 @@ import { PayCoinSelection, PaymentInsufficientBalanceDetails, ProspectivePayCoinSelection, + ScopeInfo, + ScopeType, SelectedCoin, SelectedProspectiveCoin, strcmp, @@ -55,6 +57,7 @@ import { getPaymentBalanceDetailsInTx } from "./balance.js"; import { getAutoRefreshExecuteThreshold } from "./common.js"; import { DenominationRecord, WalletDbReadOnlyTransaction } from "./db.js"; import { + checkExchangeInScope, ExchangeWireDetails, getExchangeWireDetailsInTx, } from "./exchanges.js"; @@ -993,6 +996,12 @@ export interface PeerCoinSelectionRequest { instructedAmount: AmountJson; /** + * Restrict the scope of funds that can be spent via the given + * scope info. + */ + restrictScope?: ScopeInfo; + + /** * Instruct the coin selection to repair this coin * selection instead of selecting completely new coins. */ @@ -1165,6 +1174,19 @@ export async function selectPeerCoinsInTx( if (!exchWire) { continue; } + const isInScope = req.restrictScope + ? await checkExchangeInScope(wex, exch.baseUrl, req.restrictScope) + : true; + if (!isInScope) { + continue; + } + if ( + req.restrictScope && + req.restrictScope.type === ScopeType.Exchange && + req.restrictScope.url !== exch.baseUrl + ) { + continue; + } const globalFees = getGlobalFees(exchWire); if (!globalFees) { continue; diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts @@ -1898,6 +1898,13 @@ export interface PeerPushDebitRecord { exchangeBaseUrl: string; /** + * Restricted scope for this transaction. + * + * Relevant for coin reselection. + */ + restrictScope?: ScopeInfo; + + /** * Instructed amount. */ amount: AmountString; diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts @@ -3571,3 +3571,14 @@ export async function processExchangeKyc( ); } } + +export async function checkExchangeInScope( + wex: WalletExecutionContext, + exchangeBaseUrl: string, + scope: ScopeInfo, +): Promise<boolean> { + if (scope.type === ScopeType.Exchange && scope.url !== exchangeBaseUrl) { + return false; + } + return true; +} diff --git a/packages/taler-wallet-core/src/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/pay-peer-push-debit.ts @@ -80,6 +80,7 @@ import { timestampProtocolFromDb, timestampProtocolToDb, } from "./db.js"; +import { getScopeForAllExchanges } from "./exchanges.js"; import { codecForExchangePurseStatus, getTotalPeerPaymentCost, @@ -93,7 +94,6 @@ import { notifyTransition, } from "./transactions.js"; import { WalletExecutionContext } from "./wallet.js"; -import { getScopeForAllExchanges } from "./exchanges.js"; const logger = new Logger("pay-peer-push-debit.ts"); @@ -455,6 +455,7 @@ async function internalCheckPeerPushDebit( ); const coinSelRes = await selectPeerCoins(wex, { instructedAmount, + restrictScope: req.restrictScope, }); let coins: SelectedProspectiveCoin[] | undefined = undefined; switch (coinSelRes.type) { @@ -528,6 +529,7 @@ async function handlePurseCreationConflict( const coinSelRes = await selectPeerCoins(wex, { instructedAmount, + restrictScope: peerPushInitiation.restrictScope, repair, }); @@ -599,6 +601,7 @@ async function processPeerPushDebitCreateReserve( if (!peerPushInitiation.coinSel) { const coinSelRes = await selectPeerCoins(wex, { instructedAmount: Amounts.parseOrThrow(peerPushInitiation.amount), + restrictScope: peerPushInitiation.restrictScope, }); switch (coinSelRes.type) { @@ -1228,6 +1231,7 @@ export async function initiatePeerPushDebit( async (tx) => { const coinSelRes = await selectPeerCoinsInTx(wex, tx, { instructedAmount, + restrictScope: req.restrictScope, }); let coins: SelectedProspectiveCoin[] | undefined = undefined; @@ -1255,6 +1259,7 @@ export async function initiatePeerPushDebit( const totalAmount = await getTotalPeerPaymentCostInTx(wex, tx, coins); const ppi: PeerPushDebitRecord = { amount: Amounts.stringify(instructedAmount), + restrictScope: req.restrictScope, contractPriv: contractKeyPair.priv, contractPub: contractKeyPair.pub, contractTermsHash: hContractTerms,