From 277a5c641df336d8b5b2d9bd6559e45f0b02aa79 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 29 Feb 2024 18:37:13 +0100 Subject: wallet-core: improve config handling, test builtin exchange config --- packages/taler-wallet-core/src/host-common.ts | 6 - packages/taler-wallet-core/src/host-impl.node.ts | 30 +++-- packages/taler-wallet-core/src/host-impl.qtart.ts | 30 +++-- packages/taler-wallet-core/src/wallet-api-types.ts | 61 ---------- packages/taler-wallet-core/src/wallet.ts | 133 ++++++++++++++------- 5 files changed, 123 insertions(+), 137 deletions(-) (limited to 'packages/taler-wallet-core/src') diff --git a/packages/taler-wallet-core/src/host-common.ts b/packages/taler-wallet-core/src/host-common.ts index c56d7ed1c..7651e5a12 100644 --- a/packages/taler-wallet-core/src/host-common.ts +++ b/packages/taler-wallet-core/src/host-common.ts @@ -16,7 +16,6 @@ import { WalletNotification } from "@gnu-taler/taler-util"; import { HttpRequestLibrary } from "@gnu-taler/taler-util/http"; -import { WalletConfigParameter } from "./index.js"; /** * Helpers to initiate a wallet in a host environment. @@ -45,11 +44,6 @@ export interface DefaultNodeWalletArgs { httpLib?: HttpRequestLibrary; cryptoWorkerType?: "sync" | "node-worker-thread"; - - /** - * Config parameters - */ - config?: WalletConfigParameter; } /** diff --git a/packages/taler-wallet-core/src/host-impl.node.ts b/packages/taler-wallet-core/src/host-impl.node.ts index a0c739f45..6a32a086a 100644 --- a/packages/taler-wallet-core/src/host-impl.node.ts +++ b/packages/taler-wallet-core/src/host-impl.node.ts @@ -32,7 +32,11 @@ import { shimIndexedDB, } from "@gnu-taler/idb-bridge"; import { createNodeSqlite3Impl } from "@gnu-taler/idb-bridge/node-sqlite3-bindings"; -import { Logger, SetTimeoutTimerAPI } from "@gnu-taler/taler-util"; +import { + Logger, + SetTimeoutTimerAPI, + WalletRunConfig, +} 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"; @@ -133,15 +137,18 @@ export async function createNativeWalletHost2( wallet: Wallet; getDbStats: () => AccessStats; }> { - let myHttpLib; - if (args.httpLib) { - myHttpLib = args.httpLib; - } else { - myHttpLib = createPlatformHttpLib({ - enableThrottling: true, - requireTls: !args.config?.features?.allowHttp, - }); - } + const myHttpFactory = (config: WalletRunConfig) => { + let myHttpLib; + if (args.httpLib) { + myHttpLib = args.httpLib; + } else { + myHttpLib = createPlatformHttpLib({ + enableThrottling: true, + requireTls: !config.features.allowHttp, + }); + } + return myHttpLib; + }; let dbResp: MakeDbResult; @@ -188,10 +195,9 @@ export async function createNativeWalletHost2( const w = await Wallet.create( myIdbFactory, - myHttpLib, + myHttpFactory, timer, workerFactory, - args.config, ); if (args.notifyHandler) { diff --git a/packages/taler-wallet-core/src/host-impl.qtart.ts b/packages/taler-wallet-core/src/host-impl.qtart.ts index 329e491bf..9c985d0c1 100644 --- a/packages/taler-wallet-core/src/host-impl.qtart.ts +++ b/packages/taler-wallet-core/src/host-impl.qtart.ts @@ -36,7 +36,11 @@ import { createSqliteBackend, shimIndexedDB, } from "@gnu-taler/idb-bridge"; -import { Logger, SetTimeoutTimerAPI } from "@gnu-taler/taler-util"; +import { + Logger, + SetTimeoutTimerAPI, + WalletRunConfig, +} from "@gnu-taler/taler-util"; import { createPlatformHttpLib } from "@gnu-taler/taler-util/http"; import { qjsOs, qjsStd } from "@gnu-taler/taler-util/qtart"; import { SynchronousCryptoWorkerFactoryPlain } from "./crypto/workers/synchronousWorkerFactoryPlain.js"; @@ -180,15 +184,18 @@ export async function createNativeWalletHost2( shimIndexedDB(dbResp.idbFactory); - let myHttpLib; - if (args.httpLib) { - myHttpLib = args.httpLib; - } else { - myHttpLib = createPlatformHttpLib({ - enableThrottling: true, - requireTls: !args.config?.features?.allowHttp, - }); - } + const myHttpFactory = (config: WalletRunConfig) => { + let myHttpLib; + if (args.httpLib) { + myHttpLib = args.httpLib; + } else { + myHttpLib = createPlatformHttpLib({ + enableThrottling: true, + requireTls: !config.features.allowHttp, + }); + } + return myHttpLib; + }; let workerFactory; workerFactory = new SynchronousCryptoWorkerFactoryPlain(); @@ -197,10 +204,9 @@ export async function createNativeWalletHost2( const w = await Wallet.create( myIdbFactory, - myHttpLib, + myHttpFactory, timer, workerFactory, - args.config, ); if (args.notifyHandler) { diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts index 553155ece..240012bca 100644 --- a/packages/taler-wallet-core/src/wallet-api-types.ts +++ b/packages/taler-wallet-core/src/wallet-api-types.ts @@ -277,53 +277,6 @@ export type GetVersionOp = { response: WalletCoreVersion; }; -/** - * Configurations options for the Wallet - * - * All missing values of the config will be replaced with default values - * Default values are defined by Wallet.getDefaultConfig() - */ -export type WalletConfigParameter = RecursivePartial; - -export interface BuiltinExchange { - exchangeBaseUrl: string; - currencyHint?: string; -} - -export interface WalletConfig { - /** - * 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. - */ - testing: { - /** - * Allow withdrawal of denominations even though they are about to expire. - */ - denomselAllowLate: boolean; - devModeActive: boolean; - insecureTrustExchange: boolean; - preventThrottling: boolean; - skipDefaults: boolean; - emitObservabilityEvents: boolean; - }; - - /** - * Configurations values that may be safe to show to the user - */ - features: { - allowHttp: boolean; - }; -} - // group: Basic Wallet Information /** @@ -1100,14 +1053,12 @@ export type GetPendingTasksOp = { response: any; }; - export type GetActiveTasksOp = { op: WalletApiOperation.GetActiveTasks; request: EmptyObject; response: GetActiveTasks; }; - /** * Dump all coins of the wallet in a simple JSON format. */ @@ -1305,15 +1256,3 @@ export interface WalletCoreApiClient { payload: WalletCoreRequestType, ): Promise>; } - -type Primitives = string | number | boolean; - -type RecursivePartial = { - [P in keyof T]?: T[P] extends Array - ? Array> - : T[P] extends Array - ? Array - : T[P] extends object - ? RecursivePartial - : T[P]; -} & object; diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 0dc066730..b397c43d5 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -47,6 +47,7 @@ import { ObservabilityContext, ObservabilityEventType, OpenedPromise, + PartialWalletRunConfig, PrepareWithdrawExchangeRequest, PrepareWithdrawExchangeResponse, RecoverStoredBackupRequest, @@ -64,6 +65,7 @@ import { ValidateIbanResponse, WalletCoreVersion, WalletNotification, + WalletRunConfig, WithdrawalDetailsForAmount, checkDbInvariant, codecForAbortTransaction, @@ -99,6 +101,7 @@ import { codecForGetWithdrawalDetailsForAmountRequest, codecForGetWithdrawalDetailsForUri, codecForImportDbRequest, + codecForInitRequest, codecForInitiatePeerPullPaymentRequest, codecForInitiatePeerPushDebitRequest, codecForIntegrationTestArgs, @@ -277,8 +280,6 @@ import { } from "./versions.js"; import { WalletApiOperation, - WalletConfig, - WalletConfigParameter, WalletCoreApiClient, WalletCoreResponseType, } from "./wallet-api-types.js"; @@ -685,6 +686,17 @@ async function dispatchRequestInternal( return {}; } case WalletApiOperation.InitWallet: { + const req = codecForInitRequest().decode(payload); + + logger.info(`init request: ${req}`); + + if (wex.ws.initCalled) { + logger.warn( + "initWallet called twice, new run configuration is ignored", + ); + return; + } + logger.trace("initializing wallet"); // Write to the DB to make sure that we're failing early in // case the DB is not writeable. @@ -701,6 +713,9 @@ async function dispatchRequestInternal( innerError: getErrorDetailFromException(e), }); } + + wex.ws.initWithConfig(applyRunConfigDefaults(req.config)); + wex.ws.initCalled = true; if (wex.ws.config.testing.skipDefaults) { logger.trace("skipping defaults"); @@ -1437,7 +1452,12 @@ export function getNormalWalletExecutionContext( cancellationToken, cryptoApi: ws.cryptoApi, db: ws.db, - http: ws.http, + get http() { + if (ws.initCalled) { + return ws.http; + } + throw Error("wallet not initialized"); + }, taskScheduler: ws.taskScheduler, oc, }; @@ -1456,7 +1476,7 @@ async function handleCoreApiRequest( let wex: WalletExecutionContext; let oc: ObservabilityContext; - if (ws.config.testing.emitObservabilityEvents) { + if (ws.initCalled && ws.config.testing.emitObservabilityEvents) { oc = { observe(evt) { ws.notify({ @@ -1512,6 +1532,34 @@ async function handleCoreApiRequest( } } +export function applyRunConfigDefaults( + wcp?: PartialWalletRunConfig, +): WalletRunConfig { + return { + builtin: { + exchanges: wcp?.builtin?.exchanges ?? [ + { + exchangeBaseUrl: "https://exchange.demo.taler.net/", + currencyHint: "KUDOS", + }, + ], + }, + features: { + allowHttp: wcp?.features?.allowHttp ?? false, + }, + testing: { + denomselAllowLate: wcp?.testing?.denomselAllowLate ?? false, + devModeActive: wcp?.testing?.devModeActive ?? false, + insecureTrustExchange: wcp?.testing?.insecureTrustExchange ?? false, + preventThrottling: wcp?.testing?.preventThrottling ?? false, + skipDefaults: wcp?.testing?.skipDefaults ?? false, + emitObservabilityEvents: wcp?.testing?.emitObservabilityEvents ?? false, + }, + }; +} + +export type HttpFactory = (config: WalletRunConfig) => HttpRequestLibrary; + /** * Public handle to a running wallet. */ @@ -1521,17 +1569,15 @@ export class Wallet { private constructor( idb: IDBFactory, - http: HttpRequestLibrary, + httpFactory: HttpFactory, timer: TimerAPI, cryptoWorkerFactory: CryptoWorkerFactory, - config?: WalletConfigParameter, ) { this.ws = new InternalWalletState( idb, - http, + httpFactory, timer, cryptoWorkerFactory, - Wallet.getEffectiveConfig(config), ); } @@ -1544,44 +1590,15 @@ export class Wallet { static async create( idb: IDBFactory, - http: HttpRequestLibrary, + httpFactory: HttpFactory, timer: TimerAPI, cryptoWorkerFactory: CryptoWorkerFactory, - config?: WalletConfigParameter, ): Promise { - const w = new Wallet(idb, http, timer, cryptoWorkerFactory, config); + const w = new Wallet(idb, httpFactory, timer, cryptoWorkerFactory); w._client = await getClientFromWalletState(w.ws); return w; } - public static defaultConfig: Readonly = { - builtin: { - exchanges: [ - { - exchangeBaseUrl: "https://exchange.demo.taler.net/", - currencyHint: "KUDOS", - }, - ], - }, - features: { - allowHttp: false, - }, - testing: { - preventThrottling: false, - devModeActive: false, - insecureTrustExchange: false, - denomselAllowLate: false, - skipDefaults: false, - emitObservabilityEvents: false, - }, - }; - - static getEffectiveConfig( - param?: WalletConfigParameter, - ): Readonly { - return deepMerge(Wallet.defaultConfig, param ?? {}); - } - addNotificationListener(f: (n: WalletNotification) => void): CancelFn { return this.ws.addNotificationListener(f); } @@ -1637,10 +1654,12 @@ export class InternalWalletState { taskScheduler: TaskScheduler = new TaskSchedulerImpl(this); - config: Readonly; + private _config: Readonly | undefined; private _db: DbAccess | undefined = undefined; + private _http: HttpRequestLibrary | undefined = undefined; + get db(): DbAccess { if (!this._db) { throw Error("db not initialized"); @@ -1648,20 +1667,42 @@ export class InternalWalletState { return this._db; } + initWithConfig(newConfig: WalletRunConfig): void { + if (this._config) { + throw Error("config already set"); + } + this._config = newConfig; + + this._http = this.httpFactory(newConfig); + + if (this.config.testing.devModeActive) { + this._http = new DevExperimentHttpLib(this.http); + } + } + + get config(): WalletRunConfig { + if (!this._config) { + throw Error("config not initialized"); + } + return this._config; + } + + get http(): HttpRequestLibrary { + if (!this._http) { + throw Error("wallet not initialized"); + } + return this._http; + } + constructor( public idb: IDBFactory, - public http: HttpRequestLibrary, + private httpFactory: HttpFactory, public timer: TimerAPI, cryptoWorkerFactory: CryptoWorkerFactory, - configParam: WalletConfig, ) { this.cryptoDispatcher = new CryptoDispatcher(cryptoWorkerFactory); this.cryptoApi = this.cryptoDispatcher.cryptoApi; this.timerGroup = new TimerGroup(timer); - this.config = configParam; - if (this.config.testing.devModeActive) { - this.http = new DevExperimentHttpLib(this.http); - } } async ensureWalletDbOpen(): Promise { -- cgit v1.2.3