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:
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) {