From cc4e8ddc85d36f29a7385a7f4eb08c77f46b3af6 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 31 Jul 2019 01:33:56 +0200 Subject: headless wallet WIP --- src/crypto/cryptoApi-test.ts | 24 ++++++++++++++---------- src/crypto/cryptoApi.ts | 29 ++++++++++++++++++++++++++--- src/crypto/cryptoWorker.ts | 21 +++++++++++++++------ src/crypto/emscInterface-test.ts | 8 +++++++- src/crypto/emscInterface.ts | 3 +++ src/crypto/emscLoader.js | 36 +++++++++++++++++++++++++++++------- 6 files changed, 94 insertions(+), 27 deletions(-) (limited to 'src/crypto') diff --git a/src/crypto/cryptoApi-test.ts b/src/crypto/cryptoApi-test.ts index 24342a436..6d43e2e6e 100644 --- a/src/crypto/cryptoApi-test.ts +++ b/src/crypto/cryptoApi-test.ts @@ -26,10 +26,12 @@ import { import { CryptoApi } from "./cryptoApi"; -const masterPub1: string = "CQQZ9DY3MZ1ARMN5K1VKDETS04Y2QCKMMCFHZSWJWWVN82BTTH00"; +const masterPub1: string = + "CQQZ9DY3MZ1ARMN5K1VKDETS04Y2QCKMMCFHZSWJWWVN82BTTH00"; const denomValid1: DenominationRecord = { - denomPub: "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GHS84R3JHHP6GSM2D9Q6514CGT568R32C9J6CWM4DSH64TM4DSM851K0CA48CVKAC1P6H144C2160T46DHK8CVM4HJ274S38C1M6S338D9N6GWM8DT684T3JCT36S13EC9G88R3EGHQ8S0KJGSQ60SKGD216N33AGJ2651K2E9S60TMCD1N75244HHQ6X33EDJ570R3GGJ2651MACA38D130DA560VK4HHJ68WK2CA26GW3ECSH6D13EC9S88VK2GT66WVK8D9G750K0D9R8RRK4DHQ71332GHK8D23GE26710M2H9K6WVK8HJ38MVKEGA66N23AC9H88VKACT58MV3CCSJ6H1K4DT38GRK0C9M8N33CE1R60V4AHA38H1KECSH6S33JH9N8GRKGH1K68S36GH354520818CMG26C1H60R30C935452081918G2J2G0", + denomPub: + "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30GHS84R3JHHP6GSM2D9Q6514CGT568R32C9J6CWM4DSH64TM4DSM851K0CA48CVKAC1P6H144C2160T46DHK8CVM4HJ274S38C1M6S338D9N6GWM8DT684T3JCT36S13EC9G88R3EGHQ8S0KJGSQ60SKGD216N33AGJ2651K2E9S60TMCD1N75244HHQ6X33EDJ570R3GGJ2651MACA38D130DA560VK4HHJ68WK2CA26GW3ECSH6D13EC9S88VK2GT66WVK8D9G750K0D9R8RRK4DHQ71332GHK8D23GE26710M2H9K6WVK8HJ38MVKEGA66N23AC9H88VKACT58MV3CCSJ6H1K4DT38GRK0C9M8N33CE1R60V4AHA38H1KECSH6S33JH9N8GRKGH1K68S36GH354520818CMG26C1H60R30C935452081918G2J2G0", denomPubHash: "dummy", exchangeBaseUrl: "https://exchange.example.com/", feeDeposit: { @@ -53,7 +55,8 @@ const denomValid1: DenominationRecord = { value: 0, }, isOffered: true, - masterSig: "CJFJCQ48Q45PSGJ5KY94N6M2TPARESM2E15BSPBD95YVVPEARAEQ6V6G4Z2XBMS0QM0F3Y9EYVP276FCS90EQ1578ZC8JHFBZ3NGP3G", + masterSig: + "CJFJCQ48Q45PSGJ5KY94N6M2TPARESM2E15BSPBD95YVVPEARAEQ6V6G4Z2XBMS0QM0F3Y9EYVP276FCS90EQ1578ZC8JHFBZ3NGP3G", stampExpireDeposit: "/Date(1851580381)/", stampExpireLegal: "/Date(1567756381)/", stampExpireWithdraw: "/Date(2482300381)/", @@ -69,24 +72,25 @@ const denomValid1: DenominationRecord = { const denomInvalid1 = JSON.parse(JSON.stringify(denomValid1)); denomInvalid1.value.value += 1; -test("string hashing", async (t) => { +test("string hashing", async t => { const crypto = new CryptoApi(); const s = await crypto.hashString("hello taler"); - const sh = "8RDMADB3YNF3QZBS3V467YZVJAMC2QAQX0TZGVZ6Q5PFRRAJFT70HHN0QF661QR9QWKYMMC7YEMPD679D2RADXCYK8Y669A2A5MKQFR"; + const sh = + "8RDMADB3YNF3QZBS3V467YZVJAMC2QAQX0TZGVZ6Q5PFRRAJFT70HHN0QF661QR9QWKYMMC7YEMPD679D2RADXCYK8Y669A2A5MKQFR"; t.true(s === sh); t.pass(); }); -test("precoin creation", async (t) => { +test("precoin creation", async t => { const crypto = new CryptoApi(); - const {priv, pub} = await crypto.createEddsaKeypair(); + const { priv, pub } = await crypto.createEddsaKeypair(); const r: ReserveRecord = { created: 0, current_amount: null, exchange_base_url: "https://example.com/exchange", hasPayback: false, - precoin_amount: {currency: "PUDOS", value: 0, fraction: 0}, - requested_amount: {currency: "PUDOS", value: 0, fraction: 0}, + precoin_amount: { currency: "PUDOS", value: 0, fraction: 0 }, + requested_amount: { currency: "PUDOS", value: 0, fraction: 0 }, reserve_priv: priv, reserve_pub: pub, timestamp_confirmed: 0, @@ -98,7 +102,7 @@ test("precoin creation", async (t) => { t.pass(); }); -test("denom validation", async (t) => { +test("denom validation", async t => { const crypto = new CryptoApi(); let v: boolean; v = await crypto.isValidDenom(denomValid1, masterPub1); diff --git a/src/crypto/cryptoApi.ts b/src/crypto/cryptoApi.ts index 43a3bc228..d3a93ff8d 100644 --- a/src/crypto/cryptoApi.ts +++ b/src/crypto/cryptoApi.ts @@ -98,6 +98,28 @@ export class CryptoApi { */ private numBusy: number = 0; + public enableTracing = false; + + /** + * Terminate all worker threads. + */ + terminateWorkers() { + for (let worker of this.workers) { + if (worker.w) { + worker.w.terminate(); + if (worker.terminationTimerHandle) { + worker.terminationTimerHandle.clear(); + worker.terminationTimerHandle = null; + } + if (worker.currentWorkItem) { + worker.currentWorkItem.reject(Error("explicitly terminated")); + worker.currentWorkItem = null; + } + worker.w = null; + } + } + } + /** * Start a worker (if not started) and set as busy. */ @@ -136,7 +158,7 @@ export class CryptoApi { ws.w = null; } }; - ws.terminationTimerHandle = timer.after(20 * 1000, destroy); + ws.terminationTimerHandle = timer.after(5 * 1000, destroy); } handleWorkerError(ws: WorkerState, e: ErrorEvent) { @@ -163,7 +185,7 @@ export class CryptoApi { this.findWork(ws); } - findWork(ws: WorkerState) { + private findWork(ws: WorkerState) { // try to find more work for this worker for (let i = 0; i < NUM_PRIO; i++) { const q = this.workQueues[NUM_PRIO - i - 1]; @@ -193,7 +215,8 @@ export class CryptoApi { console.error(`RPC with id ${id} has no registry entry`); return; } - console.log( + + this.enableTracing && console.log( `rpc ${currentWorkItem.operation} took ${timer.performanceNow() - currentWorkItem.startTime}ms`, ); diff --git a/src/crypto/cryptoWorker.ts b/src/crypto/cryptoWorker.ts index 9c5263a6f..5acda9053 100644 --- a/src/crypto/cryptoWorker.ts +++ b/src/crypto/cryptoWorker.ts @@ -56,6 +56,9 @@ import { import * as native from "./emscInterface"; namespace RpcFunctions { + + export let enableTracing: boolean = false; + /** * Create a pre-coin of the given denomination to be withdrawn from then given * reserve. @@ -735,19 +738,25 @@ worker.onmessage = (msg: MessageEvent) => { return; } - console.log("onmessage with", msg.data.operation); - console.log("foo"); + if (RpcFunctions.enableTracing) { + console.log("onmessage with", msg.data.operation); + } emscLoader.getLib().then(p => { const lib = p.lib; if (!native.isInitialized()) { - console.log("initializing emscripten for then first time with lib"); + if (RpcFunctions.enableTracing) { + console.log("initializing emscripten for then first time with lib"); + } native.initialize(lib); } - - console.log("about to execute", msg.data.operation); + if (RpcFunctions.enableTracing) { + console.log("about to execute", msg.data.operation); + } const res = f(...msg.data.args); - console.log("finished executing", msg.data.operation); + if (RpcFunctions.enableTracing) { + console.log("finished executing", msg.data.operation); + } worker.postMessage({ result: res, id: msg.data.id }); }); }; diff --git a/src/crypto/emscInterface-test.ts b/src/crypto/emscInterface-test.ts index 58d83e6fe..305e50ff4 100644 --- a/src/crypto/emscInterface-test.ts +++ b/src/crypto/emscInterface-test.ts @@ -17,8 +17,14 @@ // tslint:disable:max-line-length import test from "ava"; +import * as emscLoader from "./emscLoader"; import * as native from "./emscInterface"; +test.before(async () => { + const { lib } = await emscLoader.getLib(); + native.initialize(lib); +}); + test("string hashing", (t) => { const x = native.ByteArray.fromStringWithNull("hello taler"); const h = "8RDMADB3YNF3QZBS3V467YZVJAMC2QAQX0TZGVZ6Q5PFRRAJFT70HHN0QF661QR9QWKYMMC7YEMPD679D2RADXCYK8Y669A2A5MKQFR"; @@ -99,7 +105,7 @@ test("withdraw-request", (t) => { }); -test("withdraw-request", (t) => { +test("currency-conversion", (t) => { const a1 = new native.Amount({currency: "KUDOS", value: 1, fraction: 50000000}); const a2 = new native.Amount({currency: "KUDOS", value: 1, fraction: 50000000}); a1.add(a2); diff --git a/src/crypto/emscInterface.ts b/src/crypto/emscInterface.ts index 2ddc15a37..70a85cda1 100644 --- a/src/crypto/emscInterface.ts +++ b/src/crypto/emscInterface.ts @@ -43,6 +43,9 @@ export function initialize(lib: EmscLib) { if (!lib) { throw Error("library must be object"); } + if (!lib.ccall) { + throw Error("sanity check failed: EmscLib does not have 'ccall'"); + } if (maybeEmscEnv) { throw Error("emsc lib already initialized"); } diff --git a/src/crypto/emscLoader.js b/src/crypto/emscLoader.js index 59437da42..25dc6b85c 100644 --- a/src/crypto/emscLoader.js +++ b/src/crypto/emscLoader.js @@ -25,20 +25,29 @@ */ let cachedLib = undefined; +let cachedLibPromise = undefined; + +export let enableTracing = false; /** * Load the taler emscripten lib. * * If in a WebWorker, importScripts is used. Inside a browser, the module must * be globally available. Inside node, require is used. + * + * Returns a Promise<{ lib: EmscLib }> */ export function getLib() { - console.log("in getLib"); + enableTracing && console.log("in getLib"); if (cachedLib) { - console.log("lib is cached"); + enableTracing && console.log("lib is cached"); return Promise.resolve({ lib: cachedLib }); } + if (cachedLibPromise) { + return cachedLibPromise; + } if (typeof require !== "undefined") { + enableTracing && console.log("trying to load emscripten lib with 'require'"); // Make sure that TypeScript doesn't try // to check the taler-emscripten-lib. const indirectRequire = require; @@ -49,17 +58,30 @@ export function getLib() { const savedImportScripts = g.importScripts; delete g.importScripts; // Assume that the code is run from the build/ directory. - const lib = indirectRequire("../../../emscripten/taler-emscripten-lib.js"); + const libFn = indirectRequire("../../../emscripten/taler-emscripten-lib.js"); + const lib = libFn(); g.importScripts = savedImportScripts; if (lib) { - cachedLib = lib; - return Promise.resolve({ lib: cachedLib }); + if (!lib.ccall) { + throw Error("sanity check failed: taler-emscripten lib does not have 'ccall'"); + } + cachedLibPromise = new Promise((resolve, reject) => { + lib.onRuntimeInitialized = () => { + cachedLib = lib; + cachedLibPromise = undefined; + resolve({ lib: cachedLib }); + }; + }); + return cachedLibPromise; + } else { + // When we're running as a webpack bundle, the above require might + // have failed and returned 'undefined', so we try other ways to import. + console.log("failed to load emscripten lib with 'require', trying alternatives"); } - // When we're running as a webpack bundle, the above require might - // have failed and returned 'undefined', so we try other ways to import. } if (typeof importScripts !== "undefined") { + console.log("trying to load emscripten lib with 'importScripts'"); self.TalerEmscriptenLib = {}; importScripts('/emscripten/taler-emscripten-lib.js') if (!self.TalerEmscriptenLib) { -- cgit v1.2.3