diff options
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, ); } |