taler-typescript-core

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

commit d9f1f052617330cb5423e12ec611fd325cce34ca
parent 78eb78346a9eba3de75d5d013108653afd0cdbc2
Author: Florian Dold <florian@dold.me>
Date:   Thu, 16 Apr 2026 15:47:51 +0200

wallet-core: add TOPS exchange to builtin

We also version the built-in list now, to allow it to evolve without
overriding the user's settings. The wallet-core client can no longer
configure the builtin exchanges list, the corresponding test has been
removed as well.

Diffstat:
Dpackages/taler-harness/src/integrationtests/test-wallet-config.ts | 67-------------------------------------------------------------------
Mpackages/taler-harness/src/integrationtests/testrunner.ts | 6++----
Mpackages/taler-util/src/types-taler-wallet.ts | 10----------
Mpackages/taler-wallet-core/src/db.ts | 2+-
Mpackages/taler-wallet-core/src/exchanges.ts | 20+++++++++-----------
Mpackages/taler-wallet-core/src/wallet.ts | 64++++++++++++++++++++++++++++++++++++++--------------------------
6 files changed, 50 insertions(+), 119 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-wallet-config.ts b/packages/taler-harness/src/integrationtests/test-wallet-config.ts @@ -1,67 +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 { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState } from "../harness/harness.js"; -import { createWalletDaemonWithClient } from "../harness/environments.js"; - -export async function runWalletConfigTest(t: GlobalTestState) { - const w1 = await createWalletDaemonWithClient(t, { - name: "w1", - config: { - builtin: { - exchanges: [], - }, - }, - }); - - const exchangesResp1 = await w1.walletClient.call( - WalletApiOperation.ListExchanges, - {}, - ); - - t.assertDeepEqual(exchangesResp1.exchanges.length, 0); - - const w2 = await createWalletDaemonWithClient(t, { - name: "w2", - config: { - builtin: { - exchanges: [ - { - exchangeBaseUrl: "https://exchange.demo.taler.net/", - currencyHint: "KUDOS", - }, - { - exchangeBaseUrl: "https://exchange.test.taler.net/", - currencyHint: "TESTKUDOS", - }, - ], - }, - }, - }); - - const exchangesResp2 = await w2.walletClient.call( - WalletApiOperation.ListExchanges, - {}, - ); - - t.assertDeepEqual(exchangesResp2.exchanges.length, 2); -} - -runWalletConfigTest.suites = ["wallet"]; diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts @@ -147,6 +147,7 @@ import { runPeerPullTest } from "./test-peer-pull.js"; import { runPeerPushLargeTest } from "./test-peer-push-large.js"; import { runPeerPushTest } from "./test-peer-push.js"; import { runPeerRepairTest } from "./test-peer-repair.js"; +import { runPreparedTransferTest } from "./test-prepared-transfer.js"; import { runRefundAutoTest } from "./test-refund-auto.js"; import { runRefundGoneTest } from "./test-refund-gone.js"; import { runRefundIncrementalTest } from "./test-refund-incremental.js"; @@ -179,7 +180,6 @@ import { runWalletBlockedPayMerchantTest } from "./test-wallet-blocked-pay-merch import { runWalletBlockedPayPeerPullTest } from "./test-wallet-blocked-pay-peer-pull.js"; import { runWalletBlockedPayPeerPushTest } from "./test-wallet-blocked-pay-peer-push.js"; import { runWalletCliTerminationTest } from "./test-wallet-cli-termination.js"; -import { runWalletConfigTest } from "./test-wallet-config.js"; import { runWalletContactsBasicTest } from "./test-wallet-contacts-basic.js"; import { runWalletCryptoWorkerTest } from "./test-wallet-cryptoworker.js"; import { runWalletDblessTest } from "./test-wallet-dbless.js"; @@ -224,7 +224,6 @@ import { runWithdrawalHugeTest } from "./test-withdrawal-huge.js"; import { runWithdrawalIdempotentTest } from "./test-withdrawal-idempotent.js"; import { runWithdrawalManualTest } from "./test-withdrawal-manual.js"; import { runWithdrawalPrepareTest } from "./test-withdrawal-prepare.js"; -import { runPreparedTransferTest } from "./test-prepared-transfer.js" /** * Test runner. @@ -315,7 +314,6 @@ const allTests: TestMainFunction[] = [ runOtpTest, runWalletBalanceNotificationsTest, runExchangeManagementTest, - runWalletConfigTest, runWalletObservabilityTest, runWalletDevExperimentsTest, runWalletBalanceZeroTest, @@ -433,7 +431,7 @@ const allTests: TestMainFunction[] = [ runMerchantDepositLargeTest, runWireMetadataTest, runMerchantPaytoReuseTest, - runPreparedTransferTest + runPreparedTransferTest, ]; export interface TestRunSpec { diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts @@ -419,7 +419,6 @@ export interface BuiltinExchange { } export interface PartialWalletRunConfig { - builtin?: Partial<WalletRunConfig["builtin"]>; testing?: Partial<WalletRunConfig["testing"]>; features?: Partial<WalletRunConfig["features"]>; lazyTaskLoop?: Partial<WalletRunConfig["lazyTaskLoop"]>; @@ -428,15 +427,6 @@ export interface PartialWalletRunConfig { export interface WalletRunConfig { /** - * Initialization values useful for a complete startup. - * - * These are values may be overridden by different wallets - */ - builtin: { - exchanges: BuiltinExchange[]; - }; - - /** * Unsafe options which it should only be used to create * testing environment. */ diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts @@ -1727,7 +1727,7 @@ export type ConfigRecord = key: ConfigRecordKey.WalletBackupState; value: WalletBackupConfState; } - | { key: ConfigRecordKey.CurrencyDefaultsApplied; value: boolean } + | { key: ConfigRecordKey.CurrencyDefaultsApplied; value: boolean | number } | { key: ConfigRecordKey.TestLoopTx; value: number } | { key: ConfigRecordKey.LastInitInfo; value: DbProtocolTimestamp } | { key: ConfigRecordKey.MaterializedTransactionsVersion; value: number } diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts @@ -761,7 +761,7 @@ export async function addPresetExchangeEntry( tx: WalletIndexedDbTransaction, exchangeBaseUrl: string, currencyHint?: string, -): Promise<{ notification?: WalletNotification }> { +): Promise<void> { let exchange = await tx.exchanges.get(exchangeBaseUrl); if (!exchange) { const r: ExchangeEntryRecord = { @@ -783,17 +783,15 @@ export async function addPresetExchangeEntry( tosCurrentEtag: undefined, }; await tx.exchanges.put(r); - return { - notification: { - type: NotificationType.ExchangeStateTransition, - exchangeBaseUrl: exchangeBaseUrl, - // Exchange did not exist yet - oldExchangeState: undefined, - newExchangeState: getExchangeState(r), - }, - }; + tx.notify({ + type: NotificationType.ExchangeStateTransition, + exchangeBaseUrl: exchangeBaseUrl, + // Exchange did not exist yet + oldExchangeState: undefined, + newExchangeState: getExchangeState(r), + }); } - return {}; + return; } async function provideExchangeRecordInTx( diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts @@ -526,38 +526,58 @@ export type NotificationListener = (n: WalletNotification) => void; type CancelFn = () => void; +const currentDefaultsVersion = 2; + +/** + * Exchanges that ship with wallet-core. + */ +const builtinExchanges = [ + { + exchangeBaseUrl: "https://exchange.demo.taler.net/", + currencyHint: "KUDOS", + versionAdded: 1, + }, + { + exchangeBaseUrl: "https://exchange.taler-ops.ch/", + currencyHint: "CHF", + versionAdded: 2, + }, +]; + /** * Insert the hard-coded defaults for exchanges, coins and * auditors into the database, unless these defaults have * already been applied. */ async function fillDefaults(wex: WalletExecutionContext): Promise<void> { - const notifications: WalletNotification[] = []; await wex.runLegacyWalletDbTx(async (tx) => { - const appliedRec = await tx.config.get("currencyDefaultsApplied"); - let alreadyApplied = appliedRec ? !!appliedRec.value : false; - if (alreadyApplied) { - logger.trace("defaults already applied"); - return; + let appliedRec = await tx.config.get("currencyDefaultsApplied"); + let appliedVersion = appliedRec ? !!appliedRec.value : 0; + if (appliedRec != null) { + if (appliedRec.value === true) { + appliedVersion = 1; + } else if (appliedRec.value == false) { + appliedVersion = 0; + } else if (typeof appliedRec.value === "number") { + appliedVersion = appliedRec.value; + } else { + logger.error("invalid value for currencyDefaultsApplied config"); + appliedVersion = 0; + } + } else { + appliedVersion = 0; } - for (const exch of wex.ws.config.builtin.exchanges) { - const resp = await addPresetExchangeEntry( - tx, - exch.exchangeBaseUrl, - exch.currencyHint, - ); - if (resp.notification) { - notifications.push(resp.notification); + for (const exch of builtinExchanges) { + if (exch.versionAdded < currentDefaultsVersion) { + continue; } + await addPresetExchangeEntry(tx, exch.exchangeBaseUrl, exch.currencyHint); } await tx.config.put({ key: ConfigRecordKey.CurrencyDefaultsApplied, - value: true, + value: currentDefaultsVersion, }); }); - for (const notif of notifications) { - wex.ws.notify(notif); - } } /** @@ -3015,14 +3035,6 @@ function applyRunConfigDefaults(wcp?: PartialWalletRunConfig): WalletRunConfig { logger.warn(`allowHttp flag not supported anymore`); } return { - builtin: { - exchanges: wcp?.builtin?.exchanges ?? [ - { - exchangeBaseUrl: "https://exchange.demo.taler.net/", - currencyHint: "KUDOS", - }, - ], - }, features: { allowHttp: true, enableV1Contracts: wcp?.features?.enableV1Contracts ?? false,