From e1a7bf4825162b4b95669ae6b4552589f4a64217 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Mon, 19 Feb 2024 19:45:26 +0100 Subject: wallet-core: better import hygiene, cleanup withdrawals --- packages/taler-wallet-core/src/attention.ts | 2 +- packages/taler-wallet-core/src/dbless.ts | 33 ++-- packages/taler-wallet-core/src/deposits.ts | 12 +- packages/taler-wallet-core/src/exchanges.ts | 20 +- packages/taler-wallet-core/src/host-impl.node.ts | 9 +- packages/taler-wallet-core/src/index.ts | 35 +--- packages/taler-wallet-core/src/pay-merchant.ts | 10 +- .../taler-wallet-core/src/pay-peer-pull-credit.ts | 4 +- .../taler-wallet-core/src/pay-peer-pull-debit.ts | 9 +- .../taler-wallet-core/src/pay-peer-push-credit.ts | 6 +- .../taler-wallet-core/src/pay-peer-push-debit.ts | 4 +- packages/taler-wallet-core/src/refresh.ts | 10 +- packages/taler-wallet-core/src/shepherd.ts | 6 +- packages/taler-wallet-core/src/testing.ts | 6 +- packages/taler-wallet-core/src/transactions.ts | 14 +- .../taler-wallet-core/src/util/coinSelection.ts | 11 +- .../taler-wallet-core/src/util/denominations.ts | 3 +- .../src/util/instructedAmountConversion.ts | 9 +- packages/taler-wallet-core/src/withdraw.ts | 220 ++++++++++++++------- 19 files changed, 231 insertions(+), 192 deletions(-) (limited to 'packages/taler-wallet-core/src') diff --git a/packages/taler-wallet-core/src/attention.ts b/packages/taler-wallet-core/src/attention.ts index 3be1f7197..6893af010 100644 --- a/packages/taler-wallet-core/src/attention.ts +++ b/packages/taler-wallet-core/src/attention.ts @@ -28,7 +28,7 @@ import { UserAttentionsRequest, UserAttentionsResponse, } from "@gnu-taler/taler-util"; -import { timestampPreciseFromDb, timestampPreciseToDb } from "./index.js"; +import { timestampPreciseFromDb, timestampPreciseToDb } from "./db.js"; import { InternalWalletState } from "./internal-wallet-state.js"; const logger = new Logger("operations/attention.ts"); diff --git a/packages/taler-wallet-core/src/dbless.ts b/packages/taler-wallet-core/src/dbless.ts index 968d3b958..e538a6e76 100644 --- a/packages/taler-wallet-core/src/dbless.ts +++ b/packages/taler-wallet-core/src/dbless.ts @@ -29,29 +29,26 @@ import { AbsoluteTime, AgeRestriction, AmountJson, - Amounts, AmountString, + Amounts, + DenominationPubKey, + ExchangeBatchDepositRequest, + ExchangeBatchWithdrawRequest, + ExchangeMeltRequest, + ExchangeProtocolVersion, + Logger, TalerCorebankApiClient, + UnblindedSignature, codecForAny, codecForBankWithdrawalOperationPostResponse, codecForBatchDepositSuccess, codecForExchangeMeltResponse, codecForExchangeRevealResponse, - codecForWithdrawResponse, - DenominationPubKey, + codecForExchangeWithdrawBatchResponse, encodeCrock, - ExchangeBatchDepositRequest, - ExchangeMeltRequest, - ExchangeProtocolVersion, - ExchangeWithdrawRequest, getRandomBytes, hashWire, - Logger, parsePaytoUri, - UnblindedSignature, - ExchangeBatchWithdrawRequest, - ExchangeWithdrawBatchResponse, - codecForExchangeWithdrawBatchResponse, } from "@gnu-taler/taler-util"; import { HttpRequestLibrary, @@ -59,16 +56,10 @@ import { } from "@gnu-taler/taler-util/http"; import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js"; import { DenominationRecord } from "./db.js"; -import { - ExchangeInfo, - ExchangeKeysDownloadResult, - isWithdrawableDenom, -} from "./index.js"; +import { ExchangeInfo } from "./exchanges.js"; import { assembleRefreshRevealRequest } from "./refresh.js"; -import { - getBankStatusUrl, - getBankWithdrawalInfo, -} from "./withdraw.js"; +import { isWithdrawableDenom } from "./util/denominations.js"; +import { getBankStatusUrl, getBankWithdrawalInfo } from "./withdraw.js"; const logger = new Logger("dbless.ts"); diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts index b6cfa7aae..906503430 100644 --- a/packages/taler-wallet-core/src/deposits.ts +++ b/packages/taler-wallet-core/src/deposits.ts @@ -78,25 +78,24 @@ import { constructTaskIdentifier, spendCoins, } from "./common.js"; -import { DepositElementStatus, DepositGroupRecord } from "./db.js"; -import { getExchangeWireDetailsInTx } from "./exchanges.js"; import { + DepositElementStatus, + DepositGroupRecord, DepositOperationStatus, DepositTrackingInfo, KycPendingInfo, RefreshOperationStatus, - createRefreshGroup, - getCandidateWithdrawalDenomsTx, - getTotalRefreshCost, timestampPreciseToDb, timestampProtocolToDb, -} from "./index.js"; +} from "./db.js"; +import { getExchangeWireDetailsInTx } from "./exchanges.js"; import { InternalWalletState } from "./internal-wallet-state.js"; import { extractContractData, generateDepositPermissions, getTotalPaymentCost, } from "./pay-merchant.js"; +import { createRefreshGroup, getTotalRefreshCost } from "./refresh.js"; import { constructTransactionIdentifier, notifyTransition, @@ -105,6 +104,7 @@ import { import { assertUnreachable } from "./util/assertUnreachable.js"; import { selectPayCoinsNew } from "./util/coinSelection.js"; import { checkDbInvariant, checkLogicInvariant } from "./util/invariants.js"; +import { getCandidateWithdrawalDenomsTx } from "./withdraw.js"; /** * Logger. diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts index 0f080b871..2c15691aa 100644 --- a/packages/taler-wallet-core/src/exchanges.ts +++ b/packages/taler-wallet-core/src/exchanges.ts @@ -104,28 +104,28 @@ import { DenominationRecord, DenominationVerificationStatus, ExchangeDetailsRecord, - ExchangeEntryRecord, - WalletStoresV1, -} from "./db.js"; -import { ExchangeEntryDbRecordStatus, ExchangeEntryDbUpdateStatus, + ExchangeEntryRecord, WalletDbReadOnlyTransaction, WalletDbReadWriteTransaction, - createRefreshGroup, - createTimeline, - isWithdrawableDenom, - selectBestForOverlappingDenominations, - selectMinimumFee, + WalletStoresV1, timestampAbsoluteFromDb, timestampOptionalPreciseFromDb, timestampPreciseFromDb, timestampPreciseToDb, timestampProtocolFromDb, timestampProtocolToDb, -} from "./index.js"; +} from "./db.js"; import { InternalWalletState } from "./internal-wallet-state.js"; import { DbReadOnlyTransaction } from "./query.js"; +import { createRefreshGroup } from "./refresh.js"; +import { + createTimeline, + isWithdrawableDenom, + selectBestForOverlappingDenominations, + selectMinimumFee, +} from "./util/denominations.js"; import { checkDbInvariant } from "./util/invariants.js"; import { WALLET_EXCHANGE_PROTOCOL_VERSION } from "./versions.js"; diff --git a/packages/taler-wallet-core/src/host-impl.node.ts b/packages/taler-wallet-core/src/host-impl.node.ts index 622ea742e..a2c37b32b 100644 --- a/packages/taler-wallet-core/src/host-impl.node.ts +++ b/packages/taler-wallet-core/src/host-impl.node.ts @@ -25,22 +25,21 @@ import type { IDBFactory } from "@gnu-taler/idb-bridge"; // eslint-disable-next-line no-duplicate-imports import { + AccessStats, BridgeIDBFactory, MemoryBackend, createSqliteBackend, shimIndexedDB, } from "@gnu-taler/idb-bridge"; -import { AccessStats } from "@gnu-taler/idb-bridge"; +import { createNodeSqlite3Impl } from "@gnu-taler/idb-bridge/node-sqlite3-bindings"; import { Logger } from "@gnu-taler/taler-util"; +import { createPlatformHttpLib } from "@gnu-taler/taler-util/http"; import * as fs from "fs"; import { NodeThreadCryptoWorkerFactory } from "./crypto/workers/nodeThreadWorker.js"; import { SynchronousCryptoWorkerFactoryPlain } from "./crypto/workers/synchronousWorkerFactoryPlain.js"; -import { openTalerDatabase } from "./index.js"; -import { createPlatformHttpLib } from "@gnu-taler/taler-util/http"; +import { DefaultNodeWalletArgs, makeTempfileId } from "./host-common.js"; import { SetTimeoutTimerAPI } from "./util/timer.js"; import { Wallet } from "./wallet.js"; -import { DefaultNodeWalletArgs, makeTempfileId } from "./host-common.js"; -import { createNodeSqlite3Impl } from "@gnu-taler/idb-bridge/node-sqlite3-bindings"; const logger = new Logger("host-impl.node.ts"); diff --git a/packages/taler-wallet-core/src/index.ts b/packages/taler-wallet-core/src/index.ts index 2b619be69..0e1feb58f 100644 --- a/packages/taler-wallet-core/src/index.ts +++ b/packages/taler-wallet-core/src/index.ts @@ -18,40 +18,17 @@ * Module entry point for the wallet when used as a node module. */ -// Util functionality -export * from "./query.js"; - -export * from "./versions.js"; - -export * from "./db.js"; - -// Crypto and crypto workers -// export * from "./crypto/workers/nodeThreadWorker.js"; +export * from "./crypto/cryptoImplementation.js"; +export * from "./crypto/cryptoTypes.js"; export { CryptoDispatcher, CryptoWorkerFactory, } from "./crypto/workers/crypto-dispatcher.js"; export type { CryptoWorker } from "./crypto/workers/cryptoWorkerInterface.js"; - -export { InternalWalletState } from "./internal-wallet-state.js"; -export * from "./wallet-api-types.js"; -export * from "./wallet.js"; - -export * from "./backup/index.js"; - -export * from "./exchanges.js"; - -export * from "./refresh.js"; -export * from "./withdraw.js"; - -export * from "./dbless.js"; - -export * from "./crypto/cryptoImplementation.js"; -export * from "./crypto/cryptoTypes.js"; - -export * from "./util/denominations.js"; -export * from "./util/timer.js"; - export { SynchronousCryptoWorkerFactoryPlain } from "./crypto/workers/synchronousWorkerFactoryPlain.js"; +export * from "./dbless.js"; export * from "./host-common.js"; export * from "./host.js"; +export * from "./versions.js"; +export * from "./wallet-api-types.js"; +export * from "./wallet.js"; diff --git a/packages/taler-wallet-core/src/pay-merchant.ts b/packages/taler-wallet-core/src/pay-merchant.ts index 325bc24a8..15de0571c 100644 --- a/packages/taler-wallet-core/src/pay-merchant.ts +++ b/packages/taler-wallet-core/src/pay-merchant.ts @@ -114,21 +114,18 @@ import { DenominationRecord, PurchaseRecord, PurchaseStatus, - RefundReason, - WalletStoresV1, -} from "./db.js"; -import { - getCandidateWithdrawalDenomsTx, RefundGroupRecord, RefundGroupStatus, RefundItemRecord, RefundItemStatus, + RefundReason, timestampPreciseToDb, timestampProtocolFromDb, timestampProtocolToDb, WalletDbReadOnlyTransaction, WalletDbReadWriteTransaction, -} from "./index.js"; + WalletStoresV1, +} from "./db.js"; import { EXCHANGE_COINS_LOCK, InternalWalletState, @@ -147,6 +144,7 @@ import { import { assertUnreachable } from "./util/assertUnreachable.js"; import { PreviousPayCoins, selectPayCoinsNew } from "./util/coinSelection.js"; import { checkDbInvariant } from "./util/invariants.js"; +import { getCandidateWithdrawalDenomsTx } from "./withdraw.js"; /** * Logger. 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 03bc5a4b9..d862a3b3a 100644 --- a/packages/taler-wallet-core/src/pay-peer-pull-credit.ts +++ b/packages/taler-wallet-core/src/pay-peer-pull-credit.ts @@ -66,11 +66,11 @@ import { PeerPullPaymentCreditStatus, WithdrawalGroupStatus, WithdrawalRecordType, - fetchFreshExchange, timestampOptionalPreciseFromDb, timestampPreciseFromDb, timestampPreciseToDb, -} from "./index.js"; +} from "./db.js"; +import { fetchFreshExchange } from "./exchanges.js"; import { InternalWalletState } from "./internal-wallet-state.js"; import { codecForExchangePurseStatus, diff --git a/packages/taler-wallet-core/src/pay-peer-pull-debit.ts b/packages/taler-wallet-core/src/pay-peer-pull-debit.ts index 1d6cb3d18..0b4f49ce2 100644 --- a/packages/taler-wallet-core/src/pay-peer-pull-debit.ts +++ b/packages/taler-wallet-core/src/pay-peer-pull-debit.ts @@ -74,21 +74,20 @@ import { spendCoins, } from "./common.js"; import { - DbReadWriteTransaction, - InternalWalletState, PeerPullDebitRecordStatus, PeerPullPaymentIncomingRecord, RefreshOperationStatus, - StoreNames, WalletStoresV1, - createRefreshGroup, timestampPreciseToDb, -} from "./index.js"; +} from "./db.js"; +import { InternalWalletState } from "./internal-wallet-state.js"; import { codecForExchangePurseStatus, getTotalPeerPaymentCost, queryCoinInfosForSelection, } from "./pay-peer-common.js"; +import { DbReadWriteTransaction, StoreNames } from "./query.js"; +import { createRefreshGroup } from "./refresh.js"; import { constructTransactionIdentifier, notifyTransition, diff --git a/packages/taler-wallet-core/src/pay-peer-push-credit.ts b/packages/taler-wallet-core/src/pay-peer-push-credit.ts index 0795b0c5c..ed1e09495 100644 --- a/packages/taler-wallet-core/src/pay-peer-push-credit.ts +++ b/packages/taler-wallet-core/src/pay-peer-push-credit.ts @@ -60,9 +60,7 @@ import { TransactionContext, constructTaskIdentifier, } from "./common.js"; -import { fetchFreshExchange } from "./exchanges.js"; import { - InternalWalletState, KycPendingInfo, KycUserType, PeerPushCreditStatus, @@ -70,7 +68,9 @@ import { WithdrawalGroupStatus, WithdrawalRecordType, timestampPreciseToDb, -} from "./index.js"; +} from "./db.js"; +import { fetchFreshExchange } from "./exchanges.js"; +import { InternalWalletState } from "./internal-wallet-state.js"; import { codecForExchangePurseStatus, getMergeReserveInfo, 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 ec1a37a31..355418601 100644 --- a/packages/taler-wallet-core/src/pay-peer-push-debit.ts +++ b/packages/taler-wallet-core/src/pay-peer-push-debit.ts @@ -61,17 +61,17 @@ import { PeerPushDebitRecord, PeerPushDebitStatus, RefreshOperationStatus, - createRefreshGroup, timestampPreciseToDb, timestampProtocolFromDb, timestampProtocolToDb, -} from "./index.js"; +} from "./db.js"; import { InternalWalletState } from "./internal-wallet-state.js"; import { codecForExchangePurseStatus, getTotalPeerPaymentCost, queryCoinInfosForSelection, } from "./pay-peer-common.js"; +import { createRefreshGroup } from "./refresh.js"; import { constructTransactionIdentifier, notifyTransition, diff --git a/packages/taler-wallet-core/src/refresh.ts b/packages/taler-wallet-core/src/refresh.ts index f139208be..4c3bb493a 100644 --- a/packages/taler-wallet-core/src/refresh.ts +++ b/packages/taler-wallet-core/src/refresh.ts @@ -81,18 +81,15 @@ import { CoinSourceType, DenominationRecord, RefreshCoinStatus, + RefreshGroupPerExchangeInfo, RefreshGroupRecord, RefreshOperationStatus, -} from "./db.js"; -import { fetchFreshExchange } from "./exchanges.js"; -import { - getCandidateWithdrawalDenomsTx, - RefreshGroupPerExchangeInfo, RefreshSessionRecord, timestampPreciseToDb, WalletDbReadOnlyTransaction, WalletDbReadWriteTransaction, -} from "./index.js"; +} from "./db.js"; +import { fetchFreshExchange } from "./exchanges.js"; import { EXCHANGE_COINS_LOCK, InternalWalletState, @@ -104,6 +101,7 @@ import { import { assertUnreachable } from "./util/assertUnreachable.js"; import { selectWithdrawalDenominations } from "./util/coinSelection.js"; import { checkDbInvariant } from "./util/invariants.js"; +import { getCandidateWithdrawalDenomsTx } from "./withdraw.js"; const logger = new Logger("refresh.ts"); diff --git a/packages/taler-wallet-core/src/shepherd.ts b/packages/taler-wallet-core/src/shepherd.ts index 2352c844f..d6fc604e8 100644 --- a/packages/taler-wallet-core/src/shepherd.ts +++ b/packages/taler-wallet-core/src/shepherd.ts @@ -49,14 +49,14 @@ import { parseTaskIdentifier, } from "./common.js"; import { CryptoApiStoppedError } from "./crypto/workers/crypto-dispatcher.js"; -import { processDepositGroup } from "./deposits.js"; -import { updateExchangeFromUrlHandler } from "./exchanges.js"; import { OPERATION_STATUS_ACTIVE_FIRST, OPERATION_STATUS_ACTIVE_LAST, WalletDbAllStoresReadOnlyTransaction, timestampAbsoluteFromDb, -} from "./index.js"; +} from "./db.js"; +import { processDepositGroup } from "./deposits.js"; +import { updateExchangeFromUrlHandler } from "./exchanges.js"; import { InternalWalletState } from "./internal-wallet-state.js"; import { processPurchase } from "./pay-merchant.js"; import { processPeerPullCredit } from "./pay-peer-pull-credit.js"; diff --git a/packages/taler-wallet-core/src/testing.ts b/packages/taler-wallet-core/src/testing.ts index 38b2471e3..8192bacfd 100644 --- a/packages/taler-wallet-core/src/testing.ts +++ b/packages/taler-wallet-core/src/testing.ts @@ -56,12 +56,10 @@ import { HttpRequestLibrary, readSuccessResponseJsonOrThrow, } from "@gnu-taler/taler-util/http"; -import { getRefreshesForTransaction } from "./index.js"; -import { InternalWalletState } from "./internal-wallet-state.js"; -import { checkLogicInvariant } from "./util/invariants.js"; import { getBalances } from "./balance.js"; import { createDepositGroup } from "./deposits.js"; import { fetchFreshExchange } from "./exchanges.js"; +import { InternalWalletState } from "./internal-wallet-state.js"; import { confirmPay, preparePayForUri, @@ -77,7 +75,9 @@ import { preparePeerPushCredit, } from "./pay-peer-push-credit.js"; import { initiatePeerPushDebit } from "./pay-peer-push-debit.js"; +import { getRefreshesForTransaction } from "./refresh.js"; import { getTransactionById, getTransactions } from "./transactions.js"; +import { checkLogicInvariant } from "./util/invariants.js"; import { acceptWithdrawalFromUri } from "./withdraw.js"; const logger = new Logger("operations/testing.ts"); diff --git a/packages/taler-wallet-core/src/transactions.ts b/packages/taler-wallet-core/src/transactions.ts index 8c6c3f5aa..2050abac2 100644 --- a/packages/taler-wallet-core/src/transactions.ts +++ b/packages/taler-wallet-core/src/transactions.ts @@ -58,12 +58,15 @@ import { import { DepositElementStatus, DepositGroupRecord, + OPERATION_STATUS_ACTIVE_FIRST, + OPERATION_STATUS_ACTIVE_LAST, OperationRetryRecord, PeerPullCreditRecord, PeerPullDebitRecordStatus, PeerPullPaymentIncomingRecord, PeerPushCreditStatus, PeerPushDebitRecord, + PeerPushDebitStatus, PeerPushPaymentIncomingRecord, PurchaseRecord, PurchaseStatus, @@ -71,6 +74,9 @@ import { RefreshOperationStatus, RefundGroupRecord, RewardRecord, + timestampPreciseFromDb, + timestampProtocolFromDb, + WalletDbReadOnlyTransaction, WithdrawalGroupRecord, WithdrawalGroupStatus, WithdrawalRecordType, @@ -84,14 +90,6 @@ import { ExchangeWireDetails, getExchangeWireDetailsInTx, } from "./exchanges.js"; -import { - OPERATION_STATUS_ACTIVE_FIRST, - OPERATION_STATUS_ACTIVE_LAST, - PeerPushDebitStatus, - timestampPreciseFromDb, - timestampProtocolFromDb, - WalletDbReadOnlyTransaction, -} from "./index.js"; import { InternalWalletState } from "./internal-wallet-state.js"; import { computePayMerchantTransactionActions, diff --git a/packages/taler-wallet-core/src/util/coinSelection.ts b/packages/taler-wallet-core/src/util/coinSelection.ts index f33891c88..88dd08f63 100644 --- a/packages/taler-wallet-core/src/util/coinSelection.ts +++ b/packages/taler-wallet-core/src/util/coinSelection.ts @@ -54,18 +54,15 @@ import { TalerProtocolTimestamp, UnblindedSignature, } from "@gnu-taler/taler-util"; -import { DenominationRecord } from "../db.js"; -import { - getExchangeWireDetailsInTx, - isWithdrawableDenom, - WalletDbReadOnlyTransaction, -} from "../index.js"; -import { InternalWalletState } from "../internal-wallet-state.js"; import { getMerchantPaymentBalanceDetails, getPeerPaymentBalanceDetailsInTx, } from "../balance.js"; import { getAutoRefreshExecuteThreshold } from "../common.js"; +import { DenominationRecord, WalletDbReadOnlyTransaction } from "../db.js"; +import { getExchangeWireDetailsInTx } from "../exchanges.js"; +import { InternalWalletState } from "../internal-wallet-state.js"; +import { isWithdrawableDenom } from "./denominations.js"; import { checkDbInvariant, checkLogicInvariant } from "./invariants.js"; const logger = new Logger("coinSelection.ts"); diff --git a/packages/taler-wallet-core/src/util/denominations.ts b/packages/taler-wallet-core/src/util/denominations.ts index db6e69956..9557c078a 100644 --- a/packages/taler-wallet-core/src/util/denominations.ts +++ b/packages/taler-wallet-core/src/util/denominations.ts @@ -27,8 +27,7 @@ import { TalerProtocolTimestamp, TimePoint, } from "@gnu-taler/taler-util"; -import { DenominationRecord } from "../db.js"; -import { timestampProtocolFromDb } from "../index.js"; +import { DenominationRecord, timestampProtocolFromDb } from "../db.js"; /** * Given a list of denominations with the same value and same period of time: diff --git a/packages/taler-wallet-core/src/util/instructedAmountConversion.ts b/packages/taler-wallet-core/src/util/instructedAmountConversion.ts index c4a2f2d5c..f01dc4e21 100644 --- a/packages/taler-wallet-core/src/util/instructedAmountConversion.ts +++ b/packages/taler-wallet-core/src/util/instructedAmountConversion.ts @@ -30,12 +30,9 @@ import { parsePaytoUri, strcmp, } from "@gnu-taler/taler-util"; -import { - DenominationRecord, - InternalWalletState, - getExchangeWireDetailsInTx, - timestampProtocolFromDb, -} from "../index.js"; +import { DenominationRecord, timestampProtocolFromDb } from "../db.js"; +import { getExchangeWireDetailsInTx } from "../exchanges.js"; +import { InternalWalletState } from "../internal-wallet-state.js"; import { CoinInfo } from "./coinSelection.js"; import { checkDbInvariant } from "./invariants.js"; diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts index 7e9b295bd..4979b2623 100644 --- a/packages/taler-wallet-core/src/withdraw.ts +++ b/packages/taler-wallet-core/src/withdraw.ts @@ -14,6 +14,11 @@ GNU Taler; see the file COPYING. If not, see */ +/** + * @fileoverview Implementation of Taler withdrawals, both + * bank-integrated and manual. + */ + /** * Imports. */ @@ -26,6 +31,7 @@ import { AmountLike, AmountString, Amounts, + AsyncFlag, BankWithdrawDetails, CancellationToken, CoinStatus, @@ -105,11 +111,14 @@ import { KycPendingInfo, PlanchetRecord, PlanchetStatus, + WalletDbReadOnlyTransaction, + WalletDbReadWriteTransaction, WalletStoresV1, WgInfo, WithdrawalGroupRecord, WithdrawalGroupStatus, WithdrawalRecordType, + timestampPreciseToDb, } from "./db.js"; import { ReadyExchangeSummary, @@ -119,12 +128,6 @@ import { listExchanges, markExchangeUsed, } from "./exchanges.js"; -import { - WalletDbReadOnlyTransaction, - WalletDbReadWriteTransaction, - isWithdrawableDenom, - timestampPreciseToDb, -} from "./index.js"; import { InternalWalletState } from "./internal-wallet-state.js"; import { DbAccess } from "./query.js"; import { @@ -137,6 +140,7 @@ import { selectForcedWithdrawalDenominations, selectWithdrawalDenominations, } from "./util/coinSelection.js"; +import { isWithdrawableDenom } from "./util/denominations.js"; import { checkDbInvariant, checkLogicInvariant } from "./util/invariants.js"; import { WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, @@ -628,7 +632,7 @@ export async function getCandidateWithdrawalDenomsTx( exchangeBaseUrl: string, currency: string, ): Promise { - // FIXME: Use denom groups instead of querying all denominations! + // FIXME(https://bugs.taler.net/n/8446): Use denom groups instead of querying all denominations! const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl.getAll(exchangeBaseUrl); return allDenoms @@ -730,10 +734,11 @@ interface WithdrawalBatchResult { batchResp: ExchangeWithdrawBatchResponse; } -enum AmlStatus { - normal = 0, - pending = 1, - fronzen = 2, +// FIXME: Move to exchange API types +enum ExchangeAmlStatus { + Normal = 0, + Pending = 1, + Frozen = 2, } /** @@ -818,7 +823,7 @@ async function handleKycRequired( method: "GET", }); let kycUrl: string; - let amlStatus: AmlStatus | undefined; + let amlStatus: ExchangeAmlStatus | undefined; if ( kycStatusRes.status === HttpStatusCode.Ok || // FIXME: NoContent is not expected https://docs.taler.net/core/api-exchange.html#post--purses-$PURSE_PUB-merge @@ -872,11 +877,11 @@ async function handleKycRequired( }; wg2.kycUrl = kycUrl; wg2.status = - amlStatus === AmlStatus.normal || amlStatus === undefined + amlStatus === ExchangeAmlStatus.Normal || amlStatus === undefined ? WithdrawalGroupStatus.PendingKyc - : amlStatus === AmlStatus.pending + : amlStatus === ExchangeAmlStatus.Pending ? WithdrawalGroupStatus.PendingAml - : amlStatus === AmlStatus.fronzen + : amlStatus === ExchangeAmlStatus.Frozen ? WithdrawalGroupStatus.SuspendedAml : assertUnreachable(amlStatus); @@ -1333,7 +1338,7 @@ async function queryReserve( * * Used to store some cached info during a withdrawal operation. */ -export interface WithdrawalGroupContext { +interface WithdrawalGroupContext { numPlanchets: number; planchetsFinished: Set; @@ -1659,19 +1664,11 @@ export async function processWithdrawalGroup( switch (withdrawalGroup.status) { case WithdrawalGroupStatus.PendingRegisteringBank: - await processReserveBankStatus(ws, withdrawalGroupId); - // FIXME: This will get called by the main task loop, why call it here?! - return await processWithdrawalGroup( - ws, - withdrawalGroupId, - cancellationToken, - ); - case WithdrawalGroupStatus.PendingQueryingStatus: { + return await processReserveBankStatus(ws, withdrawalGroupId); + case WithdrawalGroupStatus.PendingQueryingStatus: return queryReserve(ws, withdrawalGroupId, cancellationToken); - } - case WithdrawalGroupStatus.PendingWaitConfirmBank: { + case WithdrawalGroupStatus.PendingWaitConfirmBank: return await processReserveBankStatus(ws, withdrawalGroupId); - } case WithdrawalGroupStatus.PendingAml: // FIXME: Handle this case, withdrawal doesn't support AML yet. return TaskRunResult.backoff(); @@ -1768,29 +1765,34 @@ export async function getExchangeWithdrawalInfo( logger.trace("computing earliest deposit expiration"); let earliestDepositExpiration: TalerProtocolTimestamp | undefined; - for (let i = 0; i < selectedDenoms.selectedDenoms.length; i++) { - const ds = selectedDenoms.selectedDenoms[i]; - // FIXME: Do in one transaction! - const denom = await ws.db.runReadOnlyTx(["denominations"], async (tx) => { - return ws.getDenomInfo(ws, tx, exchangeBaseUrl, ds.denomPubHash); - }); - checkDbInvariant(!!denom); - hasDenomWithAgeRestriction = - hasDenomWithAgeRestriction || denom.denomPub.age_mask > 0; - const expireDeposit = denom.stampExpireDeposit; - if (!earliestDepositExpiration) { - earliestDepositExpiration = expireDeposit; - continue; - } - if ( - AbsoluteTime.cmp( - AbsoluteTime.fromProtocolTimestamp(expireDeposit), - AbsoluteTime.fromProtocolTimestamp(earliestDepositExpiration), - ) < 0 - ) { - earliestDepositExpiration = expireDeposit; + + await ws.db.runReadOnlyTx(["denominations"], async (tx) => { + for (let i = 0; i < selectedDenoms.selectedDenoms.length; i++) { + const ds = selectedDenoms.selectedDenoms[i]; + const denom = await ws.getDenomInfo( + ws, + tx, + exchangeBaseUrl, + ds.denomPubHash, + ); + checkDbInvariant(!!denom); + hasDenomWithAgeRestriction = + hasDenomWithAgeRestriction || denom.denomPub.age_mask > 0; + const expireDeposit = denom.stampExpireDeposit; + if (!earliestDepositExpiration) { + earliestDepositExpiration = expireDeposit; + continue; + } + if ( + AbsoluteTime.cmp( + AbsoluteTime.fromProtocolTimestamp(expireDeposit), + AbsoluteTime.fromProtocolTimestamp(earliestDepositExpiration), + ) < 0 + ) { + earliestDepositExpiration = expireDeposit; + } } - } + }); checkLogicInvariant(!!earliestDepositExpiration); @@ -2192,13 +2194,7 @@ async function processReserveBankStatus( // Bank still needs to know our reserve info if (!status.selection_done) { await registerReserveWithBank(ws, withdrawalGroupId); - return await processReserveBankStatus(ws, withdrawalGroupId); - } - - // FIXME: Why do we do this?! - if (withdrawalGroup.status === WithdrawalGroupStatus.PendingRegisteringBank) { - await registerReserveWithBank(ws, withdrawalGroupId); - return await processReserveBankStatus(ws, withdrawalGroupId); + return TaskRunResult.progress(); } const transitionInfo = await ws.db.runReadWriteTx( @@ -2479,6 +2475,14 @@ export async function internalCreateWithdrawalGroup( return res.withdrawalGroup; } +/** + * Accept a bank-integrated withdrawal. + * + * Before returning, the wallet tries to register the reserve with the bank. + * + * Thus after this call returns, the withdrawal operation can be confirmed + * with the bank. + */ export async function acceptWithdrawalFromUri( ws: InternalWalletState, req: { @@ -2560,11 +2564,10 @@ export async function acceptWithdrawalFromUri( const ctx = new WithdrawTransactionContext(ws, withdrawalGroupId); - const transactionId = ctx.transactionId; + // FIXME: Do we wait here until the reserve is registered with the bank? + + await waitWithdrawalRegistered(ws, ctx); - // We do this here, as the reserve should be registered before we return, - // so that we can redirect the user to the bank's status page. - await processReserveBankStatus(ws, withdrawalGroupId); const processedWithdrawalGroup = await getWithdrawalGroupRecordTx(ws.db, { withdrawalGroupId, }); @@ -2582,10 +2585,93 @@ export async function acceptWithdrawalFromUri( return { reservePub: withdrawalGroup.reservePub, confirmTransferUrl: withdrawInfo.confirmTransferUrl, - transactionId, + transactionId: ctx.transactionId, }; } +async function internalWaitWithdrawalRegistered( + ws: InternalWalletState, + ctx: WithdrawTransactionContext, + withdrawalNotifFlag: AsyncFlag, +): Promise { + while (true) { + const { withdrawalRec, retryRec } = await ws.db.runReadOnlyTx( + ["withdrawalGroups", "operationRetries"], + async (tx) => { + return { + withdrawalRec: await tx.withdrawalGroups.get(ctx.withdrawalGroupId), + retryRec: await tx.operationRetries.get(ctx.taskId), + }; + }, + ); + + if (!withdrawalRec) { + throw Error("withdrawal not found anymore"); + } + + switch (withdrawalRec.status) { + case WithdrawalGroupStatus.FailedBankAborted: + throw TalerError.fromDetail( + TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK, + {}, + ); + case WithdrawalGroupStatus.PendingKyc: + case WithdrawalGroupStatus.PendingAml: + case WithdrawalGroupStatus.PendingQueryingStatus: + case WithdrawalGroupStatus.PendingReady: + case WithdrawalGroupStatus.Done: + case WithdrawalGroupStatus.PendingWaitConfirmBank: + return; + case WithdrawalGroupStatus.PendingRegisteringBank: + break; + default: { + if (retryRec) { + if (retryRec.lastError) { + throw TalerError.fromUncheckedDetail(retryRec.lastError); + } else { + throw Error("withdrawal unexpectedly pending"); + } + } + } + } + + await withdrawalNotifFlag.wait(); + withdrawalNotifFlag.reset(); + } +} + +async function waitWithdrawalRegistered( + ws: InternalWalletState, + ctx: WithdrawTransactionContext, +): Promise { + // FIXME: We should use Symbol.dispose magic here for cleanup! + + const withdrawalNotifFlag = new AsyncFlag(); + // Raise exchangeNotifFlag whenever we get a notification + // about our exchange. + const cancelNotif = ws.addNotificationListener((notif) => { + if ( + notif.type === NotificationType.TransactionStateTransition && + notif.transactionId === ctx.transactionId + ) { + logger.info(`raising update notification: ${j2s(notif)}`); + withdrawalNotifFlag.raise(); + } + }); + + try { + const res = await internalWaitWithdrawalRegistered( + ws, + ctx, + withdrawalNotifFlag, + ); + logger.info("done waiting for ready exchange"); + return res; + } finally { + cancelNotif(); + } +} + async function fetchAccount( ws: InternalWalletState, instructedAmount: AmountJson, @@ -2669,7 +2755,7 @@ async function fetchWithdrawalAccountInfo( reservePub?: string; }, ): Promise { - const { exchange, instructedAmount } = req; + const { exchange } = req; const withdrawalAccounts: WithdrawalExchangeAccountDetails[] = []; for (let acct of exchange.wireInfo.accounts) { const acctInfo = await fetchAccount( @@ -2732,10 +2818,10 @@ export async function createManualWithdrawal( reserveKeyPair, }); - const withdrawalGroupId = withdrawalGroup.withdrawalGroupId; - const ctx = new WithdrawTransactionContext(ws, withdrawalGroupId); - - const transactionId = ctx.transactionId; + const ctx = new WithdrawTransactionContext( + ws, + withdrawalGroup.withdrawalGroupId, + ); const exchangePaytoUris = await ws.db.runReadOnlyTx( ["withdrawalGroups", "exchanges", "exchangeDetails"], @@ -2750,6 +2836,6 @@ export async function createManualWithdrawal( reservePub: withdrawalGroup.reservePub, exchangePaytoUris: exchangePaytoUris, withdrawalAccountsList: withdrawalAccountsList, - transactionId, + transactionId: ctx.transactionId, }; } -- cgit v1.2.3