aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2024-11-18 20:02:27 +0100
committerFlorian Dold <florian@dold.me>2024-11-18 20:02:27 +0100
commita00ae494c4be0a66b2ce884cba87aec912cf0ecd (patch)
treecd1c8673da1ae6252fbc92f4761245d05f636b5e
parentd72c1470c2562b7e4d99108e27ec3d6508b1b625 (diff)
wallet-core: tolerate zero amounts in peer-push-debit and peer-pull-credit checkHEADmaster
-rw-r--r--packages/taler-harness/src/integrationtests/test-peer-to-peer-pull.ts33
-rw-r--r--packages/taler-harness/src/integrationtests/test-peer-to-peer-push.ts9
-rw-r--r--packages/taler-wallet-core/src/coinSelection.ts4
-rw-r--r--packages/taler-wallet-core/src/exchanges.ts63
-rw-r--r--packages/taler-wallet-core/src/pay-peer-pull-credit.ts21
-rw-r--r--packages/taler-wallet-core/src/pay-peer-push-debit.ts21
-rw-r--r--packages/taler-wallet-core/src/withdraw.ts5
7 files changed, 81 insertions, 75 deletions
diff --git a/packages/taler-harness/src/integrationtests/test-peer-to-peer-pull.ts b/packages/taler-harness/src/integrationtests/test-peer-to-peer-pull.ts
index 6ad07efed..145cbcee6 100644
--- a/packages/taler-harness/src/integrationtests/test-peer-to-peer-pull.ts
+++ b/packages/taler-harness/src/integrationtests/test-peer-to-peer-pull.ts
@@ -23,6 +23,7 @@ import {
Duration,
j2s,
NotificationType,
+ ScopeType,
TalerCorebankApiClient,
TransactionMajorState,
TransactionMinorState,
@@ -31,15 +32,15 @@ import {
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import {
- ExchangeService,
- GlobalTestState,
- WalletClient,
-} from "../harness/harness.js";
-import {
createSimpleTestkudosEnvironmentV3,
createWalletDaemonWithClient,
withdrawViaBankV3,
} from "../harness/environments.js";
+import {
+ ExchangeService,
+ GlobalTestState,
+ WalletClient,
+} from "../harness/harness.js";
/**
* Run test for basic, bank-integrated withdrawal and payment.
@@ -104,6 +105,28 @@ async function checkNormalPeerPull(
),
);
+ const checkPullCreditResp1 = await wallet1.client.call(
+ WalletApiOperation.CheckPeerPullCredit,
+ {
+ amount: "TESTKUDOS:5" as AmountString,
+ exchangeBaseUrl: exchange.baseUrl,
+ },
+ );
+
+ t.assertDeepEqual(checkPullCreditResp1.amountRaw, "TESTKUDOS:5");
+
+ const checkPullCreditResp2 = await wallet1.client.call(
+ WalletApiOperation.CheckPeerPullCredit,
+ {
+ amount: "TESTKUDOS:0" as AmountString,
+ exchangeBaseUrl: exchange.baseUrl,
+ },
+ );
+
+ t.assertDeepEqual(checkPullCreditResp2.amountRaw, "TESTKUDOS:0");
+ t.assertDeepEqual(checkPullCreditResp2.amountEffective, "TESTKUDOS:0");
+ t.assertDeepEqual(checkPullCreditResp2.numCoins, 0);
+
const resp = await wallet1.client.call(
WalletApiOperation.InitiatePeerPullCredit,
{
diff --git a/packages/taler-harness/src/integrationtests/test-peer-to-peer-push.ts b/packages/taler-harness/src/integrationtests/test-peer-to-peer-push.ts
index 953ea6088..fcddd1da4 100644
--- a/packages/taler-harness/src/integrationtests/test-peer-to-peer-push.ts
+++ b/packages/taler-harness/src/integrationtests/test-peer-to-peer-push.ts
@@ -89,6 +89,15 @@ export async function runPeerToPeerPushTest(t: GlobalTestState) {
),
);
+ {
+ const checkRespSmall = await w1.walletClient.call(
+ WalletApiOperation.CheckPeerPushDebit,
+ {
+ amount: "TESTKUDOS:0" as AmountString,
+ },
+ );
+ }
+
const checkResp0 = await w1.walletClient.call(
WalletApiOperation.CheckPeerPushDebit,
{
diff --git a/packages/taler-wallet-core/src/coinSelection.ts b/packages/taler-wallet-core/src/coinSelection.ts
index de051d52e..1d9ccf9ad 100644
--- a/packages/taler-wallet-core/src/coinSelection.ts
+++ b/packages/taler-wallet-core/src/coinSelection.ts
@@ -1204,7 +1204,9 @@ export async function selectPeerCoinsInTx(
if (Amounts.isZero(instructedAmount)) {
// Other parts of the code assume that we have at least
// one coin to spend.
- throw new Error("peer-to-peer payment with amount of zero not supported");
+ throw new Error(
+ "coin selection for peer-to-peer payment with amount of zero not supported",
+ );
}
const exchanges = await tx.exchanges.iter().toArray();
diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts
index c4e6b81bf..7e95d234b 100644
--- a/packages/taler-wallet-core/src/exchanges.ts
+++ b/packages/taler-wallet-core/src/exchanges.ts
@@ -3650,58 +3650,11 @@ export async function checkExchangeInScopeTx(
export async function getPreferredExchangeForCurrency(
wex: WalletExecutionContext,
currency: string,
+ restrictScope?: ScopeInfo,
): Promise<string | undefined> {
- // Find an exchange with the matching currency.
- // Prefer exchanges with the most recent withdrawal.
- const url = await wex.db.runReadOnlyTx(
- { storeNames: ["exchanges"] },
- async (tx) => {
- const exchanges = await tx.exchanges.iter().toArray();
- let candidate = undefined;
- for (const e of exchanges) {
- if (e.detailsPointer?.currency !== currency) {
- continue;
- }
- if (!candidate) {
- candidate = e;
- continue;
- }
- if (candidate.lastWithdrawal && !e.lastWithdrawal) {
- continue;
- }
- const exchangeLastWithdrawal = timestampOptionalPreciseFromDb(
- e.lastWithdrawal,
- );
- const candidateLastWithdrawal = timestampOptionalPreciseFromDb(
- candidate.lastWithdrawal,
- );
- if (exchangeLastWithdrawal && candidateLastWithdrawal) {
- if (
- AbsoluteTime.cmp(
- AbsoluteTime.fromPreciseTimestamp(exchangeLastWithdrawal),
- AbsoluteTime.fromPreciseTimestamp(candidateLastWithdrawal),
- ) > 0
- ) {
- candidate = e;
- }
- }
- }
- if (candidate) {
- return candidate.baseUrl;
- }
- return undefined;
- },
- );
- return url;
-}
-
-/**
- * Find a preferred exchange based on when we withdrew last from this exchange.
- */
-export async function getPreferredExchangeForScope(
- wex: WalletExecutionContext,
- scope: ScopeInfo,
-): Promise<string | undefined> {
+ if (restrictScope) {
+ checkLogicInvariant(restrictScope.currency === currency);
+ }
// Find an exchange with the matching currency.
// Prefer exchanges with the most recent withdrawal.
const url = await wex.db.runReadOnlyTx(
@@ -3717,11 +3670,13 @@ export async function getPreferredExchangeForScope(
const exchanges = await tx.exchanges.iter().toArray();
let candidate = undefined;
for (const e of exchanges) {
- const inScope = await checkExchangeInScopeTx(wex, tx, e.baseUrl, scope);
- if (!inScope) {
+ if (e.detailsPointer?.currency !== currency) {
continue;
}
- if (e.detailsPointer?.currency !== scope.currency) {
+ const inScope =
+ !restrictScope ||
+ (await checkExchangeInScopeTx(wex, tx, e.baseUrl, restrictScope));
+ if (!inScope) {
continue;
}
if (!candidate) {
diff --git a/packages/taler-wallet-core/src/pay-peer-pull-credit.ts b/packages/taler-wallet-core/src/pay-peer-pull-credit.ts
index a92d8e764..e5a9d9675 100644
--- a/packages/taler-wallet-core/src/pay-peer-pull-credit.ts
+++ b/packages/taler-wallet-core/src/pay-peer-pull-credit.ts
@@ -93,7 +93,6 @@ import {
checkIncomingAmountLegalUnderKycBalanceThreshold,
fetchFreshExchange,
getPreferredExchangeForCurrency,
- getPreferredExchangeForScope,
getScopeForAllExchanges,
handleStartExchangeWalletKyc,
} from "./exchanges.js";
@@ -1356,6 +1355,8 @@ export async function internalCheckPeerPullCredit(
`checking peer push debit for ${Amounts.stringify(instructedAmount)}`,
);
+ // FIXME: Create helper to avoid code duplication with pull credit initiation
+
let restrictScope: ScopeInfo;
if (req.restrictScope) {
restrictScope = req.restrictScope;
@@ -1371,14 +1372,11 @@ export async function internalCheckPeerPullCredit(
logger.trace("checking peer-pull-credit fees");
- let exchangeUrl: string | undefined;
- if (req.exchangeBaseUrl) {
- exchangeUrl = req.exchangeBaseUrl;
- } else if (req.restrictScope) {
- exchangeUrl = await getPreferredExchangeForScope(wex, req.restrictScope);
- } else {
- exchangeUrl = await getPreferredExchangeForCurrency(wex, currency);
- }
+ const exchangeUrl = await getPreferredExchangeForCurrency(
+ wex,
+ restrictScope.currency,
+ restrictScope,
+ );
if (!exchangeUrl) {
throw Error("no exchange found for initiating a peer pull payment");
@@ -1392,10 +1390,9 @@ export async function internalCheckPeerPullCredit(
Amounts.parseOrThrow(req.amount),
undefined,
);
+
if (wi.selectedDenoms.selectedDenoms.length === 0) {
- throw Error(
- `unable to check pull payment from ${exchangeUrl}, can't select denominations for instructed amount (${req.amount}`,
- );
+ logger.warn(`Amount for peer-pull-credit payment too low`);
}
logger.trace(`got withdrawal info`);
diff --git a/packages/taler-wallet-core/src/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/pay-peer-push-debit.ts
index 31d320259..fb2c98173 100644
--- a/packages/taler-wallet-core/src/pay-peer-push-debit.ts
+++ b/packages/taler-wallet-core/src/pay-peer-push-debit.ts
@@ -82,7 +82,10 @@ import {
timestampProtocolFromDb,
timestampProtocolToDb,
} from "./db.js";
-import { getScopeForAllExchanges } from "./exchanges.js";
+import {
+ getPreferredExchangeForCurrency,
+ getScopeForAllExchanges,
+} from "./exchanges.js";
import {
codecForExchangePurseStatus,
getTotalPeerPaymentCost,
@@ -468,6 +471,22 @@ async function internalCheckPeerPushDebit(
url: req.exchangeBaseUrl,
};
}
+ if (Amounts.isZero(req.amount)) {
+ const exchangeBaseUrl = await getPreferredExchangeForCurrency(
+ wex,
+ currency,
+ restrictScope,
+ );
+ if (!exchangeBaseUrl) {
+ throw Error("no exchange found for payment");
+ }
+ return {
+ amountEffective: req.amount,
+ amountRaw: req.amount,
+ exchangeBaseUrl,
+ maxExpirationDate: TalerProtocolTimestamp.never(),
+ };
+ }
const coinSelRes = await selectPeerCoins(wex, {
instructedAmount,
restrictScope,
diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts
index 080e915d0..d1ed995a3 100644
--- a/packages/taler-wallet-core/src/withdraw.ts
+++ b/packages/taler-wallet-core/src/withdraw.ts
@@ -166,7 +166,7 @@ import {
fetchFreshExchange,
getExchangePaytoUri,
getExchangeWireDetailsInTx,
- getPreferredExchangeForScope,
+ getPreferredExchangeForCurrency,
getScopeForAllExchanges,
handleStartExchangeWalletKyc,
listExchanges,
@@ -4021,8 +4021,9 @@ export async function internalGetWithdrawalDetailsForAmount(
if (req.exchangeBaseUrl) {
exchangeBaseUrl = req.exchangeBaseUrl;
} else if (req.restrictScope) {
- exchangeBaseUrl = await getPreferredExchangeForScope(
+ exchangeBaseUrl = await getPreferredExchangeForCurrency(
wex,
+ req.restrictScope.currency,
req.restrictScope,
);
}