summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2024-02-22 11:33:32 +0100
committerFlorian Dold <florian@dold.me>2024-02-27 14:58:36 +0100
commit2ab4ad686380b3cfe5c5245312e42af8a69c01f6 (patch)
treee82af5ea7defbc4bdd7b468f714212041914dc59
parent41f34031ae934e8c802cdbb73fffb5a39c9c6a68 (diff)
downloadwallet-core-dev/dold/observability.tar.gz
wallet-core-dev/dold/observability.tar.bz2
wallet-core-dev/dold/observability.zip
move operation out of ws
-rw-r--r--packages/taler-harness/src/integrationtests/test-withdrawal-notify-before-tx.ts80
-rw-r--r--packages/taler-util/src/http-common.ts3
-rw-r--r--packages/taler-util/src/notifications.ts73
-rw-r--r--packages/taler-util/src/observability.ts25
-rw-r--r--packages/taler-util/src/taler-types.ts46
-rw-r--r--packages/taler-wallet-core/src/coinSelection.ts4
-rw-r--r--packages/taler-wallet-core/src/common.ts4
-rw-r--r--packages/taler-wallet-core/src/deposits.ts6
-rw-r--r--packages/taler-wallet-core/src/pay-merchant.ts8
-rw-r--r--packages/taler-wallet-core/src/pay-peer-common.ts6
-rw-r--r--packages/taler-wallet-core/src/recoup.ts10
-rw-r--r--packages/taler-wallet-core/src/refresh.ts22
-rw-r--r--packages/taler-wallet-core/src/wallet.ts61
-rw-r--r--packages/taler-wallet-core/src/withdraw.ts27
14 files changed, 176 insertions, 199 deletions
diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-notify-before-tx.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-notify-before-tx.ts
deleted file mode 100644
index fc36b8fc3..000000000
--- a/packages/taler-harness/src/integrationtests/test-withdrawal-notify-before-tx.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2020 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 { NotificationType, TalerCorebankApiClient } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runWithdrawalNotifyBeforeTxTest(t: GlobalTestState) {
- // Set up test environment
-
- const { walletClient, bank, exchange } =
- await createSimpleTestkudosEnvironmentV2(t);
-
- // Create a withdrawal operation
-
- const bankAccessApiClient = new TalerCorebankApiClient(
- bank.corebankApiBaseUrl,
- );
- const user = await bankAccessApiClient.createRandomBankUser();
- bankAccessApiClient.setAuth(user);
- const wop = await bankAccessApiClient.createWithdrawalOperation(
- user.username,
- "TESTKUDOS:10",
- );
-
- // Hand it to the wallet
- const r1 = await walletClient.call(WalletApiOperation.GetWithdrawalDetailsForUri, {
- talerWithdrawUri: wop.taler_withdraw_uri,
- notifyChangeFromPendingTimeoutMs: 10000
- });
-
- await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
-
- // Withdraw
-
- // Abort it
- // const api = new TalerCoreBankHttpClient(bank.corebankApiBaseUrl);
- // const token = await api.getAuthenticationAPI(user.username).createAccessToken(user.password, {
- // scope: "readwrite",
- // })
- // t.assertTrue(token.type !== "fail")
-
- // const confirm = await api.confirmWithdrawalById({
- // username: user.username,
- // token: token.body.access_token,
- // }, wop.withdrawal_id)
- // t.assertTrue(confirm.type !== "fail")
-
- await walletClient.waitForNotificationCond((x) => {
- return (
- x.type === NotificationType.WithdrawalOperationTransition &&
- x.operationId === r1.operationId &&
- x.state === "confirmed"
- );
- });
-
- await t.shutdown();
-}
-
-runWithdrawalNotifyBeforeTxTest.suites = ["wallet"];
diff --git a/packages/taler-util/src/http-common.ts b/packages/taler-util/src/http-common.ts
index 3973e66fb..885bd32e6 100644
--- a/packages/taler-util/src/http-common.ts
+++ b/packages/taler-util/src/http-common.ts
@@ -26,6 +26,7 @@ import {
stringToBytes,
} from "./index.js";
import { Logger } from "./logging.js";
+import { ObservabilityContext } from "./observability.js";
import { TalerErrorCode } from "./taler-error-codes.js";
import { AbsoluteTime, Duration } from "./time.js";
import { TalerErrorDetail } from "./wallet-types.js";
@@ -71,6 +72,8 @@ export interface HttpRequestOptions {
* Same semantics as WHATWG fetch.
*/
redirect?: "follow" | "error" | "manual";
+
+ //observabilityContext?: ObservabilityContext;
}
/**
diff --git a/packages/taler-util/src/notifications.ts b/packages/taler-util/src/notifications.ts
index d84d3706d..f4dbef8b6 100644
--- a/packages/taler-util/src/notifications.ts
+++ b/packages/taler-util/src/notifications.ts
@@ -15,16 +15,15 @@
*/
/**
- * Type and schema definitions for notifications from the wallet to clients
- * of the wallet.
+ * @fileoverview Type and schema definitions for notifications from the
+ * wallet to clients of the wallet.
*/
/**
* Imports.
*/
-import { WithdrawalOperationStatus } from "./index.node.js";
import { TransactionState } from "./transactions-types.js";
-import { ExchangeEntryState, TalerErrorDetail } from "./wallet-types.js";
+import { ExchangeEntryState } from "./wallet-types.js";
export enum NotificationType {
BalanceChange = "balance-change",
@@ -33,6 +32,10 @@ export enum NotificationType {
TransactionStateTransition = "transaction-state-transition",
ExchangeStateTransition = "exchange-state-transition",
WithdrawalOperationTransition = "withdrawal-state-transition",
+ TaskProgress = "task-progress",
+ RequestProgress = "request-progress",
+ RequestOnTaskDependency = "request-on-task-dependency",
+ TaskOnTaskDependency = "task-on-task-dependency",
}
export interface ErrorInfoSummary {
@@ -97,26 +100,58 @@ export interface BalanceChangeNotification {
hintTransactionId: string;
}
-export interface BackupOperationErrorNotification {
- type: NotificationType.BackupOperationError;
- error: TalerErrorDetail;
+export interface TaskProgressNotification {
+ type: NotificationType.TaskProgress;
+ taskId: string;
}
-export interface PendingOperationProcessedNotification {
- type: NotificationType.PendingOperationProcessed;
- id: string;
- taskResultType: string;
+export interface RequestProgressNotification {
+ type: NotificationType.RequestProgress;
+ requestId: string;
}
-export interface WithdrawalOperationTransitionNotification {
- type: NotificationType.WithdrawalOperationTransition;
- operationId: string;
- state: WithdrawalOperationStatus;
+
+export interface RequestOnTaskDependencyNotification {
+ type: NotificationType.RequestOnTaskDependency;
+ parentTaskId: string;
+ childTaskId: string;
+}
+
+export interface TaskOnTaskDependencyNotification {
+ type: NotificationType.TaskOnTaskDependency;
+ parentRequestId: string;
+ childTaskId: string;
}
+export type ObservabilityEvent =
+ | {
+ type: "start-http-fetch";
+ url: string;
+ }
+ | {
+ type: "finish-http-fetch";
+ url: string;
+ status: number;
+ }
+ | {
+ type: "start-db-query";
+ name: string;
+ location: string;
+ }
+ | {
+ type: "finish-db-query";
+ name: string;
+ location: string;
+ }
+ | {
+ type: "task-processed";
+ taskResultType: string;
+ };
+
export type WalletNotification =
| BalanceChangeNotification
- | BackupOperationErrorNotification
| ExchangeStateTransitionNotification
- | PendingOperationProcessedNotification
- | WithdrawalOperationTransitionNotification
- | TransactionStateTransitionNotification;
+ | TransactionStateTransitionNotification
+ | TaskProgressNotification
+ | RequestProgressNotification
+ | RequestOnTaskDependencyNotification
+ | TaskOnTaskDependencyNotification;
diff --git a/packages/taler-util/src/observability.ts b/packages/taler-util/src/observability.ts
new file mode 100644
index 000000000..fce265040
--- /dev/null
+++ b/packages/taler-util/src/observability.ts
@@ -0,0 +1,25 @@
+/*
+ This file is part of GNU Taler
+ (C) 2019-2024 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/>
+ */
+
+import { ObservabilityEvent } from "./index.js";
+
+/**
+ * Observability sink can be passed into various operations (HTTP requests, DB access)
+ * to do structured logging within a particular context (task, request, ...).
+ */
+export interface ObservabilityContext {
+ observe(evt: ObservabilityEvent): void;
+}
diff --git a/packages/taler-util/src/taler-types.ts b/packages/taler-util/src/taler-types.ts
index fe8f47e30..b5932aba6 100644
--- a/packages/taler-util/src/taler-types.ts
+++ b/packages/taler-util/src/taler-types.ts
@@ -58,7 +58,7 @@ import {
/**
* Denomination as found in the /keys response from the exchange.
*/
-export class ExchangeDenomination {
+export interface ExchangeDenomination {
/**
* Value of one coin of the denomination.
*/
@@ -121,7 +121,7 @@ export class ExchangeDenomination {
/**
* Signature by the auditor that a particular denomination key is audited.
*/
-export class AuditorDenomSig {
+export interface AuditorDenomSig {
/**
* Denomination public key's hash.
*/
@@ -136,7 +136,7 @@ export class AuditorDenomSig {
/**
* Auditor information as given by the exchange in /keys.
*/
-export class ExchangeAuditor {
+export interface ExchangeAuditor {
/**
* Auditor's public key.
*/
@@ -702,7 +702,7 @@ export interface MerchantTipResponseV2 {
* Element of the payback list that the
* exchange gives us in /keys.
*/
-export class Recoup {
+export interface Recoup {
/**
* The hash of the denomination public key for which the payback is offered.
*/
@@ -712,7 +712,7 @@ export class Recoup {
/**
* Structure of one exchange signing key in the /keys response.
*/
-export class ExchangeSignKeyJson {
+export interface ExchangeSignKeyJson {
stamp_start: TalerProtocolTimestamp;
stamp_expire: TalerProtocolTimestamp;
stamp_end: TalerProtocolTimestamp;
@@ -723,7 +723,7 @@ export class ExchangeSignKeyJson {
/**
* Structure that the exchange gives us in /keys.
*/
-export class ExchangeKeysJson {
+export interface ExchangeKeysJson {
/**
* Canonical, public base URL of the exchange.
*/
@@ -917,7 +917,7 @@ export interface GlobalFees {
/**
* Wire fees as announced by the exchange.
*/
-export class WireFeesJson {
+export interface WireFeesJson {
/**
* Cost of a wire transfer.
*/
@@ -947,7 +947,7 @@ export class WireFeesJson {
/**
* Proposal returned from the contract URL.
*/
-export class Proposal {
+export interface Proposal {
/**
* Contract terms for the propoal.
* Raw, un-decoded JSON object.
@@ -964,7 +964,7 @@ export class Proposal {
/**
* Response from the internal merchant API.
*/
-export class CheckPaymentResponse {
+export interface CheckPaymentResponse {
order_status: string;
refunded: boolean | undefined;
refunded_amount: string | undefined;
@@ -976,7 +976,7 @@ export class CheckPaymentResponse {
/**
* Response from the bank.
*/
-export class WithdrawOperationStatusResponse {
+export interface WithdrawOperationStatusResponse {
status: "selected" | "aborted" | "confirmed" | "pending";
selection_done: boolean;
@@ -996,19 +996,6 @@ export class WithdrawOperationStatusResponse {
wire_types: string[];
}
-/**
- * Response from the merchant.
- */
-export class RewardPickupGetResponse {
- reward_amount: string;
-
- exchange_url: string;
-
- next_url?: string;
-
- expiration: TalerProtocolTimestamp;
-}
-
export enum DenomKeyType {
Rsa = "RSA",
ClauseSchnorr = "CS",
@@ -1050,11 +1037,11 @@ export const codecForBlindedDenominationSignature = () =>
.alternative(DenomKeyType.Rsa, codecForRsaBlindedDenominationSignature())
.build("BlindedDenominationSignature");
-export class ExchangeWithdrawResponse {
+export interface ExchangeWithdrawResponse {
ev_sig: BlindedDenominationSignature;
}
-export class ExchangeWithdrawBatchResponse {
+export interface ExchangeWithdrawBatchResponse {
ev_sigs: ExchangeWithdrawResponse[];
}
@@ -1594,15 +1581,6 @@ export const codecForWithdrawOperationStatusResponse =
.property("wire_types", codecForList(codecForString()))
.build("WithdrawOperationStatusResponse");
-export const codecForRewardPickupGetResponse =
- (): Codec<RewardPickupGetResponse> =>
- buildCodecForObject<RewardPickupGetResponse>()
- .property("reward_amount", codecForString())
- .property("exchange_url", codecForString())
- .property("next_url", codecOptional(codecForString()))
- .property("expiration", codecForTimestamp)
- .build("TipPickupGetResponse");
-
export const codecForRecoupConfirmation = (): Codec<RecoupConfirmation> =>
buildCodecForObject<RecoupConfirmation>()
.property("reserve_pub", codecOptional(codecForString()))
diff --git a/packages/taler-wallet-core/src/coinSelection.ts b/packages/taler-wallet-core/src/coinSelection.ts
index f0b435b54..e1ae613bc 100644
--- a/packages/taler-wallet-core/src/coinSelection.ts
+++ b/packages/taler-wallet-core/src/coinSelection.ts
@@ -64,7 +64,7 @@ import { getAutoRefreshExecuteThreshold } from "./common.js";
import { DenominationRecord, WalletDbReadOnlyTransaction } from "./db.js";
import { isWithdrawableDenom } from "./denominations.js";
import { getExchangeWireDetailsInTx } from "./exchanges.js";
-import { InternalWalletState } from "./wallet.js";
+import { getDenomInfo, InternalWalletState } from "./wallet.js";
const logger = new Logger("coinSelection.ts");
@@ -1088,7 +1088,7 @@ export async function selectPeerCoins(
if (!coin) {
throw Error("repair not possible, coin not found");
}
- const denom = await ws.getDenomInfo(
+ const denom = await getDenomInfo(
ws,
tx,
coin.exchangeBaseUrl,
diff --git a/packages/taler-wallet-core/src/common.ts b/packages/taler-wallet-core/src/common.ts
index 8c6650f4a..08adb2515 100644
--- a/packages/taler-wallet-core/src/common.ts
+++ b/packages/taler-wallet-core/src/common.ts
@@ -61,7 +61,7 @@ import {
timestampPreciseToDb,
} from "./db.js";
import { createRefreshGroup } from "./refresh.js";
-import { InternalWalletState } from "./wallet.js";
+import { InternalWalletState, getDenomInfo } from "./wallet.js";
const logger = new Logger("operations/common.ts");
@@ -161,7 +161,7 @@ export async function spendCoins(
if (!coin) {
throw Error("coin allocated for payment doesn't exist anymore");
}
- const denom = await ws.getDenomInfo(
+ const denom = await getDenomInfo(
ws,
tx,
coin.exchangeBaseUrl,
diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts
index ed8778368..9db8cfc27 100644
--- a/packages/taler-wallet-core/src/deposits.ts
+++ b/packages/taler-wallet-core/src/deposits.ts
@@ -109,7 +109,7 @@ import {
notifyTransition,
parseTransactionIdentifier,
} from "./transactions.js";
-import { InternalWalletState } from "./wallet.js";
+import { InternalWalletState, getDenomInfo } from "./wallet.js";
import { getCandidateWithdrawalDenomsTx } from "./withdraw.js";
/**
@@ -1527,7 +1527,7 @@ export async function getCounterpartyEffectiveDepositAmount(
if (!coin) {
throw Error("can't calculate deposit amount, coin not found");
}
- const denom = await ws.getDenomInfo(
+ const denom = await getDenomInfo(
ws,
tx,
coin.exchangeBaseUrl,
@@ -1593,7 +1593,7 @@ async function getTotalFeesForDepositAmount(
if (!coin) {
throw Error("can't calculate deposit amount, coin not found");
}
- const denom = await ws.getDenomInfo(
+ const denom = await getDenomInfo(
ws,
tx,
coin.exchangeBaseUrl,
diff --git a/packages/taler-wallet-core/src/pay-merchant.ts b/packages/taler-wallet-core/src/pay-merchant.ts
index 8fcb728d4..332660ad5 100644
--- a/packages/taler-wallet-core/src/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/pay-merchant.ts
@@ -138,7 +138,11 @@ import {
notifyTransition,
parseTransactionIdentifier,
} from "./transactions.js";
-import { EXCHANGE_COINS_LOCK, InternalWalletState } from "./wallet.js";
+import {
+ EXCHANGE_COINS_LOCK,
+ getDenomInfo,
+ InternalWalletState,
+} from "./wallet.js";
import { getCandidateWithdrawalDenomsTx } from "./withdraw.js";
/**
@@ -2959,7 +2963,7 @@ async function computeRefreshRequest(
if (!coin) {
throw Error("coin not found");
}
- const denomInfo = await ws.getDenomInfo(
+ const denomInfo = await getDenomInfo(
ws,
tx,
coin.exchangeBaseUrl,
diff --git a/packages/taler-wallet-core/src/pay-peer-common.ts b/packages/taler-wallet-core/src/pay-peer-common.ts
index efb5bdb7e..dbc3376b4 100644
--- a/packages/taler-wallet-core/src/pay-peer-common.ts
+++ b/packages/taler-wallet-core/src/pay-peer-common.ts
@@ -33,7 +33,7 @@ import type { SelectedPeerCoin } from "./coinSelection.js";
import { SpendCoinDetails } from "./crypto/cryptoImplementation.js";
import { PeerPushPaymentCoinSelection, ReserveRecord } from "./db.js";
import { getTotalRefreshCost } from "./refresh.js";
-import { InternalWalletState } from "./wallet.js";
+import { InternalWalletState, getDenomInfo } from "./wallet.js";
import { getCandidateWithdrawalDenomsTx } from "./withdraw.js";
/**
@@ -50,7 +50,7 @@ export async function queryCoinInfosForSelection(
if (!coin) {
throw Error("coin not found anymore");
}
- const denom = await ws.getDenomInfo(
+ const denom = await getDenomInfo(
ws,
tx,
coin.exchangeBaseUrl,
@@ -84,7 +84,7 @@ export async function getTotalPeerPaymentCost(
if (!coin) {
throw Error("can't calculate payment cost, coin not found");
}
- const denomInfo = await ws.getDenomInfo(
+ const denomInfo = await getDenomInfo(
ws,
tx,
coin.exchangeBaseUrl,
diff --git a/packages/taler-wallet-core/src/recoup.ts b/packages/taler-wallet-core/src/recoup.ts
index 0ec71f4e7..ecf55d42b 100644
--- a/packages/taler-wallet-core/src/recoup.ts
+++ b/packages/taler-wallet-core/src/recoup.ts
@@ -63,7 +63,7 @@ import {
} from "./db.js";
import { createRefreshGroup } from "./refresh.js";
import { constructTransactionIdentifier } from "./transactions.js";
-import type { InternalWalletState } from "./wallet.js";
+import { getDenomInfo, type InternalWalletState } from "./wallet.js";
import { internalCreateWithdrawalGroup } from "./withdraw.js";
const logger = new Logger("operations/recoup.ts");
@@ -123,7 +123,7 @@ async function recoupWithdrawCoin(
): Promise<void> {
const reservePub = cs.reservePub;
const denomInfo = await ws.db.runReadOnlyTx(["denominations"], async (tx) => {
- const denomInfo = await ws.getDenomInfo(
+ const denomInfo = await getDenomInfo(
ws,
tx,
coin.exchangeBaseUrl,
@@ -194,7 +194,7 @@ async function recoupRefreshCoin(
const d = await ws.db.runReadOnlyTx(
["coins", "denominations"],
async (tx) => {
- const denomInfo = await ws.getDenomInfo(
+ const denomInfo = await getDenomInfo(
ws,
tx,
coin.exchangeBaseUrl,
@@ -258,13 +258,13 @@ async function recoupRefreshCoin(
logger.warn("refresh old coin for recoup not found");
return;
}
- const oldCoinDenom = await ws.getDenomInfo(
+ const oldCoinDenom = await getDenomInfo(
ws,
tx,
oldCoin.exchangeBaseUrl,
oldCoin.denomPubHash,
);
- const revokedCoinDenom = await ws.getDenomInfo(
+ const revokedCoinDenom = await getDenomInfo(
ws,
tx,
revokedCoin.exchangeBaseUrl,
diff --git a/packages/taler-wallet-core/src/refresh.ts b/packages/taler-wallet-core/src/refresh.ts
index f1ee84f3e..cc5eff12c 100644
--- a/packages/taler-wallet-core/src/refresh.ts
+++ b/packages/taler-wallet-core/src/refresh.ts
@@ -99,7 +99,11 @@ import {
constructTransactionIdentifier,
notifyTransition,
} from "./transactions.js";
-import { EXCHANGE_COINS_LOCK, InternalWalletState } from "./wallet.js";
+import {
+ EXCHANGE_COINS_LOCK,
+ getDenomInfo,
+ InternalWalletState,
+} from "./wallet.js";
import { getCandidateWithdrawalDenomsTx } from "./withdraw.js";
const logger = new Logger("refresh.ts");
@@ -378,7 +382,7 @@ async function provideRefreshSession(
const { availableAmount, availableDenoms } = await ws.db.runReadOnlyTx(
["denominations"],
async (tx) => {
- const oldDenom = await ws.getDenomInfo(
+ const oldDenom = await getDenomInfo(
ws,
tx,
exch.exchangeBaseUrl,
@@ -516,7 +520,7 @@ async function refreshMelt(
const oldCoin = await tx.coins.get(refreshGroup.oldCoinPubs[coinIndex]);
checkDbInvariant(!!oldCoin, "melt coin doesn't exist");
- const oldDenom = await ws.getDenomInfo(
+ const oldDenom = await getDenomInfo(
ws,
tx,
oldCoin.exchangeBaseUrl,
@@ -530,7 +534,7 @@ async function refreshMelt(
const newCoinDenoms: RefreshNewDenomInfo[] = [];
for (const dh of refreshSession.newDenoms) {
- const newDenom = await ws.getDenomInfo(
+ const newDenom = await getDenomInfo(
ws,
tx,
oldCoin.exchangeBaseUrl,
@@ -819,7 +823,7 @@ async function refreshReveal(
const oldCoin = await tx.coins.get(refreshGroup.oldCoinPubs[coinIndex]);
checkDbInvariant(!!oldCoin, "melt coin doesn't exist");
- const oldDenom = await ws.getDenomInfo(
+ const oldDenom = await getDenomInfo(
ws,
tx,
oldCoin.exchangeBaseUrl,
@@ -833,7 +837,7 @@ async function refreshReveal(
const newCoinDenoms: RefreshNewDenomInfo[] = [];
for (const dh of refreshSession.newDenoms) {
- const newDenom = await ws.getDenomInfo(
+ const newDenom = await getDenomInfo(
ws,
tx,
oldCoin.exchangeBaseUrl,
@@ -1162,7 +1166,7 @@ export async function calculateRefreshOutput(
for (const ocp of oldCoinPubs) {
const coin = await tx.coins.get(ocp.coinPub);
checkDbInvariant(!!coin, "coin must be in database");
- const denom = await ws.getDenomInfo(
+ const denom = await getDenomInfo(
ws,
tx,
coin.exchangeBaseUrl,
@@ -1210,7 +1214,7 @@ async function applyRefresh(
for (const ocp of oldCoinPubs) {
const coin = await tx.coins.get(ocp.coinPub);
checkDbInvariant(!!coin, "coin must be in database");
- const denom = await ws.getDenomInfo(
+ const denom = await getDenomInfo(
ws,
tx,
coin.exchangeBaseUrl,
@@ -1420,7 +1424,7 @@ export async function forceRefresh(
if (!coin) {
throw Error(`coin (pubkey ${c}) not found`);
}
- const denom = await ws.getDenomInfo(
+ const denom = await getDenomInfo(
ws,
tx,
coin.exchangeBaseUrl,
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index f21db0531..086c2ffa5 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -26,7 +26,7 @@ import { IDBFactory } from "@gnu-taler/idb-bridge";
import {
AmountString,
Amounts,
- AsyncCondition,
+ CancellationToken,
CoinDumpJson,
CoinStatus,
CoreApiResponse,
@@ -135,6 +135,7 @@ import {
validateIban,
} from "@gnu-taler/taler-util";
import type { HttpRequestLibrary } from "@gnu-taler/taler-util/http";
+import { ObservabilityContext } from "../../taler-util/src/observability.js";
import {
getUserAttentions,
getUserAttentionsUnreadCount,
@@ -269,6 +270,21 @@ import {
const logger = new Logger("wallet.ts");
+/**
+ * Execution context for code that is run in the wallet.
+ *
+ * Typically the excecution context is either for a wallet-core
+ * request handler or for a shepherded task.
+ */
+export interface WalletExecutionContext {
+ readonly ws: InternalWalletState;
+ readonly cryptoApi: TalerCryptoInterface;
+ readonly cancellationToken: CancellationToken;
+ readonly http: HttpRequestLibrary;
+ readonly db: DbAccess<typeof WalletStoresV1>;
+ readonly oc: ObservabilityContext;
+}
+
export const EXCHANGE_COINS_LOCK = "exchange-coins-lock";
export const EXCHANGE_RESERVES_LOCK = "exchange-reserves-lock";
@@ -440,7 +456,7 @@ async function dumpCoins(ws: InternalWalletState): Promise<CoinDumpJson> {
if (cs.type == CoinSourceType.Withdraw) {
withdrawalReservePub = cs.reservePub;
}
- const denomInfo = await ws.getDenomInfo(
+ const denomInfo = await getDenomInfo(
ws,
tx,
c.exchangeBaseUrl,
@@ -1438,6 +1454,24 @@ export class Wallet {
}
}
+export async function getDenomInfo(
+ ws: InternalWalletState,
+ tx: WalletDbReadOnlyTransaction<["denominations"]>,
+ exchangeBaseUrl: string,
+ denomPubHash: string,
+): Promise<DenominationInfo | undefined> {
+ const key = `${exchangeBaseUrl}:${denomPubHash}`;
+ const cached = ws.denomCache[key];
+ if (cached) {
+ return cached;
+ }
+ const d = await tx.denominations.get([exchangeBaseUrl, denomPubHash]);
+ if (d) {
+ return DenominationRecord.toDenomInfo(d);
+ }
+ return undefined;
+}
+
/**
* Internal state of the wallet.
*
@@ -1445,10 +1479,9 @@ export class Wallet {
*/
export class InternalWalletState {
cryptoApi: TalerCryptoInterface;
- cryptoDispatcher: CryptoDispatcher;
+ private cryptoDispatcher: CryptoDispatcher;
readonly timerGroup: TimerGroup;
- workAvailable = new AsyncCondition();
stopped = false;
private listeners: NotificationListener[] = [];
@@ -1456,7 +1489,7 @@ export class InternalWalletState {
initCalled = false;
// FIXME: Use an LRU cache here.
- private denomCache: Record<string, DenominationInfo> = {};
+ denomCache: Record<string, DenominationInfo> = {};
/**
* Promises that are waiting for a particular resource.
@@ -1517,24 +1550,6 @@ export class InternalWalletState {
}
}
- async getDenomInfo(
- ws: InternalWalletState,
- tx: WalletDbReadOnlyTransaction<["denominations"]>,
- exchangeBaseUrl: string,
- denomPubHash: string,
- ): Promise<DenominationInfo | undefined> {
- const key = `${exchangeBaseUrl}:${denomPubHash}`;
- const cached = this.denomCache[key];
- if (cached) {
- return cached;
- }
- const d = await tx.denominations.get([exchangeBaseUrl, denomPubHash]);
- if (d) {
- return DenominationRecord.toDenomInfo(d);
- }
- return undefined;
- }
-
notify(n: WalletNotification): void {
logger.trace(`Notification: ${j2s(n)}`);
for (const l of this.listeners) {
diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts
index a54295613..c3c3c2a8e 100644
--- a/packages/taler-wallet-core/src/withdraw.ts
+++ b/packages/taler-wallet-core/src/withdraw.ts
@@ -146,7 +146,7 @@ import {
WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
WALLET_EXCHANGE_PROTOCOL_VERSION,
} from "./versions.js";
-import type { InternalWalletState } from "./wallet.js";
+import { getDenomInfo, type InternalWalletState } from "./wallet.js";
/**
* Logger for this file.
@@ -678,12 +678,7 @@ async function processPlanchetGenerate(
const denomPubHash = maybeDenomPubHash;
const denom = await ws.db.runReadOnlyTx(["denominations"], async (tx) => {
- return ws.getDenomInfo(
- ws,
- tx,
- withdrawalGroup.exchangeBaseUrl,
- denomPubHash,
- );
+ return getDenomInfo(ws, tx, withdrawalGroup.exchangeBaseUrl, denomPubHash);
});
checkDbInvariant(!!denom);
const r = await ws.cryptoApi.createPlanchet({
@@ -943,7 +938,7 @@ async function processPlanchetExchangeBatchRequest(
logger.warn("processPlanchet: planchet already withdrawn");
continue;
}
- const denom = await ws.getDenomInfo(
+ const denom = await getDenomInfo(
ws,
tx,
withdrawalGroup.exchangeBaseUrl,
@@ -1057,7 +1052,7 @@ async function processPlanchetVerifyAndStoreCoin(
logger.warn("processPlanchet: planchet already withdrawn");
return;
}
- const denomInfo = await ws.getDenomInfo(
+ const denomInfo = await getDenomInfo(
ws,
tx,
withdrawalGroup.exchangeBaseUrl,
@@ -1814,7 +1809,7 @@ export async function getExchangeWithdrawalInfo(
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(
+ const denom = await getDenomInfo(
ws,
tx,
exchangeBaseUrl,
@@ -1911,7 +1906,9 @@ export interface GetWithdrawalDetailsForUriOpts {
type WithdrawalOperationMemoryMap = {
[uri: string]: boolean | undefined;
};
+
const ongoingChecks: WithdrawalOperationMemoryMap = {};
+
/**
* Get more information about a taler://withdraw URI.
*
@@ -1964,9 +1961,10 @@ export async function getWithdrawalDetailsForUri(
info.apiBaseUrl,
ws.http,
);
- console.log(
- `waiting operation (${info.operationId}) to change from pending`,
+ logger.info(
+ `waiting for operation (${info.operationId}) to change from pending`,
);
+ // FIXME: This needs a cancellation token or timeout!
bankApi
.getWithdrawalOperationById(info.operationId, {
old_state: "pending",
@@ -1980,11 +1978,6 @@ export async function getWithdrawalDetailsForUri(
2,
)}`,
);
- ws.notify({
- type: NotificationType.WithdrawalOperationTransition,
- operationId: info.operationId,
- state: resp.type === "fail" ? info.status : resp.body.status,
- });
ongoingChecks[talerWithdrawUri] = false;
});
}