taler-typescript-core

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

commit 552a91ebac4280ee8eecd38b058113e4e3eb900c
parent 4959242caa9dcc4c540eedf8f2f4502d6058434e
Author: Florian Dold <florian@dold.me>
Date:   Thu, 17 Oct 2024 10:30:32 +0200

wallet-core: allow listing exchanges filtered by scope

Also remove the listExchangesForScopedCurrency request, as the same
functionality is now covered by listExchanges.

Diffstat:
Mpackages/taler-util/src/types-taler-wallet.ts | 22++++++++++++----------
Mpackages/taler-wallet-core/src/coinSelection.ts | 17++++++++++++++---
Mpackages/taler-wallet-core/src/exchanges.ts | 47+++++++++++++++++++++++++++++++++++++++++++----
Mpackages/taler-wallet-core/src/pay-peer-push-debit.ts | 3++-
Mpackages/taler-wallet-core/src/wallet-api-types.ts | 17++---------------
Mpackages/taler-wallet-core/src/wallet.ts | 30++----------------------------
Mpackages/taler-wallet-core/src/withdraw.ts | 2+-
7 files changed, 76 insertions(+), 62 deletions(-)

diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts @@ -351,16 +351,6 @@ export const codecForGetCurrencyInfoRequest = .property("scope", codecForScopeInfo()) .build("GetCurrencySpecificationRequest"); -export interface ListExchangesForScopedCurrencyRequest { - scope: ScopeInfo; -} - -export const codecForListExchangesForScopedCurrencyRequest = - (): Codec<ListExchangesForScopedCurrencyRequest> => - buildCodecForObject<ListExchangesForScopedCurrencyRequest>() - .property("scope", codecForScopeInfo()) - .build("ListExchangesForScopedCurrencyRequest"); - export interface GetCurrencySpecificationResponse { currencySpecification: CurrencySpecification; } @@ -1091,6 +1081,18 @@ export interface ExchangesListResponse { exchanges: ExchangeListItem[]; } +export interface ListExchangesRequest { + /** + * Filter results to only include exchanges in the given scope. + */ + filterByScope?: ScopeInfo; +} + +export const codecForListExchangesRequest = (): Codec<ListExchangesRequest> => + buildCodecForObject<ListExchangesRequest>() + .property("filterByScope", codecOptional(codecForScopeInfo())) + .build("ListExchangesRequest"); + export interface ExchangeDetailedResponse { exchange: ExchangeFullDetails; } diff --git a/packages/taler-wallet-core/src/coinSelection.ts b/packages/taler-wallet-core/src/coinSelection.ts @@ -59,7 +59,7 @@ import { getPaymentBalanceDetailsInTx } from "./balance.js"; import { getAutoRefreshExecuteThreshold } from "./common.js"; import { DenominationRecord, WalletDbReadOnlyTransaction } from "./db.js"; import { - checkExchangeInScope, + checkExchangeInScopeTx, ExchangeWireDetails, getExchangeWireDetailsInTx, } from "./exchanges.js"; @@ -1194,6 +1194,8 @@ export async function selectPeerCoinsInTx( "denominations", "refreshGroups", "exchangeDetails", + "globalCurrencyExchanges", + "globalCurrencyAuditors", ] >, req: PeerCoinSelectionRequest, @@ -1216,7 +1218,7 @@ export async function selectPeerCoinsInTx( continue; } const isInScope = req.restrictScope - ? await checkExchangeInScope(wex, exch.baseUrl, req.restrictScope) + ? await checkExchangeInScopeTx(wex, tx, exch.baseUrl, req.restrictScope) : true; if (!isInScope) { continue; @@ -1329,6 +1331,8 @@ export async function selectPeerCoins( "denominations", "refreshGroups", "exchangeDetails", + "globalCurrencyExchanges", + "globalCurrencyAuditors", ], }, async (tx): Promise<SelectPeerCoinsResult> => { @@ -1457,6 +1461,8 @@ export async function getMaxPeerPushDebitAmount( "coinAvailability", "denominations", "exchangeDetails", + "globalCurrencyExchanges", + "globalCurrencyAuditors", ], }, async (tx): Promise<GetMaxPeerPushDebitAmountResponse> => { @@ -1472,7 +1478,12 @@ export async function getMaxPeerPushDebitAmount( continue; } const isInScope = req.restrictScope - ? await checkExchangeInScope(wex, exch.baseUrl, req.restrictScope) + ? await checkExchangeInScopeTx( + wex, + tx, + exch.baseUrl, + req.restrictScope, + ) : true; if (!isInScope) { continue; diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts @@ -63,6 +63,7 @@ import { HttpStatusCode, LegitimizationNeededResponse, LibtoolVersion, + ListExchangesRequest, Logger, NotificationType, OperationErrorInfo, @@ -2519,6 +2520,7 @@ export async function downloadExchangeInfo( */ export async function listExchanges( wex: WalletExecutionContext, + req: ListExchangesRequest, ): Promise<ExchangesListResponse> { const exchanges: ExchangeListItem[] = []; await wex.db.runReadOnlyTx( @@ -2551,6 +2553,17 @@ export async function listExchanges( ); checkDbInvariant(!!reserveRec, "reserve record not found"); } + if (req.filterByScope) { + const inScope = await checkExchangeInScopeTx( + wex, + tx, + exchangeRec.baseUrl, + req.filterByScope, + ); + if (!inScope) { + continue; + } + } exchanges.push( await makeExchangeListItem( tx, @@ -3587,13 +3600,39 @@ export async function processExchangeKyc( } } -export async function checkExchangeInScope( +export async function checkExchangeInScopeTx( wex: WalletExecutionContext, + tx: WalletDbReadOnlyTransaction< + [ + "globalCurrencyExchanges", + "globalCurrencyAuditors", + "exchanges", + "exchangeDetails", + ] + >, exchangeBaseUrl: string, scope: ScopeInfo, ): Promise<boolean> { - if (scope.type === ScopeType.Exchange && scope.url !== exchangeBaseUrl) { - return false; + switch (scope.type) { + case ScopeType.Exchange: { + return scope.url === exchangeBaseUrl; + } + case ScopeType.Global: { + const exchangeDetails = await getExchangeRecordsInternal( + tx, + exchangeBaseUrl, + ); + if (!exchangeDetails) { + return false; + } + const gr = await tx.globalCurrencyExchanges.get([ + exchangeDetails.currency, + exchangeBaseUrl, + exchangeDetails.masterPublicKey, + ]); + return gr != null; + } + case ScopeType.Auditor: + throw Error("auditor scope not supported yet"); } - 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 @@ -25,7 +25,6 @@ import { InitiatePeerPushDebitRequest, InitiatePeerPushDebitResponse, Logger, - NotificationType, RefreshReason, SelectedProspectiveCoin, TalerError, @@ -1253,6 +1252,8 @@ export async function initiatePeerPushDebit( "refreshGroups", "refreshSessions", "transactionsMeta", + "globalCurrencyExchanges", + "globalCurrencyAuditors", ], }, async (tx) => { diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts @@ -66,7 +66,6 @@ import { EmptyObject, ExchangeDetailedResponse, ExchangesListResponse, - ExchangesShortListResponse, FailTransactionRequest, ForceRefreshRequest, ForgetKnownBankAccountsRequest, @@ -107,7 +106,7 @@ import { KnownBankAccounts, ListAssociatedRefreshesRequest, ListAssociatedRefreshesResponse, - ListExchangesForScopedCurrencyRequest, + ListExchangesRequest, ListGlobalCurrencyAuditorsResponse, ListGlobalCurrencyExchangesResponse, ListKnownBankAccountsRequest, @@ -260,7 +259,6 @@ export enum WalletApiOperation { DeleteStoredBackup = "deleteStoredBackup", RecoverStoredBackup = "recoverStoredBackup", UpdateExchangeEntry = "updateExchangeEntry", - ListExchangesForScopedCurrency = "listExchangesForScopedCurrency", PrepareWithdrawExchange = "prepareWithdrawExchange", GetExchangeResources = "getExchangeResources", DeleteExchange = "deleteExchange", @@ -655,7 +653,7 @@ export type RemoveGlobalCurrencyAuditorOp = { */ export type ListExchangesOp = { op: WalletApiOperation.ListExchanges; - request: EmptyObject; + request: ListExchangesRequest; response: ExchangesListResponse; }; @@ -672,16 +670,6 @@ export type TestingWaitExchangeWalletKycOp = { }; /** - * List exchanges that are available for withdrawing a particular - * scoped currency. - */ -export type ListExchangesForScopedCurrencyOp = { - op: WalletApiOperation.ListExchangesForScopedCurrency; - request: ListExchangesForScopedCurrencyRequest; - response: ExchangesShortListResponse; -}; - -/** * Prepare for withdrawing via a taler://withdraw-exchange URI. */ export type PrepareWithdrawExchangeOp = { @@ -1337,7 +1325,6 @@ export type WalletOperations = { [WalletApiOperation.AcceptBankIntegratedWithdrawal]: AcceptBankIntegratedWithdrawalOp; [WalletApiOperation.AcceptManualWithdrawal]: AcceptManualWithdrawalOp; [WalletApiOperation.ListExchanges]: ListExchangesOp; - [WalletApiOperation.ListExchangesForScopedCurrency]: ListExchangesForScopedCurrencyOp; [WalletApiOperation.AddExchange]: AddExchangeOp; [WalletApiOperation.ListKnownBankAccounts]: ListKnownBankAccountsOp; [WalletApiOperation.AddKnownBankAccounts]: AddKnownBankAccountsOp; diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts @@ -53,7 +53,6 @@ import { DenominationInfo, Duration, EmptyObject, - ExchangesShortListResponse, FailTransactionRequest, ForgetKnownBankAccountsRequest, GetActiveTasksResponse, @@ -75,7 +74,6 @@ import { IntegrationTestV2Args, KnownBankAccounts, KnownBankAccountsInfo, - ListExchangesForScopedCurrencyRequest, ListGlobalCurrencyAuditorsResponse, ListGlobalCurrencyExchangesResponse, ListKnownBankAccountsRequest, @@ -170,7 +168,7 @@ import { codecForInitiatePeerPushDebitRequest, codecForIntegrationTestArgs, codecForIntegrationTestV2Args, - codecForListExchangesForScopedCurrencyRequest, + codecForListExchangesRequest, codecForListKnownBankAccounts, codecForPrepareBankIntegratedWithdrawalRequest, codecForPreparePayRequest, @@ -951,26 +949,6 @@ async function handleTestingGetDenomStats( return denomStats; } -async function handleListExchangesForScopedCurrency( - wex: WalletExecutionContext, - req: ListExchangesForScopedCurrencyRequest, -): Promise<ExchangesShortListResponse> { - const exchangesResp = await listExchanges(wex); - const result: ExchangesShortListResponse = { - exchanges: [], - }; - // Right now we only filter on the currency, as wallet-core doesn't - // fully support scoped currencies yet. - for (const exch of exchangesResp.exchanges) { - if (exch.currency === req.scope.currency) { - result.exchanges.push({ - exchangeBaseUrl: exch.exchangeBaseUrl, - }); - } - } - return result; -} - async function handleAddKnownBankAccount( wex: WalletExecutionContext, req: AddKnownBankAccountsRequest, @@ -1678,17 +1656,13 @@ const handlers: { [T in WalletApiOperation]: HandlerWithValidator<T> } = { handler: handleTestingGetDenomStats, }, [WalletApiOperation.ListExchanges]: { - codec: codecForEmptyObject(), + codec: codecForListExchangesRequest(), handler: listExchanges, }, [WalletApiOperation.GetExchangeEntryByUrl]: { codec: codecForGetExchangeEntryByUrlRequest(), handler: lookupExchangeByUri, }, - [WalletApiOperation.ListExchangesForScopedCurrency]: { - codec: codecForListExchangesForScopedCurrencyRequest(), - handler: handleListExchangesForScopedCurrency, - }, [WalletApiOperation.GetExchangeDetailedInfo]: { codec: codecForAddExchangeRequest(), handler: (wex, req) => getExchangeDetailedInfo(wex, req.exchangeBaseUrl), diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts @@ -2619,7 +2619,7 @@ async function getWithdrawalDetailsForBankInfo( }); possibleExchanges = [ex]; } else { - const listExchangesResp = await listExchanges(wex); + const listExchangesResp = await listExchanges(wex, {}); for (const exchange of listExchangesResp.exchanges) { if (exchange.currency !== currency) {