[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-wallet-core] branch master updated: wallet-core: implement scope
From: |
gnunet |
Subject: |
[taler-wallet-core] branch master updated: wallet-core: implement scope restriction and account restriction reporting for getMaxDepositAmount |
Date: |
Wed, 04 Dec 2024 22:46:17 +0100 |
This is an automated email from the git hooks/post-receive script.
dold pushed a commit to branch master
in repository wallet-core.
The following commit(s) were added to refs/heads/master by this push:
new d1cda666c wallet-core: implement scope restriction and account
restriction reporting for getMaxDepositAmount
d1cda666c is described below
commit d1cda666c629bfc3b5bc0a8f2039f5eaa80a6964
Author: Florian Dold <florian@dold.me>
AuthorDate: Wed Dec 4 22:46:13 2024 +0100
wallet-core: implement scope restriction and account restriction reporting
for getMaxDepositAmount
---
packages/taler-util/src/types-taler-wallet.ts | 20 +++
packages/taler-wallet-core/src/coinSelection.ts | 77 ++++++++++--
packages/taler-wallet-core/src/deposits.ts | 18 +--
packages/taler-wallet-core/src/pay-merchant.ts | 156 ++++++++++--------------
4 files changed, 151 insertions(+), 120 deletions(-)
diff --git a/packages/taler-util/src/types-taler-wallet.ts
b/packages/taler-util/src/types-taler-wallet.ts
index ae5aa2021..c48ca5fef 100644
--- a/packages/taler-util/src/types-taler-wallet.ts
+++ b/packages/taler-util/src/types-taler-wallet.ts
@@ -238,14 +238,27 @@ export const codecForConvertAmountRequest =
.build("ConvertAmountRequest");
export interface GetMaxDepositAmountRequest {
+ /**
+ * Currency to deposit.
+ */
currency: string;
+
+ /**
+ * Target bank account to deposit into.
+ */
depositPaytoUri?: string;
+
+ /**
+ * Restrict the deposit to a certain scope.
+ */
+ restrictScope?: ScopeInfo;
}
export const codecForGetMaxDepositAmountRequest =
buildCodecForObject<GetMaxDepositAmountRequest>()
.property("currency", codecForString())
.property("depositPaytoUri", codecOptional(codecForString()))
+ .property("restrictScope", codecOptional(codecForScopeInfo()))
.build("GetAmountRequest");
export interface GetMaxPeerPushDebitAmountRequest {
@@ -268,6 +281,13 @@ export const codecForGetMaxPeerPushDebitAmountRequest =
export interface GetMaxDepositAmountResponse {
effectiveAmount: AmountString;
rawAmount: AmountString;
+
+ /**
+ * Account restrictions that affect the max deposit amount.
+ */
+ depositRestrictions?: {
+ [exchangeBaseUrl: string]: { [paytoUri: string]: AccountRestriction[] };
+ };
}
export interface GetMaxPeerPushDebitAmountResponse {
diff --git a/packages/taler-wallet-core/src/coinSelection.ts
b/packages/taler-wallet-core/src/coinSelection.ts
index 1d9ccf9ad..df9eaf3f6 100644
--- a/packages/taler-wallet-core/src/coinSelection.ts
+++ b/packages/taler-wallet-core/src/coinSelection.ts
@@ -26,6 +26,7 @@
import { GlobalIDB } from "@gnu-taler/idb-bridge";
import {
AbsoluteTime,
+ AccountRestriction,
AgeRestriction,
AllowedAuditorInfo,
AllowedExchangeInfo,
@@ -184,6 +185,8 @@ async function internalSelectPayCoins(
"exchanges",
"exchangeDetails",
"coins",
+ "globalCurrencyAuditors",
+ "globalCurrencyExchanges",
]
>,
req: SelectPayCoinRequestNg,
@@ -288,6 +291,8 @@ export async function selectPayCoinsInTx(
"exchanges",
"exchangeDetails",
"coins",
+ "globalCurrencyAuditors",
+ "globalCurrencyExchanges",
]
>,
req: SelectPayCoinRequestNg,
@@ -380,6 +385,8 @@ export async function selectPayCoins(
"exchanges",
"exchangeDetails",
"coins",
+ "globalCurrencyAuditors",
+ "globalCurrencyExchanges",
],
},
async (tx) => {
@@ -756,7 +763,11 @@ export function findMatchingWire(
wireMethod: string,
depositPaytoUri: string | undefined,
exchangeWireDetails: ExchangeWireDetails,
-): { wireFee: AmountJson } | undefined {
+):
+ | { ok: true; wireFee: AmountJson }
+ | { ok: false; accountRestrictions: Record<string, AccountRestriction[]> }
+ | undefined {
+ const accountRestrictions: Record<string, AccountRestriction[]> = {};
for (const acc of exchangeWireDetails.wireInfo.accounts) {
const pp = parsePaytoUri(acc.payto_uri);
checkLogicInvariant(!!pp);
@@ -796,10 +807,18 @@ export function findMatchingWire(
}
return {
+ ok: true,
wireFee: Amounts.parseOrThrow(wireFeeStr),
};
}
- return undefined;
+ if (Object.keys(accountRestrictions).length > 0) {
+ return {
+ ok: false,
+ accountRestrictions,
+ };
+ } else {
+ return undefined;
+ }
}
function checkExchangeAccepted(
@@ -831,6 +850,7 @@ interface SelectPayCandidatesRequest {
currency: string;
restrictWireMethod: string | undefined;
depositPaytoUri?: string;
+ restrictScope?: ScopeInfo;
restrictExchanges: ExchangeRestrictionSpec | undefined;
requiredMinimumAge?: number;
@@ -845,12 +865,20 @@ interface SelectPayCandidatesRequest {
export interface PayCoinCandidates {
coinAvailability: AvailableCoinsOfDenom[];
currentWireFeePerExchange: Record<string, AmountJson>;
+ depositRestrictions: Record<string, Record<string, AccountRestriction[]>>;
}
async function selectPayCandidates(
wex: WalletExecutionContext,
tx: WalletDbReadOnlyTransaction<
- ["exchanges", "coinAvailability", "exchangeDetails", "denominations"]
+ [
+ "exchanges",
+ "coinAvailability",
+ "exchangeDetails",
+ "denominations",
+ "globalCurrencyAuditors",
+ "globalCurrencyExchanges",
+ ]
>,
req: SelectPayCandidatesRequest,
): Promise<PayCoinCandidates> {
@@ -861,26 +889,30 @@ async function selectPayCandidates(
const denoms: AvailableCoinsOfDenom[] = [];
const exchanges = await tx.exchanges.iter().toArray();
const wfPerExchange: Record<string, AmountJson> = {};
+ const depositRestrictions: Record<
+ string,
+ Record<string, AccountRestriction[]>
+ > = {};
for (const exchange of exchanges) {
const exchangeDetails = await getExchangeWireDetailsInTx(
tx,
exchange.baseUrl,
);
- // 1. exchange has same currency
+ // Exchange has same currency
if (exchangeDetails?.currency !== req.currency) {
logger.shouldLogTrace() &&
logger.trace(`skipping ${exchange.baseUrl} due to currency mismatch`);
continue;
}
- // 2. Exchange supports wire method (only for pay/deposit)
+ // Exchange supports wire method (only for pay/deposit)
if (req.restrictWireMethod) {
- const wire = findMatchingWire(
+ const wireMatch = findMatchingWire(
req.restrictWireMethod,
req.depositPaytoUri,
exchangeDetails,
);
- if (!wire) {
+ if (!wireMatch) {
if (logger.shouldLogTrace()) {
logger.trace(
`skipping ${exchange.baseUrl} due to missing wire info mismatch`,
@@ -888,10 +920,14 @@ async function selectPayCandidates(
}
continue;
}
- wfPerExchange[exchange.baseUrl] = wire.wireFee;
+ if (!wireMatch.ok) {
+ depositRestrictions[exchange.baseUrl] = wireMatch.accountRestrictions;
+ continue;
+ }
+ wfPerExchange[exchange.baseUrl] = wireMatch.wireFee;
}
- // 3. exchange is trusted in the exchange list or auditor list
+ // Exchange is trusted in the exchange list or auditor list
let accepted = checkExchangeAccepted(
exchangeDetails,
req.restrictExchanges,
@@ -903,7 +939,20 @@ async function selectPayCandidates(
continue;
}
- // 4. filter coins restricted by age
+ const isInScope = req.restrictScope
+ ? await checkExchangeInScopeTx(
+ wex,
+ tx,
+ exchange.baseUrl,
+ req.restrictScope,
+ )
+ : true;
+
+ if (!isInScope) {
+ continue;
+ }
+
+ // Filter coins restricted by age
let ageLower = 0;
let ageUpper = AgeRestriction.AGE_UNRESTRICTED;
if (req.requiredMinimumAge) {
@@ -926,7 +975,7 @@ async function selectPayCandidates(
let numUsable = 0;
- // 5. save denoms with how many coins are available
+ // Save denoms with how many coins are available
// FIXME: Check that the individual denomination is audited!
// FIXME: Should we exclude denominations that are
// not spendable anymore?
@@ -977,6 +1026,7 @@ async function selectPayCandidates(
return {
coinAvailability: denoms,
currentWireFeePerExchange: wfPerExchange,
+ depositRestrictions: depositRestrictions,
};
}
@@ -1125,6 +1175,8 @@ async function internalSelectPeerCoins(
"denominations",
"refreshGroups",
"exchangeDetails",
+ "globalCurrencyAuditors",
+ "globalCurrencyExchanges",
]
>,
req: PeerCoinSelectionRequest,
@@ -1399,6 +1451,8 @@ export async function getMaxDepositAmount(
"coinAvailability",
"denominations",
"exchangeDetails",
+ "globalCurrencyAuditors",
+ "globalCurrencyExchanges",
],
},
async (tx): Promise<GetMaxDepositAmountResponse> => {
@@ -1414,6 +1468,7 @@ export async function getMaxDepositAmount(
currency: req.currency,
restrictExchanges: undefined,
restrictWireMethod,
+ restrictScope: req.restrictScope,
depositPaytoUri: req.depositPaytoUri,
requiredMinimumAge: undefined,
includePendingCoins: true,
diff --git a/packages/taler-wallet-core/src/deposits.ts
b/packages/taler-wallet-core/src/deposits.ts
index 3eda5b165..b424c407a 100644
--- a/packages/taler-wallet-core/src/deposits.ts
+++ b/packages/taler-wallet-core/src/deposits.ts
@@ -1473,22 +1473,8 @@ async function processDepositGroupPendingDeposit(
if (!depositGroup.payCoinSelection) {
logger.info("missing coin selection for deposit group, selecting now");
- const transitionDone = await wex.db.runReadWriteTx(
- {
- storeNames: [
- "contractTerms",
- "exchanges",
- "exchangeDetails",
- "depositGroups",
- "coins",
- "coinAvailability",
- "coinHistory",
- "refreshGroups",
- "refreshSessions",
- "denominations",
- "transactionsMeta",
- ],
- },
+ const transitionDone = await wex.db.runAllStoresReadWriteTx(
+ {},
async (tx) => {
const dg = await tx.depositGroups.get(depositGroupId);
if (!dg) {
diff --git a/packages/taler-wallet-core/src/pay-merchant.ts
b/packages/taler-wallet-core/src/pay-merchant.ts
index a81caacff..d6dafac69 100644
--- a/packages/taler-wallet-core/src/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/pay-merchant.ts
@@ -1442,89 +1442,72 @@ async function handleInsufficientFunds(
// FIXME: Above code should go into the transaction.
- await wex.db.runReadWriteTx(
- {
- storeNames: [
- "coinAvailability",
- "coinHistory",
- "coins",
- "contractTerms",
- "denominations",
- "exchangeDetails",
- "exchanges",
- "purchases",
- "refreshGroups",
- "refreshSessions",
- "transactionsMeta",
- ],
- },
- async (tx) => {
- const p = await tx.purchases.get(proposalId);
- if (!p) {
- return;
- }
- const payInfo = p.payInfo;
- if (!payInfo) {
- return;
- }
-
- const { contractData } = await expectProposalDownloadInTx(
- wex,
- tx,
- proposal,
- );
+ await wex.db.runAllStoresReadWriteTx({}, async (tx) => {
+ const p = await tx.purchases.get(proposalId);
+ if (!p) {
+ return;
+ }
+ const payInfo = p.payInfo;
+ if (!payInfo) {
+ return;
+ }
- for (let i = 0; i < payCoinSelection.coinPubs.length; i++) {
- const coinPub = payCoinSelection.coinPubs[i];
- const contrib = payCoinSelection.coinContributions[i];
- prevPayCoins.push({
- coinPub,
- contribution: Amounts.parseOrThrow(contrib),
- });
- }
+ const { contractData } = await expectProposalDownloadInTx(
+ wex,
+ tx,
+ proposal,
+ );
- const res = await selectPayCoinsInTx(wex, tx, {
- restrictExchanges: {
- auditors: [],
- exchanges: contractData.allowedExchanges,
- },
- restrictWireMethod: contractData.wireMethod,
- contractTermsAmount: Amounts.parseOrThrow(contractData.amount),
- depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee),
- prevPayCoins,
- requiredMinimumAge: contractData.minimumAge,
+ for (let i = 0; i < payCoinSelection.coinPubs.length; i++) {
+ const coinPub = payCoinSelection.coinPubs[i];
+ const contrib = payCoinSelection.coinContributions[i];
+ prevPayCoins.push({
+ coinPub,
+ contribution: Amounts.parseOrThrow(contrib),
});
+ }
- switch (res.type) {
- case "failure":
- logger.trace("insufficient funds for coin re-selection");
- return;
- case "prospective":
- return;
- case "success":
- break;
- default:
- assertUnreachable(res);
- }
+ const res = await selectPayCoinsInTx(wex, tx, {
+ restrictExchanges: {
+ auditors: [],
+ exchanges: contractData.allowedExchanges,
+ },
+ restrictWireMethod: contractData.wireMethod,
+ contractTermsAmount: Amounts.parseOrThrow(contractData.amount),
+ depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee),
+ prevPayCoins,
+ requiredMinimumAge: contractData.minimumAge,
+ });
- // Convert to DB format
- payInfo.payCoinSelection = {
- coinContributions: res.coinSel.coins.map((x) => x.contribution),
- coinPubs: res.coinSel.coins.map((x) => x.coinPub),
- };
- payInfo.payCoinSelectionUid = encodeCrock(getRandomBytes(32));
- await tx.purchases.put(p);
- await ctx.updateTransactionMeta(tx);
- await spendCoins(wex, tx, {
- transactionId: ctx.transactionId,
- coinPubs: payInfo.payCoinSelection.coinPubs,
- contributions: payInfo.payCoinSelection.coinContributions.map((x) =>
- Amounts.parseOrThrow(x),
- ),
- refreshReason: RefreshReason.PayMerchant,
- });
- },
- );
+ switch (res.type) {
+ case "failure":
+ logger.trace("insufficient funds for coin re-selection");
+ return;
+ case "prospective":
+ return;
+ case "success":
+ break;
+ default:
+ assertUnreachable(res);
+ }
+
+ // Convert to DB format
+ payInfo.payCoinSelection = {
+ coinContributions: res.coinSel.coins.map((x) => x.contribution),
+ coinPubs: res.coinSel.coins.map((x) => x.coinPub),
+ };
+ payInfo.payCoinSelectionUid = encodeCrock(getRandomBytes(32));
+ await tx.purchases.put(p);
+ await ctx.updateTransactionMeta(tx);
+ await spendCoins(wex, tx, {
+ transactionId: ctx.transactionId,
+ coinPubs: payInfo.payCoinSelection.coinPubs,
+ contributions: payInfo.payCoinSelection.coinContributions.map((x) =>
+ Amounts.parseOrThrow(x),
+ ),
+ refreshReason: RefreshReason.PayMerchant,
+ });
+ });
wex.ws.notify({
type: NotificationType.BalanceChange,
@@ -2216,21 +2199,8 @@ export async function confirmPay(
`recording payment on ${proposal.orderId} with session ID ${sessionId}`,
);
- const transitionInfo = await wex.db.runReadWriteTx(
- {
- storeNames: [
- "coinAvailability",
- "coinHistory",
- "coins",
- "denominations",
- "exchangeDetails",
- "exchanges",
- "purchases",
- "refreshGroups",
- "refreshSessions",
- "transactionsMeta",
- ],
- },
+ const transitionInfo = await wex.db.runAllStoresReadWriteTx(
+ {},
async (tx) => {
const p = await tx.purchases.get(proposal.proposalId);
if (!p) {
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-wallet-core] branch master updated: wallet-core: implement scope restriction and account restriction reporting for getMaxDepositAmount,
gnunet <=