diff options
author | Florian Dold <florian@dold.me> | 2021-06-17 15:49:05 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2021-06-17 15:49:05 +0200 |
commit | a70d37ef1675b53241f707c6730fab1537bd9d24 (patch) | |
tree | 5493e11884c9071efda9a8f4680e3787c6cdf0a5 /packages/taler-wallet-core/src/operations | |
parent | 453656b240c7e8771068ab877b6f5c9e3a26a4dc (diff) | |
download | wallet-core-a70d37ef1675b53241f707c6730fab1537bd9d24.tar.xz |
towards factoring out cyclic dependencies
Diffstat (limited to 'packages/taler-wallet-core/src/operations')
22 files changed, 173 insertions, 525 deletions
diff --git a/packages/taler-wallet-core/src/operations/README.md b/packages/taler-wallet-core/src/operations/README.md new file mode 100644 index 000000000..32e2fbfc8 --- /dev/null +++ b/packages/taler-wallet-core/src/operations/README.md @@ -0,0 +1,7 @@ +# Wallet Operations + +This folder contains the implementations for all wallet operations that operate on the wallet state. + +To avoid cyclic dependencies, these files must **not** reference each other. Instead, other operations should only be accessed via injected dependencies. + +Avoiding cyclic dependencies is important for module bundlers.
\ No newline at end of file diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts b/packages/taler-wallet-core/src/operations/backup/export.ts index 4eab9d5ee..42cc9b651 100644 --- a/packages/taler-wallet-core/src/operations/backup/export.ts +++ b/packages/taler-wallet-core/src/operations/backup/export.ts @@ -49,7 +49,7 @@ import { BackupRefreshSession, BackupExchangeDetails, } from "@gnu-taler/taler-util"; -import { InternalWalletState } from "../state.js"; +import { InternalWalletState } from "../../common.js"; import { provideBackupState, getWalletBackupState } from "./state"; import { Amounts, getTimestampNow } from "@gnu-taler/taler-util"; import { diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index 1caa508ff..ce4d14f1e 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -51,7 +51,7 @@ import { j2s } from "@gnu-taler/taler-util"; import { checkDbInvariant, checkLogicInvariant } from "../../util/invariants"; import { Logger } from "@gnu-taler/taler-util"; import { initRetryInfo } from "../../util/retries.js"; -import { InternalWalletState } from "../state.js"; +import { InternalWalletState } from "../../common.js"; import { provideBackupState } from "./state.js"; import { makeEventId, TombstoneTag } from "../transactions.js"; import { getExchangeDetails } from "../exchanges.js"; diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts b/packages/taler-wallet-core/src/operations/backup/index.ts index 2cc056721..8a0aad560 100644 --- a/packages/taler-wallet-core/src/operations/backup/index.ts +++ b/packages/taler-wallet-core/src/operations/backup/index.ts @@ -24,7 +24,7 @@ /** * Imports. */ -import { InternalWalletState } from "../state.js"; +import { InternalWalletState } from "../../common.js"; import { AmountString, BackupRecovery, diff --git a/packages/taler-wallet-core/src/operations/backup/state.ts b/packages/taler-wallet-core/src/operations/backup/state.ts index bb540d5af..3a7311d14 100644 --- a/packages/taler-wallet-core/src/operations/backup/state.ts +++ b/packages/taler-wallet-core/src/operations/backup/state.ts @@ -23,7 +23,7 @@ import { } from "../../db.js"; import { checkDbInvariant } from "../../util/invariants.js"; import { GetReadOnlyAccess } from "../../util/query.js"; -import { InternalWalletState } from "../state.js"; +import { InternalWalletState } from "../../common.js"; export async function provideBackupState( ws: InternalWalletState, diff --git a/packages/taler-wallet-core/src/operations/balance.ts b/packages/taler-wallet-core/src/operations/balance.ts index 4dba6beb7..298893920 100644 --- a/packages/taler-wallet-core/src/operations/balance.ts +++ b/packages/taler-wallet-core/src/operations/balance.ts @@ -23,12 +23,11 @@ import { Amounts, Logger, } from "@gnu-taler/taler-util"; - import { CoinStatus, WalletStoresV1 } from "../db.js"; import { GetReadOnlyAccess } from "../util/query.js"; -import { InternalWalletState } from "./state.js"; +import { InternalWalletState } from "../common.js"; -const logger = new Logger("withdraw.ts"); +const logger = new Logger("operations/balance.ts"); interface WalletBalance { available: AmountJson; diff --git a/packages/taler-wallet-core/src/operations/currencies.ts b/packages/taler-wallet-core/src/operations/currencies.ts deleted file mode 100644 index e591b50c0..000000000 --- a/packages/taler-wallet-core/src/operations/currencies.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2021 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { ExchangeRecord } from "../db.js"; -import { Logger } from "@gnu-taler/taler-util"; -import { getExchangeDetails } from "./exchanges.js"; -import { InternalWalletState } from "./state.js"; - -const logger = new Logger("currencies.ts"); - -export interface TrustInfo { - isTrusted: boolean; - isAudited: boolean; -} - -/** - * Check if and how an exchange is trusted and/or audited. - */ -export async function getExchangeTrust( - ws: InternalWalletState, - exchangeInfo: ExchangeRecord, -): Promise<TrustInfo> { - let isTrusted = false; - let isAudited = false; - - return await ws.db - .mktx((x) => ({ - exchanges: x.exchanges, - exchangeDetails: x.exchangeDetails, - exchangesTrustStore: x.exchangeTrust, - auditorTrust: x.auditorTrust, - })) - .runReadOnly(async (tx) => { - const exchangeDetails = await getExchangeDetails( - tx, - exchangeInfo.baseUrl, - ); - - if (!exchangeDetails) { - throw Error(`exchange ${exchangeInfo.baseUrl} details not available`); - } - const exchangeTrustRecord = await tx.exchangesTrustStore.indexes.byExchangeMasterPub.get( - exchangeDetails.masterPublicKey, - ); - if ( - exchangeTrustRecord && - exchangeTrustRecord.uids.length > 0 && - exchangeTrustRecord.currency === exchangeDetails.currency - ) { - isTrusted = true; - } - - for (const auditor of exchangeDetails.auditors) { - const auditorTrustRecord = await tx.auditorTrust.indexes.byAuditorPub.get( - auditor.auditor_pub, - ); - if (auditorTrustRecord && auditorTrustRecord.uids.length > 0) { - isAudited = true; - break; - } - } - - return { isTrusted, isAudited }; - }); -} diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts index c376ae8e0..5051fd32a 100644 --- a/packages/taler-wallet-core/src/operations/deposits.ts +++ b/packages/taler-wallet-core/src/operations/deposits.ts @@ -14,19 +14,10 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { kdf } from "../crypto/primitives/kdf.js"; -import { - encodeCrock, - getRandomBytes, - stringToBytes, -} from "../crypto/talerCrypto.js"; -import { selectPayCoins } from "../util/coinSelection.js"; -import { canonicalJson } from "@gnu-taler/taler-util"; -import { readSuccessResponseJsonOrThrow } from "../util/http.js"; -import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries.js"; import { Amounts, buildCodecForObject, + canonicalJson, Codec, codecForString, codecForTimestamp, @@ -36,6 +27,7 @@ import { CreateDepositGroupResponse, durationFromSpec, getTimestampNow, + Logger, NotificationType, parsePaytoUri, TalerErrorDetails, @@ -45,7 +37,20 @@ import { TrackDepositGroupRequest, TrackDepositGroupResponse, } from "@gnu-taler/taler-util"; -import { URL } from "../util/url"; +import { InternalWalletState } from "../common.js"; +import { kdf } from "../crypto/primitives/kdf.js"; +import { + encodeCrock, + getRandomBytes, + stringToBytes, +} from "../crypto/talerCrypto.js"; +import { DepositGroupRecord } from "../db.js"; +import { guardOperationException } from "../errors.js"; +import { selectPayCoins } from "../util/coinSelection.js"; +import { readSuccessResponseJsonOrThrow } from "../util/http.js"; +import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries.js"; +import { URL } from "../util/url.js"; +import { getExchangeDetails } from "./exchanges.js"; import { applyCoinSpend, extractContractData, @@ -54,12 +59,6 @@ import { getEffectiveDepositAmount, getTotalPaymentCost, } from "./pay.js"; -import { InternalWalletState } from "./state.js"; -import { Logger } from "@gnu-taler/taler-util"; -import { DepositGroupRecord } from "../db.js"; - -import { guardOperationException } from "./errors.js"; -import { getExchangeDetails } from "./exchanges.js"; /** * Logger. diff --git a/packages/taler-wallet-core/src/operations/errors.ts b/packages/taler-wallet-core/src/operations/errors.ts deleted file mode 100644 index d788405ff..000000000 --- a/packages/taler-wallet-core/src/operations/errors.ts +++ /dev/null @@ -1,132 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2019-2020 Taler Systems SA - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Classes and helpers for error handling specific to wallet operations. - * - * @author Florian Dold <dold@taler.net> - */ - -/** - * Imports. - */ -import { TalerErrorCode, TalerErrorDetails } from "@gnu-taler/taler-util"; - -/** - * This exception is there to let the caller know that an error happened, - * but the error has already been reported by writing it to the database. - */ -export class OperationFailedAndReportedError extends Error { - static fromCode( - ec: TalerErrorCode, - message: string, - details: Record<string, unknown>, - ): OperationFailedAndReportedError { - return new OperationFailedAndReportedError( - makeErrorDetails(ec, message, details), - ); - } - - constructor(public operationError: TalerErrorDetails) { - super(operationError.message); - - // Set the prototype explicitly. - Object.setPrototypeOf(this, OperationFailedAndReportedError.prototype); - } -} - -/** - * This exception is thrown when an error occurred and the caller is - * responsible for recording the failure in the database. - */ -export class OperationFailedError extends Error { - static fromCode( - ec: TalerErrorCode, - message: string, - details: Record<string, unknown>, - ): OperationFailedError { - return new OperationFailedError(makeErrorDetails(ec, message, details)); - } - - constructor(public operationError: TalerErrorDetails) { - super(operationError.message); - - // Set the prototype explicitly. - Object.setPrototypeOf(this, OperationFailedError.prototype); - } -} - -export function makeErrorDetails( - ec: TalerErrorCode, - message: string, - details: Record<string, unknown>, -): TalerErrorDetails { - return { - code: ec, - hint: `Error: ${TalerErrorCode[ec]}`, - details: details, - message, - }; -} - -/** - * Run an operation and call the onOpError callback - * when there was an exception or operation error that must be reported. - * The cause will be re-thrown to the caller. - */ -export async function guardOperationException<T>( - op: () => Promise<T>, - onOpError: (e: TalerErrorDetails) => Promise<void>, -): Promise<T> { - try { - return await op(); - } catch (e) { - if (e instanceof OperationFailedAndReportedError) { - throw e; - } - if (e instanceof OperationFailedError) { - await onOpError(e.operationError); - throw new OperationFailedAndReportedError(e.operationError); - } - if (e instanceof Error) { - const opErr = makeErrorDetails( - TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION, - `unexpected exception (message: ${e.message})`, - { - stack: e.stack, - }, - ); - await onOpError(opErr); - throw new OperationFailedAndReportedError(opErr); - } - // Something was thrown that is not even an exception! - // Try to stringify it. - let excString: string; - try { - excString = e.toString(); - } catch (e) { - // Something went horribly wrong. - excString = "can't stringify exception"; - } - const opErr = makeErrorDetails( - TalerErrorCode.WALLET_UNEXPECTED_EXCEPTION, - `unexpected exception (not an exception, ${excString})`, - {}, - ); - await onOpError(opErr); - throw new OperationFailedAndReportedError(opErr); - } -} diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index c8dfcbc17..65cc8f5e9 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -20,6 +20,7 @@ import { Amounts, Auditor, + canonicalizeBaseUrl, codecForExchangeKeysJson, codecForExchangeWireJson, compare, @@ -30,6 +31,7 @@ import { ExchangeWireJson, getTimestampNow, isTimestampExpired, + j2s, Logger, NotificationType, parsePaytoUri, @@ -38,38 +40,37 @@ import { TalerErrorDetails, Timestamp, } from "@gnu-taler/taler-util"; +import { decodeCrock, encodeCrock, hash } from "../crypto/talerCrypto.js"; +import { CryptoApi } from "../crypto/workers/cryptoApi.js"; import { DenominationRecord, DenominationStatus, + ExchangeDetailsRecord, ExchangeRecord, + WalletStoresV1, WireFee, - ExchangeDetailsRecord, WireInfo, - WalletStoresV1, } from "../db.js"; -import { j2s, canonicalizeBaseUrl } from "@gnu-taler/taler-util"; -import { updateRetryInfoTimeout, initRetryInfo } from "../util/retries.js"; -import { - makeErrorDetails, - guardOperationException, - OperationFailedError, -} from "./errors.js"; -import { createRecoupGroup, processRecoupGroup } from "./recoup.js"; -import { InternalWalletState } from "./state.js"; -import { - WALLET_CACHE_BREAKER_CLIENT_VERSION, - WALLET_EXCHANGE_PROTOCOL_VERSION, -} from "./versions.js"; import { getExpiryTimestamp, HttpRequestLibrary, readSuccessResponseJsonOrThrow, readSuccessResponseTextOrThrow, } from "../util/http.js"; -import { CryptoApi } from "../crypto/workers/cryptoApi.js"; import { DbAccess, GetReadOnlyAccess } from "../util/query.js"; -import { decodeCrock, encodeCrock, hash } from "../crypto/talerCrypto.js"; +import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries.js"; import { URL } from "../util/url.js"; +import { + guardOperationException, + makeErrorDetails, + OperationFailedError, +} from "../errors.js"; +import { createRecoupGroup, processRecoupGroup } from "./recoup.js"; +import { InternalWalletState, TrustInfo } from "../common.js"; +import { + WALLET_CACHE_BREAKER_CLIENT_VERSION, + WALLET_EXCHANGE_PROTOCOL_VERSION, +} from "../versions.js"; const logger = new Logger("exchanges.ts"); @@ -605,3 +606,54 @@ export async function getExchangePaytoUri( } throw Error("no matching exchange account found"); } + +/** + * Check if and how an exchange is trusted and/or audited. + */ +export async function getExchangeTrust( + ws: InternalWalletState, + exchangeInfo: ExchangeRecord, +): Promise<TrustInfo> { + let isTrusted = false; + let isAudited = false; + + return await ws.db + .mktx((x) => ({ + exchanges: x.exchanges, + exchangeDetails: x.exchangeDetails, + exchangesTrustStore: x.exchangeTrust, + auditorTrust: x.auditorTrust, + })) + .runReadOnly(async (tx) => { + const exchangeDetails = await getExchangeDetails( + tx, + exchangeInfo.baseUrl, + ); + + if (!exchangeDetails) { + throw Error(`exchange ${exchangeInfo.baseUrl} details not available`); + } + const exchangeTrustRecord = await tx.exchangesTrustStore.indexes.byExchangeMasterPub.get( + exchangeDetails.masterPublicKey, + ); + if ( + exchangeTrustRecord && + exchangeTrustRecord.uids.length > 0 && + exchangeTrustRecord.currency === exchangeDetails.currency + ) { + isTrusted = true; + } + + for (const auditor of exchangeDetails.auditors) { + const auditorTrustRecord = await tx.auditorTrust.indexes.byAuditorPub.get( + auditor.auditor_pub, + ); + if (auditorTrustRecord && auditorTrustRecord.uids.length > 0) { + isAudited = true; + break; + } + } + + return { isTrusted, isAudited }; + }); +} diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts index c62ad0f25..6d185cae8 100644 --- a/packages/taler-wallet-core/src/operations/pay.ts +++ b/packages/taler-wallet-core/src/operations/pay.ts @@ -68,7 +68,7 @@ import { getRetryDuration, } from "../util/retries.js"; import { getTotalRefreshCost, createRefreshGroup } from "./refresh.js"; -import { InternalWalletState, EXCHANGE_COINS_LOCK } from "./state.js"; +import { InternalWalletState, EXCHANGE_COINS_LOCK } from "../common.js"; import { ContractTermsUtil } from "../util/contractTerms.js"; import { getExchangeDetails } from "./exchanges.js"; import { GetReadWriteAccess } from "../util/query.js"; @@ -98,7 +98,7 @@ import { makeErrorDetails, OperationFailedAndReportedError, OperationFailedError, -} from "./errors.js"; +} from "../errors.js"; import { URL } from "../util/url.js"; /** diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts index 8201e8cb9..fff64739c 100644 --- a/packages/taler-wallet-core/src/operations/pending.ts +++ b/packages/taler-wallet-core/src/operations/pending.ts @@ -29,7 +29,7 @@ import { ReserveType, } from "../pending-types.js"; import { getTimestampNow, Timestamp } from "@gnu-taler/taler-util"; -import { InternalWalletState } from "./state.js"; +import { InternalWalletState } from "../common.js"; import { getBalancesInsideTransaction } from "./balance.js"; import { GetReadOnlyAccess } from "../util/query.js"; diff --git a/packages/taler-wallet-core/src/operations/recoup.ts b/packages/taler-wallet-core/src/operations/recoup.ts index 938422d3b..402111129 100644 --- a/packages/taler-wallet-core/src/operations/recoup.ts +++ b/packages/taler-wallet-core/src/operations/recoup.ts @@ -48,10 +48,10 @@ import { readSuccessResponseJsonOrThrow } from "../util/http.js"; import { Logger } from "@gnu-taler/taler-util"; import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries.js"; import { URL } from "../util/url.js"; -import { guardOperationException } from "./errors.js"; +import { guardOperationException } from "../errors.js"; import { createRefreshGroup, processRefreshGroup } from "./refresh.js"; import { getReserveRequestTimeout, processReserve } from "./reserves.js"; -import { InternalWalletState } from "./state.js"; +import { InternalWalletState } from "../common.js"; import { GetReadWriteAccess } from "../util/query.js"; const logger = new Logger("operations/recoup.ts"); diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index 3c81362ce..06d735645 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -52,9 +52,9 @@ import { timestampMin, } from "@gnu-taler/taler-util"; import { URL } from "../util/url.js"; -import { guardOperationException } from "./errors.js"; +import { guardOperationException } from "../errors.js"; import { updateExchangeFromUrl } from "./exchanges.js"; -import { EXCHANGE_COINS_LOCK, InternalWalletState } from "./state.js"; +import { EXCHANGE_COINS_LOCK, InternalWalletState } from "../common.js"; import { isWithdrawableDenom, selectWithdrawalDenominations, diff --git a/packages/taler-wallet-core/src/operations/refund.ts b/packages/taler-wallet-core/src/operations/refund.ts index 6ef55e535..09006b811 100644 --- a/packages/taler-wallet-core/src/operations/refund.ts +++ b/packages/taler-wallet-core/src/operations/refund.ts @@ -23,12 +23,7 @@ /** * Imports. */ -import { InternalWalletState } from "./state.js"; -import { guardOperationException } from "./errors.js"; import { - getTimestampNow, - timestampAddDuration, - TalerErrorDetails, AbortingCoin, AbortRequest, AmountJson, @@ -37,29 +32,34 @@ import { codecForAbortResponse, codecForMerchantOrderRefundPickupResponse, CoinPublicKey, + getTimestampNow, + Logger, MerchantCoinRefundFailureStatus, MerchantCoinRefundStatus, MerchantCoinRefundSuccessStatus, NotificationType, parseRefundUri, RefreshReason, + TalerErrorCode, + TalerErrorDetails, + timestampAddDuration, } from "@gnu-taler/taler-util"; -import { Logger } from "@gnu-taler/taler-util"; -import { readSuccessResponseJsonOrThrow } from "../util/http.js"; -import { URL } from "../util/url.js"; -import { updateRetryInfoTimeout, initRetryInfo } from "../util/retries.js"; -import { checkDbInvariant } from "../util/invariants.js"; -import { TalerErrorCode } from "@gnu-taler/taler-util"; import { - PurchaseRecord, - CoinStatus, - RefundState, AbortStatus, + CoinStatus, + PurchaseRecord, RefundReason, + RefundState, WalletStoresV1, } from "../db.js"; -import { getTotalRefreshCost, createRefreshGroup } from "./refresh.js"; +import { readSuccessResponseJsonOrThrow } from "../util/http.js"; +import { checkDbInvariant } from "../util/invariants.js"; import { GetReadWriteAccess } from "../util/query.js"; +import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries.js"; +import { URL } from "../util/url.js"; +import { guardOperationException } from "../errors.js"; +import { createRefreshGroup, getTotalRefreshCost } from "./refresh.js"; +import { InternalWalletState } from "../common.js"; const logger = new Logger("refund.ts"); diff --git a/packages/taler-wallet-core/src/operations/reserves.ts b/packages/taler-wallet-core/src/operations/reserves.ts index 998fbf3e0..dde71f040 100644 --- a/packages/taler-wallet-core/src/operations/reserves.ts +++ b/packages/taler-wallet-core/src/operations/reserves.ts @@ -47,13 +47,14 @@ import { getRetryDuration, updateRetryInfoTimeout, } from "../util/retries.js"; -import { guardOperationException, OperationFailedError } from "./errors.js"; +import { guardOperationException, OperationFailedError } from "../errors.js"; import { updateExchangeFromUrl, getExchangePaytoUri, getExchangeDetails, + getExchangeTrust, } from "./exchanges.js"; -import { InternalWalletState } from "./state.js"; +import { InternalWalletState } from "../common.js"; import { updateWithdrawalDenoms, getCandidateWithdrawalDenoms, @@ -62,7 +63,6 @@ import { processWithdrawGroup, getBankWithdrawalInfo, } from "./withdraw.js"; -import { getExchangeTrust } from "./currencies.js"; import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto.js"; import { Logger } from "@gnu-taler/taler-util"; import { diff --git a/packages/taler-wallet-core/src/operations/state.ts b/packages/taler-wallet-core/src/operations/state.ts deleted file mode 100644 index ee7ceb8af..000000000 --- a/packages/taler-wallet-core/src/operations/state.ts +++ /dev/null @@ -1,148 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2019 GNUnet e.V. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Imports. - */ -import { - WalletNotification, - BalancesResponse, - Logger, -} from "@gnu-taler/taler-util"; -import { CryptoApi, CryptoWorkerFactory } from "../crypto/workers/cryptoApi.js"; -import { WalletStoresV1 } from "../db.js"; -import { PendingOperationsResponse } from "../pending-types.js"; -import { AsyncOpMemoMap, AsyncOpMemoSingle } from "../util/asyncMemo.js"; -import { HttpRequestLibrary } from "../util/http"; -import { - AsyncCondition, - OpenedPromise, - openPromise, -} from "../util/promiseUtils.js"; -import { DbAccess } from "../util/query.js"; -import { TimerGroup } from "../util/timer.js"; - -type NotificationListener = (n: WalletNotification) => void; - -const logger = new Logger("state.ts"); - -export const EXCHANGE_COINS_LOCK = "exchange-coins-lock"; -export const EXCHANGE_RESERVES_LOCK = "exchange-reserves-lock"; - -/** - * Internal state of the wallet. - */ -export class InternalWalletState { - memoProcessReserve: AsyncOpMemoMap<void> = new AsyncOpMemoMap(); - memoMakePlanchet: AsyncOpMemoMap<void> = new AsyncOpMemoMap(); - memoGetPending: AsyncOpMemoSingle<PendingOperationsResponse> = new AsyncOpMemoSingle(); - memoGetBalance: AsyncOpMemoSingle<BalancesResponse> = new AsyncOpMemoSingle(); - memoProcessRefresh: AsyncOpMemoMap<void> = new AsyncOpMemoMap(); - memoProcessRecoup: AsyncOpMemoMap<void> = new AsyncOpMemoMap(); - memoProcessDeposit: AsyncOpMemoMap<void> = new AsyncOpMemoMap(); - cryptoApi: CryptoApi; - - timerGroup: TimerGroup = new TimerGroup(); - latch = new AsyncCondition(); - stopped = false; - memoRunRetryLoop = new AsyncOpMemoSingle<void>(); - - listeners: NotificationListener[] = []; - - initCalled: boolean = false; - - /** - * Promises that are waiting for a particular resource. - */ - private resourceWaiters: Record<string, OpenedPromise<void>[]> = {}; - - /** - * Resources that are currently locked. - */ - private resourceLocks: Set<string> = new Set(); - - constructor( - // FIXME: Make this a getter and make - // the actual value nullable. - // Check if we are in a DB migration / garbage collection - // and throw an error in that case. - public db: DbAccess<typeof WalletStoresV1>, - public http: HttpRequestLibrary, - cryptoWorkerFactory: CryptoWorkerFactory, - ) { - this.cryptoApi = new CryptoApi(cryptoWorkerFactory); - } - - notify(n: WalletNotification): void { - logger.trace("Notification", n); - for (const l of this.listeners) { - const nc = JSON.parse(JSON.stringify(n)); - setTimeout(() => { - l(nc); - }, 0); - } - } - - addNotificationListener(f: (n: WalletNotification) => void): void { - this.listeners.push(f); - } - - /** - * Stop ongoing processing. - */ - stop(): void { - this.stopped = true; - this.timerGroup.stopCurrentAndFutureTimers(); - this.cryptoApi.stop(); - } - - /** - * Run an async function after acquiring a list of locks, identified - * by string tokens. - */ - async runSequentialized<T>(tokens: string[], f: () => Promise<T>) { - // Make sure locks are always acquired in the same order - tokens = [...tokens].sort(); - - for (const token of tokens) { - if (this.resourceLocks.has(token)) { - const p = openPromise<void>(); - let waitList = this.resourceWaiters[token]; - if (!waitList) { - waitList = this.resourceWaiters[token] = []; - } - waitList.push(p); - await p.promise; - } - this.resourceLocks.add(token); - } - - try { - logger.trace(`begin exclusive execution on ${JSON.stringify(tokens)}`); - const result = await f(); - logger.trace(`end exclusive execution on ${JSON.stringify(tokens)}`); - return result; - } finally { - for (const token of tokens) { - this.resourceLocks.delete(token); - let waiter = (this.resourceWaiters[token] ?? []).shift(); - if (waiter) { - waiter.resolve(); - } - } - } - } -} diff --git a/packages/taler-wallet-core/src/operations/testing.ts b/packages/taler-wallet-core/src/operations/testing.ts index ce3a47f36..8a0c20200 100644 --- a/packages/taler-wallet-core/src/operations/testing.ts +++ b/packages/taler-wallet-core/src/operations/testing.ts @@ -34,7 +34,7 @@ import { PreparePayResultType, } from "@gnu-taler/taler-util"; import { createTalerWithdrawReserve } from "./reserves.js"; -import { InternalWalletState } from "./state.js"; +import { InternalWalletState } from "../common.js"; import { URL } from "../util/url.js"; import { confirmPay, preparePayForUri } from "./pay.js"; import { getBalances } from "./balance.js"; diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts index d82331632..1da2c887a 100644 --- a/packages/taler-wallet-core/src/operations/tip.ts +++ b/packages/taler-wallet-core/src/operations/tip.ts @@ -40,9 +40,9 @@ import { import { j2s } from "@gnu-taler/taler-util"; import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js"; import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries.js"; -import { guardOperationException, makeErrorDetails } from "./errors.js"; +import { guardOperationException, makeErrorDetails } from "../errors.js"; import { updateExchangeFromUrl } from "./exchanges.js"; -import { InternalWalletState } from "./state.js"; +import { InternalWalletState } from "../common.js"; import { getExchangeWithdrawalInfo, updateWithdrawalDenoms, diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 5836a6ee3..82cfec551 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -17,7 +17,7 @@ /** * Imports. */ -import { InternalWalletState } from "./state.js"; +import { InternalWalletState } from "../common.js"; import { WalletRefundItem, RefundState, diff --git a/packages/taler-wallet-core/src/operations/versions.ts b/packages/taler-wallet-core/src/operations/versions.ts deleted file mode 100644 index b798871c2..000000000 --- a/packages/taler-wallet-core/src/operations/versions.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2019 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Protocol version spoken with the exchange. - * - * Uses libtool's current:revision:age versioning. - */ -export const WALLET_EXCHANGE_PROTOCOL_VERSION = "9:0:0"; - -/** - * Protocol version spoken with the merchant. - * - * Uses libtool's current:revision:age versioning. - */ -export const WALLET_MERCHANT_PROTOCOL_VERSION = "1:0:0"; - -/** - * Protocol version spoken with the merchant. - * - * Uses libtool's current:revision:age versioning. - */ -export const WALLET_BANK_INTEGRATION_PROTOCOL_VERSION = "0:0:0"; - -/** - * Cache breaker that is appended to queries such as /keys and /wire - * to break through caching, if it has been accidentally/badly configured - * by the exchange. - * - * This is only a temporary measure. - */ -export const WALLET_CACHE_BREAKER_CLIENT_VERSION = "3"; diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 3400238ed..6cf20259c 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -17,65 +17,56 @@ /** * Imports. */ +import * as LibtoolVersion from "@gnu-taler/taler-util"; import { AmountJson, Amounts, + BankWithdrawDetails, + codecForTalerConfigResponse, + codecForWithdrawOperationStatusResponse, + codecForWithdrawResponse, + compare, durationFromSpec, + ExchangeListItem, + getDurationRemaining, + getTimestampNow, + Logger, + NotificationType, parseWithdrawUri, + TalerErrorCode, + TalerErrorDetails, Timestamp, + timestampCmp, + timestampSubtractDuraction, + WithdrawResponse, + WithdrawUriInfoResponse, } from "@gnu-taler/taler-util"; import { - DenominationRecord, - DenominationStatus, - CoinStatus, CoinRecord, CoinSourceType, + CoinStatus, + DenominationRecord, DenominationSelectionInfo, - PlanchetRecord, + DenominationStatus, DenomSelectionState, - ExchangeRecord, ExchangeDetailsRecord, + ExchangeRecord, + PlanchetRecord, } from "../db.js"; -import { - BankWithdrawDetails, - TalerErrorDetails, - ExchangeListItem, - WithdrawUriInfoResponse, -} from "@gnu-taler/taler-util"; -import { - codecForWithdrawOperationStatusResponse, - codecForWithdrawResponse, - WithdrawResponse, - codecForTalerConfigResponse, -} from "@gnu-taler/taler-util"; -import { InternalWalletState } from "./state.js"; -import { Logger } from "@gnu-taler/taler-util"; -import { getExchangeDetails, updateExchangeFromUrl } from "./exchanges.js"; -import { - WALLET_EXCHANGE_PROTOCOL_VERSION, - WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, -} from "./versions.js"; - -import * as LibtoolVersion from "@gnu-taler/taler-util"; +import { walletCoreDebugFlags } from "../util/debugFlags.js"; +import { readSuccessResponseJsonOrThrow } from "../util/http.js"; +import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries.js"; +import { URL } from "../util/url.js"; import { guardOperationException, makeErrorDetails, OperationFailedError, -} from "./errors.js"; -import { NotificationType } from "@gnu-taler/taler-util"; +} from "../errors.js"; +import { InternalWalletState } from "../common.js"; import { - getTimestampNow, - getDurationRemaining, - timestampCmp, - timestampSubtractDuraction, -} from "@gnu-taler/taler-util"; -import { readSuccessResponseJsonOrThrow } from "../util/http.js"; -import { URL } from "../util/url.js"; -import { TalerErrorCode } from "@gnu-taler/taler-util"; -import { updateRetryInfoTimeout, initRetryInfo } from "../util/retries.js"; -import { compare } from "@gnu-taler/taler-util"; -import { walletCoreDebugFlags } from "../util/debugFlags.js"; -import { getExchangeTrust } from "./currencies.js"; + WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, + WALLET_EXCHANGE_PROTOCOL_VERSION, +} from "../versions.js"; /** * Logger for this file. @@ -690,7 +681,7 @@ export async function updateWithdrawalDenoms( exchangeDetails: x.exchangeDetails, })) .runReadOnly(async (tx) => { - return getExchangeDetails(tx, exchangeBaseUrl); + return ws.exchangeOps.getExchangeDetails(tx, exchangeBaseUrl); }); if (!exchangeDetails) { logger.error("exchange details not available"); @@ -816,7 +807,10 @@ async function processWithdrawGroupImpl( return; } - await updateExchangeFromUrl(ws, withdrawalGroup.exchangeBaseUrl); + await ws.exchangeOps.updateExchangeFromUrl( + ws, + withdrawalGroup.exchangeBaseUrl, + ); const numTotalCoins = withdrawalGroup.denomsSel.selectedDenoms .map((x) => x.count) @@ -910,10 +904,10 @@ export async function getExchangeWithdrawalInfo( baseUrl: string, amount: AmountJson, ): Promise<ExchangeWithdrawDetails> { - const { exchange, exchangeDetails } = await updateExchangeFromUrl( - ws, - baseUrl, - ); + const { + exchange, + exchangeDetails, + } = await ws.exchangeOps.updateExchangeFromUrl(ws, baseUrl); await updateWithdrawalDenoms(ws, baseUrl); const denoms = await getCandidateWithdrawalDenoms(ws, baseUrl); const selectedDenoms = selectWithdrawalDenominations(amount, denoms); @@ -922,7 +916,10 @@ export async function getExchangeWithdrawalInfo( exchangeWireAccounts.push(account.payto_uri); } - const { isTrusted, isAudited } = await getExchangeTrust(ws, exchange); + const { isTrusted, isAudited } = await ws.exchangeOps.getExchangeTrust( + ws, + exchange, + ); let earliestDepositExpiration = selectedDenoms.selectedDenoms[0].denom.stampExpireDeposit; @@ -1009,7 +1006,7 @@ export async function getWithdrawalDetailsForUri( // FIXME: right now the exchange gets permanently added, // we might want to only temporarily add it. try { - await updateExchangeFromUrl(ws, info.suggestedExchange); + await ws.exchangeOps.updateExchangeFromUrl(ws, info.suggestedExchange); } catch (e) { // We still continued if it failed, as other exchanges might be available. // We don't want to fail if the bank-suggested exchange is broken/offline. @@ -1029,7 +1026,7 @@ export async function getWithdrawalDetailsForUri( .runReadOnly(async (tx) => { const exchangeRecords = await tx.exchanges.iter().toArray(); for (const r of exchangeRecords) { - const details = await getExchangeDetails(tx, r.baseUrl); + const details = await ws.exchangeOps.getExchangeDetails(tx, r.baseUrl); if (details) { exchanges.push({ exchangeBaseUrl: details.exchangeBaseUrl, |