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:
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,