From 825d2c4352022e7397854b2bd9ba7d3589873c07 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 15 Feb 2023 23:32:42 +0100 Subject: make wallet-cli runnable under qtart --- packages/taler-wallet-core/src/host-impl.node.ts | 159 +++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 packages/taler-wallet-core/src/host-impl.node.ts (limited to 'packages/taler-wallet-core/src/host-impl.node.ts') diff --git a/packages/taler-wallet-core/src/host-impl.node.ts b/packages/taler-wallet-core/src/host-impl.node.ts new file mode 100644 index 000000000..ec57e0ebe --- /dev/null +++ b/packages/taler-wallet-core/src/host-impl.node.ts @@ -0,0 +1,159 @@ +/* + 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 + */ + +/** + * Helpers to create headless wallets. + * @author Florian Dold + */ + +/** + * Imports. + */ +import type { IDBFactory } from "@gnu-taler/idb-bridge"; +// eslint-disable-next-line no-duplicate-imports +import { + BridgeIDBFactory, + MemoryBackend, + shimIndexedDB, +} from "@gnu-taler/idb-bridge"; +import { AccessStats } from "@gnu-taler/idb-bridge"; +import { Logger } from "@gnu-taler/taler-util"; +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 { SetTimeoutTimerAPI } from "./util/timer.js"; +import { Wallet } from "./wallet.js"; +import { DefaultNodeWalletArgs, makeTempfileId } from "./host-common.js"; + +const logger = new Logger("host-impl.node.ts"); + + +/** + * Get a wallet instance with default settings for node. + * + * Extended version that allows getting DB stats. + */ +export async function createNativeWalletHost2( + args: DefaultNodeWalletArgs = {}, +): Promise<{ + wallet: Wallet; + getDbStats: () => AccessStats; +}> { + BridgeIDBFactory.enableTracing = false; + const myBackend = new MemoryBackend(); + myBackend.enableTracing = false; + + const storagePath = args.persistentStoragePath; + if (storagePath) { + try { + const dbContentStr: string = fs.readFileSync(storagePath, { + encoding: "utf-8", + }); + const dbContent = JSON.parse(dbContentStr); + myBackend.importDump(dbContent); + } catch (e: any) { + const code: string = e.code; + if (code === "ENOENT") { + logger.trace("wallet file doesn't exist yet"); + } else { + logger.error("could not open wallet database file"); + throw e; + } + } + + myBackend.afterCommitCallback = async () => { + logger.trace("committing database"); + // Allow caller to stop persisting the wallet. + if (args.persistentStoragePath === undefined) { + return; + } + const tmpPath = `${args.persistentStoragePath}-${makeTempfileId(5)}.tmp`; + logger.trace("exported DB dump"); + const dbContent = myBackend.exportDump(); + fs.writeFileSync(tmpPath, JSON.stringify(dbContent, undefined, 2), { + encoding: "utf-8", + }); + // Atomically move the temporary file onto the DB path. + fs.renameSync(tmpPath, args.persistentStoragePath); + logger.trace("committing database done"); + }; + } + + BridgeIDBFactory.enableTracing = false; + + const myBridgeIdbFactory = new BridgeIDBFactory(myBackend); + const myIdbFactory: IDBFactory = myBridgeIdbFactory as any as IDBFactory; + + let myHttpLib; + if (args.httpLib) { + myHttpLib = args.httpLib; + } else { + myHttpLib = createPlatformHttpLib({ + enableThrottling: true, + }); + } + + const myVersionChange = (): Promise => { + logger.error("version change requested, should not happen"); + throw Error( + "BUG: wallet DB version change event can't happen with memory IDB", + ); + }; + + shimIndexedDB(myBridgeIdbFactory); + + const myDb = await openTalerDatabase(myIdbFactory, myVersionChange); + + let workerFactory; + const cryptoWorkerType = args.cryptoWorkerType ?? "node-worker-thread"; + if (cryptoWorkerType === "sync") { + logger.info("using synchronous crypto worker"); + workerFactory = new SynchronousCryptoWorkerFactoryPlain(); + } else if (cryptoWorkerType === "node-worker-thread") { + try { + // Try if we have worker threads available, fails in older node versions. + const _r = "require"; + // eslint-disable-next-line no-unused-vars + const worker_threads = module[_r]("worker_threads"); + // require("worker_threads"); + workerFactory = new NodeThreadCryptoWorkerFactory(); + logger.info("using node thread crypto worker"); + } catch (e) { + logger.warn( + "worker threads not available, falling back to synchronous workers", + ); + workerFactory = new SynchronousCryptoWorkerFactoryPlain(); + } + } else { + throw Error(`unsupported crypto worker type '${cryptoWorkerType}'`); + } + + const timer = new SetTimeoutTimerAPI(); + + const w = await Wallet.create(myDb, myHttpLib, timer, workerFactory); + + if (args.notifyHandler) { + w.addNotificationListener(args.notifyHandler); + } + return { + wallet: w, + getDbStats: () => myBackend.accessStats, + }; +} -- cgit v1.2.3