taler-typescript-core

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

commit 544469d6337952ba8f02b5773dc87cbcc24df51a
parent 3d4d25ed08ba4d50ff3de1f3bf6c8ae727fbdd6c
Author: Florian Dold <florian@dold.me>
Date:   Wed, 11 Dec 2024 17:23:01 +0100

wallet-core: support restrictScope in check/create deposit

Diffstat:
Mpackages/taler-util/src/types-taler-wallet.ts | 29+++++++++++++++++++++++------
Mpackages/taler-wallet-core/src/coinSelection.ts | 2++
Mpackages/taler-wallet-core/src/deposits.ts | 144+++++++++++++++++++++++++++++++++++++++++--------------------------------------
3 files changed, 99 insertions(+), 76 deletions(-)

diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts @@ -2401,20 +2401,30 @@ export interface DepositGroupFees { } export interface CreateDepositGroupRequest { + depositPaytoUri: string; + /** - * Pre-allocated transaction ID. - * Allows clients to easily handle notifications - * that occur while the operation has been created but - * before the creation request has returned. + * Amount to deposit. */ - transactionId?: TransactionIdStr; - depositPaytoUri: string; amount: AmountString; /** + * Restrict the deposit to a certain scope. + */ + restrictScope?: ScopeInfo; + + /** * Use a fixed merchant private key. */ testingFixedPriv?: string; + + /** + * Pre-allocated transaction ID. + * Allows clients to easily handle notifications + * that occur while the operation has been created but + * before the creation request has returned. + */ + transactionId?: TransactionIdStr; } export interface CheckDepositRequest { @@ -2432,6 +2442,11 @@ export interface CheckDepositRequest { amount: AmountString; /** + * Restrict the deposit to a certain scope. + */ + restrictScope?: ScopeInfo; + + /** * ID provided by the client to cancel the request. * * If the same request is made again with the same clientCancellationId, @@ -2447,6 +2462,7 @@ export interface CheckDepositRequest { export const codecForCheckDepositRequest = (): Codec<CheckDepositRequest> => buildCodecForObject<CheckDepositRequest>() + .property("restrictScope", codecOptional(codecForScopeInfo())) .property("amount", codecForAmountString()) .property("depositPaytoUri", codecForString()) .property("clientCancellationId", codecOptional(codecForString())) @@ -2469,6 +2485,7 @@ export interface CheckDepositResponse { export const codecForCreateDepositGroupRequest = (): Codec<CreateDepositGroupRequest> => buildCodecForObject<CreateDepositGroupRequest>() + .property("restrictScope", codecOptional(codecForScopeInfo())) .property("amount", codecForAmountString()) .property("depositPaytoUri", codecForString()) .property("transactionId", codecOptional(codecForTransactionIdStr())) diff --git a/packages/taler-wallet-core/src/coinSelection.ts b/packages/taler-wallet-core/src/coinSelection.ts @@ -214,6 +214,7 @@ async function internalSelectPayCoins( currency: Amounts.currencyOf(req.contractTermsAmount), restrictExchanges: req.restrictExchanges, restrictWireMethod: req.restrictWireMethod, + restrictScope: req.restrictScope, depositPaytoUri: req.depositPaytoUri, requiredMinimumAge: req.requiredMinimumAge, includePendingCoins, @@ -737,6 +738,7 @@ function selectForced( export interface SelectPayCoinRequestNg { restrictExchanges: ExchangeRestrictionSpec | undefined; + restrictScope?: ScopeInfo; restrictWireMethod: string; contractTermsAmount: AmountJson; depositFeeLimit: AmountJson; diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts @@ -43,6 +43,7 @@ import { Logger, MerchantContractTerms, RefreshReason, + ScopeInfo, SelectedProspectiveCoin, TalerError, TalerErrorCode, @@ -108,6 +109,7 @@ import { } from "./db.js"; import { ReadyExchangeSummary, + checkExchangeInScopeTx, fetchFreshExchange, getExchangeWireDetailsInTx, getExchangeWireFee, @@ -1820,6 +1822,39 @@ export async function checkDepositGroup( ); } +async function getExchangesForDeposit( + wex: WalletExecutionContext, + req: { restrictScope?: ScopeInfo; currency: string }, +): Promise<Exchange[]> { + const exchangeInfos: Exchange[] = []; + await wex.db.runAllStoresReadOnlyTx({}, async (tx) => { + const allExchanges = await tx.exchanges.iter().toArray(); + for (const e of allExchanges) { + const details = await getExchangeWireDetailsInTx(tx, e.baseUrl); + if (!details || req.currency !== details.currency) { + continue; + } + if (req.restrictScope) { + const inScope = await checkExchangeInScopeTx( + wex, + tx, + e.baseUrl, + req.restrictScope, + ); + if (!inScope) { + break; + } + } + exchangeInfos.push({ + master_pub: details.masterPublicKey, + priority: 1, + url: e.baseUrl, + }); + } + }); + return exchangeInfos; +} + /** * Check if creating a deposit group is possible and calculate * the associated fees. @@ -1835,69 +1870,28 @@ export async function internalCheckDepositGroup( const amount = Amounts.parseOrThrow(req.amount); const currency = Amounts.currencyOf(amount); - const exchangeInfos: Exchange[] = []; - - await wex.db.runReadOnlyTx( - { storeNames: ["exchangeDetails", "exchanges"] }, - async (tx) => { - const allExchanges = await tx.exchanges.iter().toArray(); - for (const e of allExchanges) { - const details = await getExchangeWireDetailsInTx(tx, e.baseUrl); - if (!details || amount.currency !== details.currency) { - continue; - } - exchangeInfos.push({ - master_pub: details.masterPublicKey, - priority: 1, - url: e.baseUrl, - }); - } - }, - ); - - const now = AbsoluteTime.now(); - const nowRounded = AbsoluteTime.toProtocolTimestamp(now); - const contractTerms: MerchantContractTerms = { - exchanges: exchangeInfos, - amount: req.amount, - max_fee: Amounts.stringify(amount), - wire_method: p.targetType, - timestamp: nowRounded, - merchant_base_url: "", - summary: "", - nonce: "", - wire_transfer_deadline: nowRounded, - order_id: "", - h_wire: "", - pay_deadline: AbsoluteTime.toProtocolTimestamp( - AbsoluteTime.addDuration(now, Duration.fromSpec({ hours: 1 })), - ), - merchant: { - name: "(wallet)", - }, - merchant_pub: "", - refund_deadline: TalerProtocolTimestamp.zero(), - }; - - const { h: contractTermsHash } = await wex.cryptoApi.hashString({ - str: canonicalJson(contractTerms), + const exchangeInfos: Exchange[] = await getExchangesForDeposit(wex, { + currency, + restrictScope: req.restrictScope, }); - const contractData = extractContractData( - contractTerms, - contractTermsHash, - "", - ); + const depositFeeLimit = amount; const payCoinSel = await selectPayCoins(wex, { restrictExchanges: { auditors: [], - exchanges: contractData.allowedExchanges, + exchanges: exchangeInfos.map((x) => { + return { + exchangeBaseUrl: x.url, + exchangePub: x.master_pub, + }; + }), }, - restrictWireMethod: contractData.wireMethod, + restrictScope: req.restrictScope, + restrictWireMethod: p.targetType, depositPaytoUri: req.depositPaytoUri, - contractTermsAmount: Amounts.parseOrThrow(contractData.amount), - depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee), + contractTermsAmount: Amounts.parseOrThrow(req.amount), + depositFeeLimit, prevPayCoins: [], }); @@ -1975,24 +1969,33 @@ export async function createDepositGroup( const amount = Amounts.parseOrThrow(req.amount); const currency = amount.currency; - const exchangeInfos: { url: string; master_pub: string }[] = []; + const exchangeInfos: Exchange[] = []; - await wex.db.runReadOnlyTx( - { storeNames: ["exchanges", "exchangeDetails"] }, - async (tx) => { - const allExchanges = await tx.exchanges.iter().toArray(); - for (const e of allExchanges) { - const details = await getExchangeWireDetailsInTx(tx, e.baseUrl); - if (!details || amount.currency !== details.currency) { - continue; + await wex.db.runAllStoresReadOnlyTx({}, async (tx) => { + const allExchanges = await tx.exchanges.iter().toArray(); + for (const e of allExchanges) { + const details = await getExchangeWireDetailsInTx(tx, e.baseUrl); + if (!details || amount.currency !== details.currency) { + continue; + } + if (req.restrictScope) { + const inScope = await checkExchangeInScopeTx( + wex, + tx, + e.baseUrl, + req.restrictScope, + ); + if (!inScope) { + break; } - exchangeInfos.push({ - master_pub: details.masterPublicKey, - url: e.baseUrl, - }); } - }, - ); + exchangeInfos.push({ + master_pub: details.masterPublicKey, + priority: 1, + url: e.baseUrl, + }); + } + }); const now = AbsoluteTime.now(); const wireDeadline = AbsoluteTime.toProtocolTimestamp( @@ -2008,6 +2011,7 @@ export async function createDepositGroup( exchangePub: x.master_pub, })), }, + restrictScope: req.restrictScope, restrictWireMethod: depositPayto.targetType, depositPaytoUri: req.depositPaytoUri, contractTermsAmount: amount,