From df6f50028339c033982b94662e8ab465383095b3 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 20 Feb 2024 02:19:41 +0100 Subject: wallet-core: count deposits towards pendingOutgoing --- .../src/integrationtests/test-deposit.ts | 10 ++++- packages/taler-wallet-core/src/balance.ts | 30 ++++++++++++++ packages/taler-wallet-core/src/db.ts | 3 +- packages/taler-wallet-core/src/deposits.ts | 46 ++++++++++++++++++++-- packages/taler-wallet-core/src/withdraw.ts | 16 +++++--- 5 files changed, 93 insertions(+), 12 deletions(-) (limited to 'packages') diff --git a/packages/taler-harness/src/integrationtests/test-deposit.ts b/packages/taler-harness/src/integrationtests/test-deposit.ts index 4339e75db..fa5750d0c 100644 --- a/packages/taler-harness/src/integrationtests/test-deposit.ts +++ b/packages/taler-harness/src/integrationtests/test-deposit.ts @@ -22,6 +22,7 @@ import { NotificationType, TransactionMajorState, TransactionMinorState, + j2s, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState, generateRandomPayto } from "../harness/harness.js"; @@ -29,7 +30,6 @@ import { createSimpleTestkudosEnvironmentV2, withdrawViaBankV2, } from "../harness/helpers.js"; -import { defaultCoinConfig } from "../harness/denomStructures.js"; /** * Run test for basic, bank-integrated withdrawal and payment. @@ -84,6 +84,10 @@ export async function runDepositTest(t: GlobalTestState) { t.assertDeepEqual(depositGroupResult.transactionId, depositTxId); + const balDuring = await walletClient.call(WalletApiOperation.GetBalances, {}); + console.log(`balances during deposit: ${j2s(balDuring)}`); + t.assertAmountEquals(balDuring.balances[0].pendingOutgoing, "TESTKUDOS:10"); + await depositTrack; await exchange.runAggregatorOnceWithTimetravel({ @@ -103,6 +107,10 @@ export async function runDepositTest(t: GlobalTestState) { // The raw amount is what ends up on the bank account, which includes // deposit and wire fees. t.assertDeepEqual(transactions.transactions[1].amountRaw, "TESTKUDOS:9.79"); + + const balAfter = await walletClient.call(WalletApiOperation.GetBalances, {}); + console.log(`balances after deposit: ${j2s(balAfter)}`); + t.assertAmountEquals(balAfter.balances[0].pendingOutgoing, "TESTKUDOS:0"); } runDepositTest.suites = ["wallet"]; diff --git a/packages/taler-wallet-core/src/balance.ts b/packages/taler-wallet-core/src/balance.ts index e63ea6641..94a500384 100644 --- a/packages/taler-wallet-core/src/balance.ts +++ b/packages/taler-wallet-core/src/balance.ts @@ -196,6 +196,15 @@ class BalancesStore { b.pendingIncoming = Amounts.add(b.pendingIncoming, amount).amount; } + async addPendingOutgoing( + currency: string, + exchangeBaseUrl: string, + amount: AmountLike, + ): Promise { + const b = await this.initBalance(currency, exchangeBaseUrl); + b.pendingOutgoing = Amounts.add(b.pendingOutgoing, amount).amount; + } + async setFlagIncomingAml( currency: string, exchangeBaseUrl: string, @@ -388,6 +397,27 @@ export async function getBalancesInsideTransaction( case DepositOperationStatus.PendingKyc: await balanceStore.setFlagOutgoingKyc(currency, e); } + + switch (dgRecord.operationStatus) { + case DepositOperationStatus.SuspendedKyc: + case DepositOperationStatus.PendingKyc: + case DepositOperationStatus.PendingTrack: + case DepositOperationStatus.SuspendedAborting: + case DepositOperationStatus.SuspendedDeposit: + case DepositOperationStatus.SuspendedTrack: + case DepositOperationStatus.PendingDeposit: { + const perExchange = dgRecord.infoPerExchange; + if (perExchange) { + for (const [e, v] of Object.entries(perExchange)) { + await balanceStore.addPendingOutgoing( + currency, + e, + v.amountEffective, + ); + } + } + } + } } }); diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 33f962339..672c1d9aa 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -30,7 +30,6 @@ import { import { AbsoluteTime, AgeCommitmentProof, - AmountJson, AmountString, Amounts, AttentionInfo, @@ -1724,7 +1723,7 @@ export interface DepositInfoPerExchange { * Expected effective amount that will be deposited * from coins of this exchange. */ - amountEffective: AmountJson; + amountEffective: AmountString; } /** diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts index 6f247501e..b327632d3 100644 --- a/packages/taler-wallet-core/src/deposits.ts +++ b/packages/taler-wallet-core/src/deposits.ts @@ -81,6 +81,7 @@ import { import { DepositElementStatus, DepositGroupRecord, + DepositInfoPerExchange, DepositOperationStatus, DepositTrackingInfo, KycPendingInfo, @@ -226,6 +227,10 @@ export class DepositTransactionContext implements TransactionContext { ws.taskScheduler.stopShepherdTask(retryTag); notifyTransition(ws, transactionId, transitionInfo); ws.taskScheduler.startShepherdTask(retryTag); + ws.notify({ + type: NotificationType.BalanceChange, + hintTransactionId: transactionId, + }); } async resumeTransaction(): Promise { @@ -300,6 +305,10 @@ export class DepositTransactionContext implements TransactionContext { ); ws.taskScheduler.stopShepherdTask(retryTag); notifyTransition(ws, transactionId, transitionInfo); + ws.notify({ + type: NotificationType.BalanceChange, + hintTransactionId: transactionId, + }); } } @@ -458,6 +467,10 @@ async function waitForRefreshOnDepositGroup( ); notifyTransition(ws, transactionId, transitionInfo); + ws.notify({ + type: NotificationType.BalanceChange, + hintTransactionId: transactionId, + }); return TaskRunResult.backoff(); } @@ -610,10 +623,6 @@ async function processDepositGroupPendingKyc( tag: TransactionType.Deposit, depositGroupId, }); - const retryTag = constructTaskIdentifier({ - tag: PendingTaskType.Deposit, - depositGroupId, - }); const kycInfo = depositGroup.kycInfo; const userType = "individual"; @@ -873,6 +882,10 @@ async function processDepositGroupPendingTrack( }); notifyTransition(ws, transactionId, transitionInfo); if (allWired) { + ws.notify({ + type: NotificationType.BalanceChange, + hintTransactionId: transactionId, + }); return TaskRunResult.finished(); } else { // FIXME: Use long-polling. @@ -1365,6 +1378,30 @@ export async function createDepositGroup( depositGroupId = encodeCrock(getRandomBytes(32)); } + const infoPerExchange: Record = {}; + + await ws.db.runReadOnlyTx(["coins"], async (tx) => { + for (let i = 0; i < payCoinSel.coinSel.coinPubs.length; i++) { + const coin = await tx.coins.get(payCoinSel.coinSel.coinPubs[i]); + if (!coin) { + logger.error("coin not found anymore"); + continue; + } + let depPerExchange = infoPerExchange[coin.exchangeBaseUrl]; + if (!depPerExchange) { + infoPerExchange[coin.exchangeBaseUrl] = depPerExchange = { + amountEffective: Amounts.stringify( + Amounts.zeroOfAmount(totalDepositCost), + ), + }; + } + const contrib = payCoinSel.coinSel.coinContributions[i]; + depPerExchange.amountEffective = Amounts.stringify( + Amounts.add(depPerExchange.amountEffective, contrib).amount, + ); + } + }); + const counterpartyEffectiveDepositAmount = await getCounterpartyEffectiveDepositAmount( ws, @@ -1402,6 +1439,7 @@ export async function createDepositGroup( salt: wireSalt, }, operationStatus: DepositOperationStatus.PendingDeposit, + infoPerExchange, }; const ctx = new DepositTransactionContext(ws, depositGroupId); diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts index 3f4b3ef70..b34ad521d 100644 --- a/packages/taler-wallet-core/src/withdraw.ts +++ b/packages/taler-wallet-core/src/withdraw.ts @@ -1685,7 +1685,11 @@ export async function processWithdrawalGroup( case WithdrawalGroupStatus.PendingQueryingStatus: return processQueryReserve(ws, withdrawalGroupId, cancellationToken); case WithdrawalGroupStatus.PendingWaitConfirmBank: - return await processReserveBankStatus(ws, withdrawalGroupId); + return await processReserveBankStatus( + ws, + withdrawalGroupId, + cancellationToken, + ); case WithdrawalGroupStatus.PendingAml: // FIXME: Handle this case, withdrawal doesn't support AML yet. return TaskRunResult.backoff(); @@ -2233,6 +2237,7 @@ async function processBankRegisterReserve( async function processReserveBankStatus( ws: InternalWalletState, withdrawalGroupId: string, + cancellationToken: CancellationToken, ): Promise { const withdrawalGroup = await getWithdrawalGroupRecordTx(ws.db, { withdrawalGroupId, @@ -2258,15 +2263,16 @@ async function processReserveBankStatus( if (!uriResult) { throw Error(`can't parse withdrawal URL ${bankInfo.talerWithdrawUri}`); } - const url = new URL( + const bankStatusUrl = new URL( `withdrawal-operation/${uriResult.withdrawalOperationId}`, uriResult.bankIntegrationApiBaseUrl, ); - url.searchParams.set("long_poll_ms", "30000"); + bankStatusUrl.searchParams.set("long_poll_ms", "30000"); - logger.info(`long-polling for withdrawal operation at ${url.href}`); - const statusResp = await ws.http.fetch(url.href, { + logger.info(`long-polling for withdrawal operation at ${bankStatusUrl.href}`); + const statusResp = await ws.http.fetch(bankStatusUrl.href, { timeout: getReserveRequestTimeout(withdrawalGroup), + cancellationToken, }); logger.info( `long-polling for withdrawal operation returned status ${statusResp.status}`, -- cgit v1.2.3