taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 08e5fafee3ecd140ef0c59f1183b7f592ad19e11
parent fccab85a58e5eed4a9c12816ab255bc8c8b92960
Author: Florian Dold <florian@dold.me>
Date:   Wed, 11 Sep 2024 16:13:53 +0200

wallet-core: fix perExchange insufficient balance reporting

Also tests the expected behavior in the integration test

Diffstat:
Mpackages/taler-harness/src/harness/helpers.ts | 5+++--
Mpackages/taler-harness/src/integrationtests/test-fee-regression.ts | 1+
Mpackages/taler-harness/src/integrationtests/test-kyc.ts | 1+
Mpackages/taler-harness/src/integrationtests/test-revocation.ts | 1+
Mpackages/taler-harness/src/integrationtests/test-wallet-insufficient-balance.ts | 156++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mpackages/taler-harness/src/integrationtests/test-wallettesting.ts | 3++-
Mpackages/taler-wallet-core/src/balance.ts | 1-
Mpackages/taler-wallet-core/src/coinSelection.ts | 16++++++++++------
8 files changed, 141 insertions(+), 43 deletions(-)

diff --git a/packages/taler-harness/src/harness/helpers.ts b/packages/taler-harness/src/harness/helpers.ts @@ -109,6 +109,7 @@ export interface SimpleTestEnvironmentNg { */ export interface SimpleTestEnvironmentNg3 { commonDb: DbInfo; + bank: BankService; bankClient: TalerCorebankApiClient; exchange: ExchangeService; exchangeBankAccount: HarnessExchangeBankAccount; @@ -475,7 +476,7 @@ export async function createSimpleTestkudosEnvironmentV3( const exchangeBankPassword = "mypw"; const exchangePaytoUri = generateRandomPayto(exchangeBankUsername); const wireGatewayApiBaseUrl = new URL( - "accounts/exchange/taler-wire-gateway/", + `accounts/${exchangeBankUsername}/taler-wire-gateway/`, bank.corebankApiBaseUrl, ).href; @@ -539,7 +540,6 @@ export async function createSimpleTestkudosEnvironmentV3( opts.additionalExchangeConfig(exchange); } await exchange.start(); - await exchange.pingUntilAvailable(); merchant.addExchange(exchange); @@ -584,6 +584,7 @@ export async function createSimpleTestkudosEnvironmentV3( merchant, walletClient, walletService, + bank, bankClient, exchangeBankAccount, }; diff --git a/packages/taler-harness/src/integrationtests/test-fee-regression.ts b/packages/taler-harness/src/integrationtests/test-fee-regression.ts @@ -189,6 +189,7 @@ export async function createMyTestkudosEnvironment( walletClient, walletService, bankClient, + bank, exchangeBankAccount: { accountName: "", accountPassword: "", diff --git a/packages/taler-harness/src/integrationtests/test-kyc.ts b/packages/taler-harness/src/integrationtests/test-kyc.ts @@ -265,6 +265,7 @@ async function createKycTestkudosEnvironment( merchant, walletClient, walletService, + bank, bankClient, exchangeBankAccount: { accountName: "", diff --git a/packages/taler-harness/src/integrationtests/test-revocation.ts b/packages/taler-harness/src/integrationtests/test-revocation.ts @@ -179,6 +179,7 @@ async function createTestEnvironment( merchant, walletClient, walletService, + bank, bankClient, exchangeBankAccount: { accountName: "", diff --git a/packages/taler-harness/src/integrationtests/test-wallet-insufficient-balance.ts b/packages/taler-harness/src/integrationtests/test-wallet-insufficient-balance.ts @@ -20,35 +20,78 @@ import { AmountString, Duration, + j2s, PaymentInsufficientBalanceDetails, TalerErrorCode, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; import { - GlobalTestState, + ExchangeService, generateRandomPayto, + GlobalTestState, + HarnessExchangeBankAccount, setupDb, } from "../harness/harness.js"; -import { createSimpleTestkudosEnvironmentV3, withdrawViaBankV3 } from "../harness/helpers.js"; +import { + createSimpleTestkudosEnvironmentV3, + withdrawViaBankV3, +} from "../harness/helpers.js"; export async function runWalletInsufficientBalanceTest(t: GlobalTestState) { // Set up test environment - const db = await setupDb(t); - const coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")); - let { - bankClient, - exchange, - merchant, - walletService, - walletClient, - } = await createSimpleTestkudosEnvironmentV3(t, coinConfig, { - skipWireFeeCreation: true, + let { bankClient, bank, exchange, merchant, walletClient } = + await createSimpleTestkudosEnvironmentV3(t, coinConfig, { + skipWireFeeCreation: true, + }); + + const dbTwo = await setupDb(t, { + nameSuffix: "two", + }); + + const exchangeTwo = ExchangeService.create(t, { + name: "testexchange-2", + currency: "TESTKUDOS", + httpPort: 9081, + database: dbTwo.connStr, }); + { + const receiverName = "Exchange2"; + const exchangeBankUsername = "exchange2"; + const exchangeBankPassword = "mypw"; + const exchangePaytoUri = generateRandomPayto(exchangeBankUsername); + const wireGatewayApiBaseUrl = new URL( + `accounts/${exchangeBankUsername}/taler-wire-gateway/`, + bank.corebankApiBaseUrl, + ).href; + + const exchangeBankAccount: HarnessExchangeBankAccount = { + wireGatewayApiBaseUrl, + accountName: exchangeBankUsername, + accountPassword: exchangeBankPassword, + accountPaytoUri: exchangePaytoUri, + skipWireFeeCreation: true, + }; + + await exchangeTwo.addBankAccount("1", exchangeBankAccount); + + await bankClient.registerAccountExtended({ + name: receiverName, + password: exchangeBankPassword, + username: exchangeBankUsername, + is_taler_exchange: true, + payto_uri: exchangePaytoUri, + }); + + exchangeTwo.addCoinConfigList(coinConfig); + + await exchangeTwo.start(); + } + await merchant.addInstanceWithWireAccount({ id: "default", name: "Default Instance", @@ -75,6 +118,8 @@ export async function runWalletInsufficientBalanceTest(t: GlobalTestState) { }, }); + t.logStep("setup-done"); + const wres = await withdrawViaBankV3(t, { amount: "TESTKUDOS:10", bankClient, @@ -83,29 +128,74 @@ export async function runWalletInsufficientBalanceTest(t: GlobalTestState) { }); await wres.withdrawalFinishedCond; - const exc = await t.assertThrowsTalerErrorAsync(async () => { - await walletClient.call(WalletApiOperation.CheckDeposit, { - amount: "TESTKUDOS:5" as AmountString, - depositPaytoUri: "payto://x-taler-bank/localhost/foobar", + { + const exc = await t.assertThrowsTalerErrorAsync(async () => { + await walletClient.call(WalletApiOperation.CheckDeposit, { + amount: "TESTKUDOS:5" as AmountString, + depositPaytoUri: "payto://x-taler-bank/localhost/foobar", + }); + }); + + t.assertDeepEqual( + exc.errorDetail.code, + TalerErrorCode.WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE, + ); + + const insufficientBalanceDetails: PaymentInsufficientBalanceDetails = + exc.errorDetail.insufficientBalanceDetails; + + t.assertAmountEquals( + insufficientBalanceDetails.balanceAvailable, + "TESTKUDOS:9.72", + ); + t.assertAmountEquals( + insufficientBalanceDetails.balanceExchangeDepositable, + "TESTKUDOS:0", + ); + } + + t.logStep("start-p2p-push-test"); + + // Now check for p2p-push + + { + const wres2 = await withdrawViaBankV3(t, { + amount: "TESTKUDOS:5", + bankClient, + exchange: exchangeTwo, + walletClient, + }); + await wres2.withdrawalFinishedCond; + + const exc = await t.assertThrowsTalerErrorAsync(async () => { + await walletClient.call(WalletApiOperation.CheckPeerPushDebit, { + amount: "TESTKUDOS:20" as AmountString, + }); }); - }); - t.assertDeepEqual( - exc.errorDetail.code, - TalerErrorCode.WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE, - ); - - const insufficientBalanceDetails: PaymentInsufficientBalanceDetails = - exc.errorDetail.insufficientBalanceDetails; - - t.assertAmountEquals( - insufficientBalanceDetails.balanceAvailable, - "TESTKUDOS:9.72", - ); - t.assertAmountEquals( - insufficientBalanceDetails.balanceExchangeDepositable, - "TESTKUDOS:0", - ); + const insufficientBalanceDetails: PaymentInsufficientBalanceDetails = + exc.errorDetail.insufficientBalanceDetails; + + const perMyExchange = + insufficientBalanceDetails.perExchange[exchange.baseUrl]; + + t.assertTrue(!!perMyExchange); + + console.log(j2s(exc.errorDetail)); + + t.assertAmountEquals( + insufficientBalanceDetails.amountRequested, + "TESTKUDOS:20", + ); + t.assertAmountEquals( + insufficientBalanceDetails.maxEffectiveSpendAmount, + "TESTKUDOS:14.22", + ); + t.assertAmountEquals( + perMyExchange.maxEffectiveSpendAmount, + "TESTKUDOS:9.47", + ); + } } runWalletInsufficientBalanceTest.suites = ["wallet"]; diff --git a/packages/taler-harness/src/integrationtests/test-wallettesting.ts b/packages/taler-harness/src/integrationtests/test-wallettesting.ts @@ -44,7 +44,7 @@ export async function createMyEnvironment( ): Promise<SimpleTestEnvironmentNg3> { const db = await setupDb(t); - const { bankClient, exchange, merchant, exchangeBankAccount } = + const { bankClient, bank, exchange, merchant, exchangeBankAccount } = await createSimpleTestkudosEnvironmentV3(t, coinConfig, {}); console.log("setup done!"); @@ -62,6 +62,7 @@ export async function createMyEnvironment( merchant, walletClient, walletService, + bank, bankClient, exchangeBankAccount, }; diff --git a/packages/taler-wallet-core/src/balance.ts b/packages/taler-wallet-core/src/balance.ts @@ -453,7 +453,6 @@ export async function getBalancesInsideTransaction( case DepositOperationStatus.PendingAggregateKyc: await balanceStore.setFlagOutgoingKyc(currency, e); } - switch (dgRecord.operationStatus) { case DepositOperationStatus.SuspendedAggregateKyc: case DepositOperationStatus.PendingAggregateKyc: diff --git a/packages/taler-wallet-core/src/coinSelection.ts b/packages/taler-wallet-core/src/coinSelection.ts @@ -493,12 +493,16 @@ export async function reportInsufficientBalanceDetails( let missingGlobalFees = false; const exchWire = await getExchangeWireDetailsInTx(tx, exch.baseUrl); if (!exchWire) { + // No wire details about the exchange known, skip! + continue; + } + const globalFees = getGlobalFees(exchWire); + if (!globalFees) { missingGlobalFees = true; - } else { - const globalFees = getGlobalFees(exchWire); - if (!globalFees) { - missingGlobalFees = true; - } + } + if (exchWire.currency !== Amounts.currencyOf(req.instructedAmount)) { + // Do not report anything for an exchange with a different currency. + continue; } const exchDet = await getPaymentBalanceDetailsInTx(wex, tx, { restrictExchanges: { @@ -510,7 +514,7 @@ export async function reportInsufficientBalanceDetails( ], auditors: [], }, - restrictWireMethods: req.wireMethod ? [req.wireMethod] : [], + restrictWireMethods: req.wireMethod ? [req.wireMethod] : undefined, currency: Amounts.currencyOf(req.instructedAmount), minAge: req.requiredMinimumAge ?? 0, depositPaytoUri: req.depositPaytoUri,