summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/balance.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2024-03-06 14:17:31 +0100
committerFlorian Dold <florian@dold.me>2024-03-06 20:34:42 +0100
commit91be5b89cd92c53d6aa2f68247f9626c8bc8f64a (patch)
tree2c562c9170d32833647d2bffb69a91c42bef9b0d /packages/taler-wallet-core/src/balance.ts
parent2e344093305ddf72f97e099cba107356970bb1e4 (diff)
downloadwallet-core-91be5b89cd92c53d6aa2f68247f9626c8bc8f64a.tar.gz
wallet-core-91be5b89cd92c53d6aa2f68247f9626c8bc8f64a.tar.bz2
wallet-core-91be5b89cd92c53d6aa2f68247f9626c8bc8f64a.zip
towards refactoring coin selection
Diffstat (limited to 'packages/taler-wallet-core/src/balance.ts')
-rw-r--r--packages/taler-wallet-core/src/balance.ts257
1 files changed, 162 insertions, 95 deletions
diff --git a/packages/taler-wallet-core/src/balance.ts b/packages/taler-wallet-core/src/balance.ts
index 6dc0783c0..a77358363 100644
--- a/packages/taler-wallet-core/src/balance.ts
+++ b/packages/taler-wallet-core/src/balance.ts
@@ -468,12 +468,16 @@ export interface MerchantPaymentBalanceDetails {
balanceAvailable: AmountJson;
}
-export interface MerchantPaymentRestrictionsForBalance {
+export interface PaymentRestrictionsForBalance {
currency: string;
minAge: number;
- acceptedExchanges: AllowedExchangeInfo[];
- acceptedAuditors: AllowedAuditorInfo[];
- acceptedWireMethods: string[];
+ restrictExchanges:
+ | {
+ exchanges: AllowedExchangeInfo[];
+ auditors: AllowedAuditorInfo[];
+ }
+ | undefined;
+ restrictWireMethods: string[] | undefined;
}
export interface AcceptableExchanges {
@@ -492,69 +496,73 @@ export interface AcceptableExchanges {
/**
* Get all exchanges that are acceptable for a particular payment.
*/
-export async function getAcceptableExchangeBaseUrls(
+async function getAcceptableExchangeBaseUrlsInTx(
wex: WalletExecutionContext,
- req: MerchantPaymentRestrictionsForBalance,
+ tx: WalletDbReadOnlyTransaction<["exchanges", "exchangeDetails"]>,
+ req: PaymentRestrictionsForBalance,
): Promise<AcceptableExchanges> {
const acceptableExchangeUrls = new Set<string>();
const depositableExchangeUrls = new Set<string>();
- await wex.db.runReadOnlyTx(["exchanges", "exchangeDetails"], async (tx) => {
- // FIXME: We should have a DB index to look up all exchanges
- // for a particular auditor ...
+ // FIXME: We should have a DB index to look up all exchanges
+ // for a particular auditor ...
- const canonExchanges = new Set<string>();
- const canonAuditors = new Set<string>();
+ const canonExchanges = new Set<string>();
+ const canonAuditors = new Set<string>();
- for (const exchangeHandle of req.acceptedExchanges) {
+ if (req.restrictExchanges) {
+ for (const exchangeHandle of req.restrictExchanges.exchanges) {
const normUrl = canonicalizeBaseUrl(exchangeHandle.exchangeBaseUrl);
canonExchanges.add(normUrl);
}
- for (const auditorHandle of req.acceptedAuditors) {
+ for (const auditorHandle of req.restrictExchanges.auditors) {
const normUrl = canonicalizeBaseUrl(auditorHandle.auditorBaseUrl);
canonAuditors.add(normUrl);
}
+ }
- await tx.exchanges.iter().forEachAsync(async (exchange) => {
- const dp = exchange.detailsPointer;
- if (!dp) {
- return;
- }
- const { currency, masterPublicKey } = dp;
- const exchangeDetails = await tx.exchangeDetails.indexes.byPointer.get([
- exchange.baseUrl,
- currency,
- masterPublicKey,
- ]);
- if (!exchangeDetails) {
- return;
- }
+ await tx.exchanges.iter().forEachAsync(async (exchange) => {
+ const dp = exchange.detailsPointer;
+ if (!dp) {
+ return;
+ }
+ const { currency, masterPublicKey } = dp;
+ const exchangeDetails = await tx.exchangeDetails.indexes.byPointer.get([
+ exchange.baseUrl,
+ currency,
+ masterPublicKey,
+ ]);
+ if (!exchangeDetails) {
+ return;
+ }
- let acceptable = false;
+ let acceptable = false;
- if (canonExchanges.has(exchange.baseUrl)) {
+ if (canonExchanges.has(exchange.baseUrl)) {
+ acceptableExchangeUrls.add(exchange.baseUrl);
+ acceptable = true;
+ }
+ for (const exchangeAuditor of exchangeDetails.auditors) {
+ if (canonAuditors.has(exchangeAuditor.auditor_url)) {
acceptableExchangeUrls.add(exchange.baseUrl);
acceptable = true;
+ break;
}
- for (const exchangeAuditor of exchangeDetails.auditors) {
- if (canonAuditors.has(exchangeAuditor.auditor_url)) {
- acceptableExchangeUrls.add(exchange.baseUrl);
- acceptable = true;
- break;
- }
- }
+ }
- if (!acceptable) {
- return;
- }
- // FIXME: Also consider exchange and auditor public key
- // instead of just base URLs?
+ if (!acceptable) {
+ return;
+ }
+ // FIXME: Also consider exchange and auditor public key
+ // instead of just base URLs?
+
+ let wireMethodSupported = false;
- let wireMethodSupported = false;
+ if (req.restrictWireMethods) {
for (const acc of exchangeDetails.wireInfo.accounts) {
const pp = parsePaytoUri(acc.payto_uri);
checkLogicInvariant(!!pp);
- for (const wm of req.acceptedWireMethods) {
+ for (const wm of req.restrictWireMethods) {
if (pp.targetType === wm) {
wireMethodSupported = true;
break;
@@ -564,12 +572,14 @@ export async function getAcceptableExchangeBaseUrls(
}
}
}
+ } else {
+ wireMethodSupported = true;
+ }
- acceptableExchangeUrls.add(exchange.baseUrl);
- if (wireMethodSupported) {
- depositableExchangeUrls.add(exchange.baseUrl);
- }
- });
+ acceptableExchangeUrls.add(exchange.baseUrl);
+ if (wireMethodSupported) {
+ depositableExchangeUrls.add(exchange.baseUrl);
+ }
});
return {
acceptableExchanges: [...acceptableExchangeUrls],
@@ -606,9 +616,24 @@ export interface MerchantPaymentBalanceDetails {
export async function getMerchantPaymentBalanceDetails(
wex: WalletExecutionContext,
- req: MerchantPaymentRestrictionsForBalance,
+ req: PaymentRestrictionsForBalance,
): Promise<MerchantPaymentBalanceDetails> {
- const acceptability = await getAcceptableExchangeBaseUrls(wex, req);
+ return await wex.db.runReadOnlyTx(
+ ["coinAvailability", "refreshGroups", "exchanges", "exchangeDetails"],
+ async (tx) => {
+ return getMerchantPaymentBalanceDetailsInTx(wex, tx, req);
+ },
+ );
+}
+
+export async function getMerchantPaymentBalanceDetailsInTx(
+ wex: WalletExecutionContext,
+ tx: WalletDbReadOnlyTransaction<
+ ["coinAvailability", "refreshGroups", "exchanges", "exchangeDetails"]
+ >,
+ req: PaymentRestrictionsForBalance,
+): Promise<MerchantPaymentBalanceDetails> {
+ const acceptability = await getAcceptableExchangeBaseUrlsInTx(wex, tx, req);
const d: MerchantPaymentBalanceDetails = {
balanceAvailable: Amounts.zeroOfCurrency(req.currency),
@@ -618,53 +643,46 @@ export async function getMerchantPaymentBalanceDetails(
balanceMerchantDepositable: Amounts.zeroOfCurrency(req.currency),
};
- await wex.db.runReadOnlyTx(
- ["coinAvailability", "refreshGroups"],
- async (tx) => {
- await tx.coinAvailability.iter().forEach((ca) => {
- if (ca.currency != req.currency) {
- return;
- }
- const singleCoinAmount: AmountJson = Amounts.parseOrThrow(ca.value);
- const coinAmount: AmountJson = Amounts.mult(
- singleCoinAmount,
- ca.freshCoinCount,
+ await tx.coinAvailability.iter().forEach((ca) => {
+ if (ca.currency != req.currency) {
+ return;
+ }
+ const singleCoinAmount: AmountJson = Amounts.parseOrThrow(ca.value);
+ const coinAmount: AmountJson = Amounts.mult(
+ singleCoinAmount,
+ ca.freshCoinCount,
+ ).amount;
+ d.balanceAvailable = Amounts.add(d.balanceAvailable, coinAmount).amount;
+ d.balanceMaterial = Amounts.add(d.balanceMaterial, coinAmount).amount;
+ if (ca.maxAge === 0 || ca.maxAge > req.minAge) {
+ d.balanceAgeAcceptable = Amounts.add(
+ d.balanceAgeAcceptable,
+ coinAmount,
+ ).amount;
+ if (acceptability.acceptableExchanges.includes(ca.exchangeBaseUrl)) {
+ d.balanceMerchantAcceptable = Amounts.add(
+ d.balanceMerchantAcceptable,
+ coinAmount,
).amount;
- d.balanceAvailable = Amounts.add(d.balanceAvailable, coinAmount).amount;
- d.balanceMaterial = Amounts.add(d.balanceMaterial, coinAmount).amount;
- if (ca.maxAge === 0 || ca.maxAge > req.minAge) {
- d.balanceAgeAcceptable = Amounts.add(
- d.balanceAgeAcceptable,
+ if (acceptability.depositableExchanges.includes(ca.exchangeBaseUrl)) {
+ d.balanceMerchantDepositable = Amounts.add(
+ d.balanceMerchantDepositable,
coinAmount,
).amount;
- if (acceptability.acceptableExchanges.includes(ca.exchangeBaseUrl)) {
- d.balanceMerchantAcceptable = Amounts.add(
- d.balanceMerchantAcceptable,
- coinAmount,
- ).amount;
- if (
- acceptability.depositableExchanges.includes(ca.exchangeBaseUrl)
- ) {
- d.balanceMerchantDepositable = Amounts.add(
- d.balanceMerchantDepositable,
- coinAmount,
- ).amount;
- }
- }
}
- });
+ }
+ }
+ });
- await tx.refreshGroups.iter().forEach((r) => {
- if (r.currency != req.currency) {
- return;
- }
- d.balanceAvailable = Amounts.add(
- d.balanceAvailable,
- computeRefreshGroupAvailableAmount(r),
- ).amount;
- });
- },
- );
+ await tx.refreshGroups.iter().forEach((r) => {
+ if (r.currency != req.currency) {
+ return;
+ }
+ d.balanceAvailable = Amounts.add(
+ d.balanceAvailable,
+ computeRefreshGroupAvailableAmount(r),
+ ).amount;
+ });
return d;
}
@@ -697,9 +715,11 @@ export async function getBalanceDetail(
return await getMerchantPaymentBalanceDetails(wex, {
currency: req.currency,
- acceptedAuditors: [],
- acceptedExchanges: exchanges,
- acceptedWireMethods: wires,
+ restrictExchanges: {
+ auditors: [],
+ exchanges,
+ },
+ restrictWireMethods: wires,
minAge: 0,
});
}
@@ -763,3 +783,50 @@ export async function getPeerPaymentBalanceDetailsInTx(
balanceMaterial,
};
}
+
+/**
+ * Get information about the balance at a given exchange
+ * with certain restrictions.
+ */
+export async function getExchangePaymentBalanceDetailsInTx(
+ wex: WalletExecutionContext,
+ tx: WalletDbReadOnlyTransaction<["coinAvailability", "refreshGroups"]>,
+ req: PeerPaymentRestrictionsForBalance,
+): Promise<PeerPaymentBalanceDetails> {
+ let balanceAvailable = Amounts.zeroOfCurrency(req.currency);
+ let balanceMaterial = Amounts.zeroOfCurrency(req.currency);
+
+ await tx.coinAvailability.iter().forEach((ca) => {
+ if (ca.currency != req.currency) {
+ return;
+ }
+ if (
+ req.restrictExchangeTo &&
+ req.restrictExchangeTo !== ca.exchangeBaseUrl
+ ) {
+ return;
+ }
+ const singleCoinAmount: AmountJson = Amounts.parseOrThrow(ca.value);
+ const coinAmount: AmountJson = Amounts.mult(
+ singleCoinAmount,
+ ca.freshCoinCount,
+ ).amount;
+ balanceAvailable = Amounts.add(balanceAvailable, coinAmount).amount;
+ balanceMaterial = Amounts.add(balanceMaterial, coinAmount).amount;
+ });
+
+ await tx.refreshGroups.iter().forEach((r) => {
+ if (r.currency != req.currency) {
+ return;
+ }
+ balanceAvailable = Amounts.add(
+ balanceAvailable,
+ computeRefreshGroupAvailableAmount(r),
+ ).amount;
+ });
+
+ return {
+ balanceAvailable,
+ balanceMaterial,
+ };
+}