gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-wallet-webex] 05/05: crypto worker refactoring


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] 05/05: crypto worker refactoring
Date: Fri, 16 Aug 2019 15:04:06 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-webex.

commit a1e0fc3b88ed4305f942fadbea66b29a3934721c
Author: Florian Dold <address@hidden>
AuthorDate: Fri Aug 16 15:03:52 2019 +0200

    crypto worker refactoring
---
 src/crypto/browserWorkerEntry.ts   | 120 ++++++++++++++++++++++++++++++++++++
 src/crypto/cryptoApi-test.ts       |   3 +-
 src/crypto/cryptoApi.ts            |  44 +------------
 src/crypto/cryptoWorker.ts         |  81 ++----------------------
 src/crypto/emscInterface-test.ts   |  35 ++++++-----
 src/crypto/emscInterface.ts        |  49 ++++++++++++++-
 src/crypto/emscLoader.d.ts         |  64 -------------------
 src/crypto/emscLoader.js           | 117 -----------------------------------
 src/crypto/nodeEmscriptenLoader.ts | 102 +++++++++++++++++++++++++++++++
 src/crypto/nodeProcessWorker.ts    |  64 ++++++++++++-------
 src/crypto/nodeWorkerEntry.ts      | 105 +++++++++++++++----------------
 src/crypto/synchronousWorker.ts    | 122 ++++++++-----------------------------
 src/headless/taler-wallet-cli.ts   |   2 +-
 tsconfig.json                      |   4 +-
 webpack.config.js                  |   5 +-
 15 files changed, 424 insertions(+), 493 deletions(-)

diff --git a/src/crypto/browserWorkerEntry.ts b/src/crypto/browserWorkerEntry.ts
new file mode 100644
index 00000000..3df59fe0
--- /dev/null
+++ b/src/crypto/browserWorkerEntry.ts
@@ -0,0 +1,120 @@
+/*
+ This file is part of TALER
+ (C) 2016 GNUnet e.V.
+
+ 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.
+
+ 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
+ TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Web worker for crypto operations.
+ */
+
+/**
+ * Imports.
+ */
+
+import { CryptoImplementation } from "./cryptoImplementation";
+import { EmscEnvironment } from "./emscInterface";
+
+const worker: Worker = (self as any) as Worker;
+
+class BrowserEmscriptenLoader {
+  private cachedEmscEnvironment: EmscEnvironment | undefined = undefined;
+  private cachedEmscEnvironmentPromise:
+    | Promise<EmscEnvironment>
+    | undefined = undefined;
+
+  async getEmscriptenEnvironment(): Promise<EmscEnvironment> {
+
+    if (this.cachedEmscEnvironment) {
+      return this.cachedEmscEnvironment;
+    }
+
+    if (this.cachedEmscEnvironmentPromise) {
+      return this.cachedEmscEnvironmentPromise;
+    }
+
+    console.log("loading emscripten lib with 'importScripts'");
+    // @ts-ignore
+    self.TalerEmscriptenLib = {};
+    // @ts-ignore
+    importScripts('/emscripten/taler-emscripten-lib.js')
+    // @ts-ignore
+    if (!self.TalerEmscriptenLib) {
+      throw Error("can't import taler emscripten lib");
+    }
+    const locateFile = (path: string, scriptDir: string) => {
+      console.log("locating file", "path", path, "scriptDir", scriptDir);
+      // This is quite hacky and assumes that our scriptDir is dist/
+      return scriptDir + "../emscripten/" + path;
+    };
+    console.log("instantiating TalerEmscriptenLib");
+    // @ts-ignore
+    const lib = self.TalerEmscriptenLib({ locateFile });
+    return new Promise((resolve, reject) => {
+      lib.then((mod: any) => {
+        this.cachedEmscEnvironmentPromise = undefined;
+        const emsc = new EmscEnvironment(mod);
+        this.cachedEmscEnvironment = new EmscEnvironment(mod);
+        console.log("emscripten module fully loaded");
+        resolve(emsc);
+      });
+    });
+  }
+}
+
+let loader = new BrowserEmscriptenLoader();
+
+async function handleRequest(operation: string, id: number, args: string[]) {
+  let emsc = await loader.getEmscriptenEnvironment();
+
+  const impl = new CryptoImplementation(emsc);
+
+  if (!(operation in impl)) {
+    console.error(`crypto operation '${operation}' not found`);
+    return;
+  }
+
+  try {
+    const result = (impl as any)[operation](...args);
+    worker.postMessage({ result, id });
+  } catch (e) {
+    console.log("error during operation", e);
+    return;
+  }
+}
+
+worker.onmessage = (msg: MessageEvent) => {
+  const args = msg.data.args;
+  if (!Array.isArray(args)) {
+    console.error("args must be array");
+    return;
+  }
+  const id = msg.data.id;
+  if (typeof id !== "number") {
+    console.error("RPC id must be number");
+    return;
+  }
+  const operation = msg.data.operation;
+  if (typeof operation !== "string") {
+    console.error("RPC operation must be string");
+    return;
+  }
+
+  if (CryptoImplementation.enableTracing) {
+    console.log("onmessage with", operation);
+  }
+
+  handleRequest(operation, id, args).catch((e) => {
+    console.error("error in browsere worker", e);
+  });
+};
diff --git a/src/crypto/cryptoApi-test.ts b/src/crypto/cryptoApi-test.ts
index 9c592412..48231e5f 100644
--- a/src/crypto/cryptoApi-test.ts
+++ b/src/crypto/cryptoApi-test.ts
@@ -24,7 +24,8 @@ import {
   ReserveRecord,
 } from "../dbTypes";
 
-import { CryptoApi, NodeCryptoWorkerFactory } from "./cryptoApi";
+import { CryptoApi } from "./cryptoApi";
+import { NodeCryptoWorkerFactory } from "./nodeProcessWorker";
 
 const masterPub1: string =
   "CQQZ9DY3MZ1ARMN5K1VKDETS04Y2QCKMMCFHZSWJWWVN82BTTH00";
diff --git a/src/crypto/cryptoApi.ts b/src/crypto/cryptoApi.ts
index c68b1070..a4e12c00 100644
--- a/src/crypto/cryptoApi.ts
+++ b/src/crypto/cryptoApi.ts
@@ -34,6 +34,8 @@ import {
   WireFee,
 } from "../dbTypes";
 
+import { CryptoWorker } from "./cryptoWorker";
+
 import { ContractTerms, PaybackRequest } from "../talerTypes";
 
 import { BenchmarkResult, CoinWithDenom, PayCoinInfo } from "../walletTypes";
@@ -83,15 +85,6 @@ interface WorkItem {
  */
 const NUM_PRIO = 5;
 
-interface CryptoWorker {
-  postMessage(message: any): void;
-
-  terminate(): void;
-
-  onmessage: (m: any) => void;
-  onerror: (m: any) => void;
-}
-
 export interface CryptoWorkerFactory {
   /**
    * Start a new worker.
@@ -105,21 +98,6 @@ export interface CryptoWorkerFactory {
   getConcurrency(): number;
 }
 
-export class NodeCryptoWorkerFactory implements CryptoWorkerFactory {
-  startWorker(): CryptoWorker {
-    if (typeof require === "undefined") {
-      throw Error("cannot make worker, require(...) not defined");
-    }
-    const workerCtor = require("./nodeProcessWorker").Worker;
-    const workerPath = __dirname + "/cryptoWorker.js";
-    return new workerCtor(workerPath);
-  }
-
-  getConcurrency(): number {
-    return 2;
-  }
-}
-
 export class BrowserCryptoWorkerFactory implements CryptoWorkerFactory {
   startWorker(): CryptoWorker {
     const workerCtor = Worker;
@@ -142,24 +120,6 @@ export class BrowserCryptoWorkerFactory implements 
CryptoWorkerFactory {
 }
 
 /**
- * The synchronous crypto worker produced by this factory doesn't run in the
- * background, but actually blocks the caller until the operation is done.
- */
-export class SynchronousCryptoWorkerFactory implements CryptoWorkerFactory {
-  startWorker(): CryptoWorker {
-    if (typeof require === "undefined") {
-      throw Error("cannot make worker, require(...) not defined");
-    }
-    const workerCtor = require("./synchronousWorker").SynchronousCryptoWorker;
-    return new workerCtor();
-  }
-
-  getConcurrency(): number {
-    return 1;
-  }
-}
-
-/**
  * Crypto API that interfaces manages a background crypto thread
  * for the execution of expensive operations.
  */
diff --git a/src/crypto/cryptoWorker.ts b/src/crypto/cryptoWorker.ts
index 11e3d964..0ea641dd 100644
--- a/src/crypto/cryptoWorker.ts
+++ b/src/crypto/cryptoWorker.ts
@@ -1,77 +1,8 @@
-/*
- This file is part of TALER
- (C) 2016 GNUnet e.V.
+export interface CryptoWorker {
+  postMessage(message: any): void;
 
- 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.
+  terminate(): void;
 
- 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
- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Web worker for crypto operations.
- */
-
-/**
- * Imports.
- */
-
-import * as emscLoader from "./emscLoader";
-
-import { CryptoImplementation } from "./cryptoImplementation";
-import { EmscEnvironment } from "./emscInterface";
-
-const worker: Worker = (self as any) as Worker;
-
-let impl: CryptoImplementation | undefined;
-
-
-worker.onmessage = (msg: MessageEvent) => {
-  const args = msg.data.args;
-  if (!Array.isArray(args)) {
-    console.error("args must be array");
-    return;
-  }
-  const id = msg.data.id;
-  if (typeof id !== "number") {
-    console.error("RPC id must be number");
-    return;
-  }
-  const operation = msg.data.operation;
-  if (typeof operation !== "string") {
-    console.error("RPC operation must be string");
-    return;
-  }
-
-  if (CryptoImplementation.enableTracing) {
-    console.log("onmessage with", operation);
-  }
-
-  emscLoader.getLib().then(p => {
-    const lib = p.lib;
-    const emsc = new EmscEnvironment(lib);
-    const impl = new CryptoImplementation(emsc);
-
-    if (!(operation in impl)) {
-      console.error(`unknown operation: '${operation}'`);
-      return;
-    }
-
-    if (CryptoImplementation.enableTracing) {
-      console.log("about to execute", operation);
-    }
-
-    const result = (impl as any)[operation](...args);
-
-    if (CryptoImplementation.enableTracing) {
-      console.log("finished executing", operation);
-    }
-    worker.postMessage({ result, id });
-  });
-};
+  onmessage: (m: any) => void;
+  onerror: (m: any) => void;
+}
\ No newline at end of file
diff --git a/src/crypto/emscInterface-test.ts b/src/crypto/emscInterface-test.ts
index 51f2d58b..b1a45cbc 100644
--- a/src/crypto/emscInterface-test.ts
+++ b/src/crypto/emscInterface-test.ts
@@ -17,13 +17,13 @@
 // tslint:disable:max-line-length
 
 import test from "ava";
-import * as emscLoader from "./emscLoader";
+import { NodeEmscriptenLoader } from "./nodeEmscriptenLoader";
 import * as native from "./emscInterface";
 
 
 test("string hashing", async (t) => {
-  const { lib } = await emscLoader.getLib();
-  const emsc = new native.EmscEnvironment(lib);
+  const loader =  new NodeEmscriptenLoader();
+  const emsc = await loader.getEmscriptenEnvironment();
 
   const x = native.ByteArray.fromStringWithNull(emsc, "hello taler");
   const h = 
"8RDMADB3YNF3QZBS3V467YZVJAMC2QAQX0TZGVZ6Q5PFRRAJFT70HHN0QF661QR9QWKYMMC7YEMPD679D2RADXCYK8Y669A2A5MKQFR";
@@ -35,8 +35,8 @@ test("string hashing", async (t) => {
 
 
 test("signing", async (t) => {
-  const { lib } = await emscLoader.getLib();
-  const emsc = new native.EmscEnvironment(lib);
+  const loader =  new NodeEmscriptenLoader();
+  const emsc = await loader.getEmscriptenEnvironment();
 
   const x = native.ByteArray.fromStringWithNull(emsc, "hello taler");
   const priv = native.EddsaPrivateKey.create(emsc, );
@@ -49,8 +49,8 @@ test("signing", async (t) => {
 
 
 test("signing-fixed-data", async (t) => {
-  const { lib } = await emscLoader.getLib();
-  const emsc = new native.EmscEnvironment(lib);
+  const loader =  new NodeEmscriptenLoader();
+  const emsc = await loader.getEmscriptenEnvironment();
 
   const x = native.ByteArray.fromStringWithNull(emsc, "hello taler");
   const purpose = new native.EccSignaturePurpose(emsc, 
native.SignaturePurpose.TEST, x);
@@ -74,8 +74,8 @@ const denomPubStr1 = 
"51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30G9R64VK6HHS6M
 
 
 test("rsa-encode", async (t) => {
-  const { lib } = await emscLoader.getLib();
-  const emsc = new native.EmscEnvironment(lib);
+  const loader =  new NodeEmscriptenLoader();
+  const emsc = await loader.getEmscriptenEnvironment();
 
   const pubHashStr = 
"JM63YM5X7X547164QJ3MGJZ4WDD47GEQR5DW5SH35G4JFZXEJBHE5JBNZM5K8XN5C4BRW25BE6GSVAYBF790G2BZZ13VW91D41S4DS0";
   const denomPub = native.RsaPublicKey.fromCrock(emsc, denomPubStr1);
@@ -86,8 +86,8 @@ test("rsa-encode", async (t) => {
 
 
 test("withdraw-request", async (t) => {
-  const { lib } = await emscLoader.getLib();
-  const emsc = new native.EmscEnvironment(lib);
+  const loader =  new NodeEmscriptenLoader();
+  const emsc = await loader.getEmscriptenEnvironment();
 
   const reservePrivStr = 
"G9R8KRRCAFKPD0KW7PW48CC2T03VQ8K2AN9J6J6K2YW27J5MHN90";
   const reservePriv = native.EddsaPrivateKey.fromCrock(emsc, reservePrivStr);
@@ -117,9 +117,8 @@ test("withdraw-request", async (t) => {
 
 
 test("currency-conversion", async (t) => {
-
-  const { lib } = await emscLoader.getLib();
-  const emsc = new native.EmscEnvironment(lib);
+  const loader =  new NodeEmscriptenLoader();
+  const emsc = await loader.getEmscriptenEnvironment();
 
   const a1 = new native.Amount(emsc, {currency: "KUDOS", value: 1, fraction: 
50000000});
   const a2 = new native.Amount(emsc, {currency: "KUDOS", value: 1, fraction: 
50000000});
@@ -133,8 +132,8 @@ test("currency-conversion", async (t) => {
 
 
 test("ecdsa", async (t) => {
-  const { lib } = await emscLoader.getLib();
-  const emsc = new native.EmscEnvironment(lib);
+  const loader =  new NodeEmscriptenLoader();
+  const emsc = await loader.getEmscriptenEnvironment();
 
   const priv = native.EcdsaPrivateKey.create(emsc);
   const pub1 = priv.getPublicKey();
@@ -145,8 +144,8 @@ test("ecdsa", async (t) => {
 
 
 test("ecdhe", async (t) => {
-  const { lib } = await emscLoader.getLib();
-  const emsc = new native.EmscEnvironment(lib);
+  const loader =  new NodeEmscriptenLoader();
+  const emsc = await loader.getEmscriptenEnvironment();
 
   const priv = native.EcdhePrivateKey.create(emsc);
   const pub = priv.getPublicKey();
diff --git a/src/crypto/emscInterface.ts b/src/crypto/emscInterface.ts
index 96e94e3f..0c7edefa 100644
--- a/src/crypto/emscInterface.ts
+++ b/src/crypto/emscInterface.ts
@@ -28,8 +28,6 @@
  */
 import { AmountJson } from "../amounts";
 
-import { EmscFunGen, EmscLib } from "./emscLoader";
-
 /**
  * Size of a native pointer.  Must match the size
  * use when compiling via emscripten.
@@ -38,6 +36,53 @@ const PTR_SIZE = 4;
 
 const GNUNET_OK = 1;
 
+
+/**
+ * Signature of the function that retrieves emscripten
+ * function implementations.
+ */
+export interface EmscFunGen {
+  (name: string,
+   ret: string,
+   args: string[]): ((...x: Array<number|string>) => any);
+  (name: string,
+   ret: "number",
+   args: string[]): ((...x: Array<number|string>) => number);
+  (name: string,
+   ret: "void",
+   args: string[]): ((...x: Array<number|string>) => void);
+  (name: string,
+   ret: "string",
+   args: string[]): ((...x: Array<number|string>) => string);
+}
+
+
+interface EmscLib {
+  cwrap: EmscFunGen;
+
+  ccall(name: string, ret: "number"|"string", argTypes: any[], args: any[]): 
any;
+
+  stringToUTF8(s: string, addr: number, maxLength: number): void;
+
+  onRuntimeInitialized(f: () => void): void;
+
+  readBinary?: (filename: string) => Promise<ArrayBuffer>;
+
+  calledRun?: boolean;
+
+  _free(ptr: number): void;
+
+  _malloc(n: number): number;
+
+  Pointer_stringify(p: number, len?: number): string;
+
+  getValue(ptr: number, type: string, noSafe?: boolean): number;
+
+  setValue(ptr: number, value: number, type: string, noSafe?: boolean): void;
+
+  writeStringToMemory(s: string, buffer: number, dontAddNull?: boolean): void;
+}
+
 interface EmscFunctions {
   amount_add(a1: number, a2: number, a3: number): number;
   amount_cmp(a1: number, a2: number): number;
diff --git a/src/crypto/emscLoader.d.ts b/src/crypto/emscLoader.d.ts
deleted file mode 100644
index 3ec4f4cf..00000000
--- a/src/crypto/emscLoader.d.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- This file is part of TALER
- (C) 2016 GNUnet e.V.
-
- 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.
-
- 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
- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-
-declare function getLib(): Promise<{ lib: EmscLib }>;
-
-/**
- * Signature of the function that retrieves emscripten
- * function implementations.
- */
-export interface EmscFunGen {
-  (name: string,
-   ret: string,
-   args: string[]): ((...x: Array<number|string>) => any);
-  (name: string,
-   ret: "number",
-   args: string[]): ((...x: Array<number|string>) => number);
-  (name: string,
-   ret: "void",
-   args: string[]): ((...x: Array<number|string>) => void);
-  (name: string,
-   ret: "string",
-   args: string[]): ((...x: Array<number|string>) => string);
-}
-
-
-interface EmscLib {
-  cwrap: EmscFunGen;
-
-  ccall(name: string, ret: "number"|"string", argTypes: any[], args: any[]): 
any;
-
-  stringToUTF8(s: string, addr: number, maxLength: number): void;
-
-  onRuntimeInitialized(f: () => void): void;
-
-  readBinary?: (filename: string) => Promise<ArrayBuffer>;
-
-  calledRun?: boolean;
-
-  _free(ptr: number): void;
-
-  _malloc(n: number): number;
-
-  Pointer_stringify(p: number, len?: number): string;
-
-  getValue(ptr: number, type: string, noSafe?: boolean): number;
-
-  setValue(ptr: number, value: number, type: string, noSafe?: boolean): void;
-
-  writeStringToMemory(s: string, buffer: number, dontAddNull?: boolean): void;
-}
diff --git a/src/crypto/emscLoader.js b/src/crypto/emscLoader.js
deleted file mode 100644
index 25dc6b85..00000000
--- a/src/crypto/emscLoader.js
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- This file is part of TALER
- (C) 2017 Inria and GNUnet e.V.
-
- 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.
-
- 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
- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-
-// @ts-nocheck
-
-
-/**
- * This module loads the emscripten library, and is written in unchecked
- * JavaScript since it needs to do environment detection and dynamically select
- * the right way to load the library.
- */
-
-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() {
-  enableTracing && console.log("in getLib");
-  if (cachedLib) {
-    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;
-    const g = global;
-    // unavoidable hack, so that emscripten detects
-    // the environment as node even though importScripts
-    // is present.
-    const savedImportScripts = g.importScripts;
-    delete g.importScripts;
-    // Assume that the code is run from the build/ directory.
-    const libFn = 
indirectRequire("../../../emscripten/taler-emscripten-lib.js");
-    const lib = libFn();
-    g.importScripts = savedImportScripts;
-    if (lib) {
-      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"); 
-    }
-  }
-
-  if (typeof importScripts !== "undefined") {
-    console.log("trying to load emscripten lib with 'importScripts'");
-    self.TalerEmscriptenLib = {};
-    importScripts('/emscripten/taler-emscripten-lib.js')
-    if (!self.TalerEmscriptenLib) {
-      throw Error("can't import taler emscripten lib");
-    }
-    const locateFile = (path, scriptDir) => {
-      console.log("locating file", "path", path, "scriptDir", scriptDir);
-      // This is quite hacky and assumes that our scriptDir is dist/
-      return scriptDir + "../emscripten/" + path;
-    };
-    console.log("instantiating TalerEmscriptenLib");
-    const lib = self.TalerEmscriptenLib({ locateFile });
-    cachedLib = lib;
-    return new Promise((resolve, reject) => {
-      lib.then(mod => {
-        console.log("emscripten module fully loaded");
-        resolve({ lib: mod });
-      });
-    });
-  }
-
-  // Last resort, we don't have require, we're not running in a webworker.
-  // Maybe we're on a normal browser page, in this case TalerEmscriptenLib
-  // must be included in a script tag on the page.
-
-  if (typeof window !== "undefined") {
-    if (window.TalerEmscriptenLib) {
-      return Promise.resolve(TalerEmscriptenLib);
-    }
-    throw Error("Looks like running in browser, but TalerEmscriptenLib is not 
defined");
-  }
-  throw Error("Running in unsupported environment");
-}
diff --git a/src/crypto/nodeEmscriptenLoader.ts 
b/src/crypto/nodeEmscriptenLoader.ts
new file mode 100644
index 00000000..6d6a7306
--- /dev/null
+++ b/src/crypto/nodeEmscriptenLoader.ts
@@ -0,0 +1,102 @@
+
+import { EmscEnvironment } from "./emscInterface";
+import { CryptoImplementation } from "./cryptoImplementation";
+
+import fs = require("fs");
+
+export class NodeEmscriptenLoader {
+  private cachedEmscEnvironment: EmscEnvironment | undefined = undefined;
+  private cachedEmscEnvironmentPromise:
+    | Promise<EmscEnvironment>
+    | undefined = undefined;
+
+    private async getWasmBinary(): Promise<Uint8Array> {
+      // @ts-ignore
+      const akonoGetData = global.__akono_getData;
+      if (akonoGetData) {
+        // We're running embedded node on Android
+        console.log("reading wasm binary from akono");
+        const data = akonoGetData("taler-emscripten-lib.wasm");
+        // The data we get is base64-encoded binary data
+        let buf = new Buffer(data, 'base64');
+        return new Uint8Array(buf);
+  
+      } else {
+        // We're in a normal node environment
+        const binaryPath = __dirname + 
"/../../../emscripten/taler-emscripten-lib.wasm";
+        console.log("reading from", binaryPath);
+        const wasmBinary = new Uint8Array(fs.readFileSync(binaryPath));
+        return wasmBinary;
+      }
+    }
+  
+    async getEmscriptenEnvironment(): Promise<EmscEnvironment> {
+      if (this.cachedEmscEnvironment) {
+        return this.cachedEmscEnvironment;
+      }
+  
+      if (this.cachedEmscEnvironmentPromise) {
+        return this.cachedEmscEnvironmentPromise;
+      }
+  
+      let lib: any;
+  
+      const wasmBinary = await this.getWasmBinary();
+  
+      return new Promise((resolve, reject) => {
+        // Arguments passed to the emscripten prelude
+        const libArgs = {
+          wasmBinary,
+          onRuntimeInitialized: () => {
+            if (!lib) {
+              console.error("fatal emscripten initialization error");
+              return;
+            }
+            this.cachedEmscEnvironmentPromise = undefined;
+            this.cachedEmscEnvironment = new EmscEnvironment(lib);
+            resolve(this.cachedEmscEnvironment);
+          },
+        };
+  
+        // Make sure that TypeScript doesn't try
+        // to check the taler-emscripten-lib.
+        const indirectRequire = require;
+  
+        const g = global;
+  
+        // unavoidable hack, so that emscripten detects
+        // the environment as node even though importScripts
+        // is present.
+  
+        // @ts-ignore
+        const savedImportScripts = g.importScripts;
+        // @ts-ignore
+        delete g.importScripts;
+        // @ts-ignore
+        const savedCrypto = g.crypto;
+        // @ts-ignore
+        delete g.crypto;
+  
+        // Assume that the code is run from the build/ directory.
+        const libFn = indirectRequire(
+          "../../../emscripten/taler-emscripten-lib.js",
+        );
+        lib = libFn(libArgs);
+  
+        // @ts-ignore
+        g.importScripts = savedImportScripts;
+        // @ts-ignore
+        g.crypto = savedCrypto;
+  
+        if (!lib) {
+          throw Error("could not load taler-emscripten-lib.js");
+        }
+  
+        if (!lib.ccall) {
+          throw Error(
+            "sanity check failed: taler-emscripten lib does not have 'ccall'",
+          );
+        }
+      });
+    }
+}
diff --git a/src/crypto/nodeProcessWorker.ts b/src/crypto/nodeProcessWorker.ts
index b5a2e8b4..c5d0f2e7 100644
--- a/src/crypto/nodeProcessWorker.ts
+++ b/src/crypto/nodeProcessWorker.ts
@@ -1,3 +1,5 @@
+import { CryptoWorkerFactory } from "./cryptoApi";
+
 /*
  This file is part of TALER
  (C) 2016 GNUnet e.V.
@@ -17,11 +19,29 @@
 
 // tslint:disable:no-var-requires
 
-const path = require("path");
-const fork = require("child_process").fork;
+import { CryptoWorker } from "./cryptoWorker";
+
+import path = require("path");
+import child_process = require("child_process");
 
 const nodeWorkerEntry = path.join(__dirname, "nodeWorkerEntry.js");
 
+
+export class NodeCryptoWorkerFactory implements CryptoWorkerFactory {
+  startWorker(): CryptoWorker {
+    if (typeof require === "undefined") {
+      throw Error("cannot make worker, require(...) not defined");
+    }
+    const workerCtor = require("./nodeProcessWorker").Worker;
+    const workerPath = __dirname + "/cryptoWorker.js";
+    return new workerCtor(workerPath);
+  }
+
+  getConcurrency(): number {
+    return 4;
+  }
+}
+
 /**
  * Worker implementation that uses node subprocesses.
  */
@@ -38,33 +58,35 @@ export class Worker {
    */
   onerror: undefined | ((m: any) => void);
 
-  constructor(scriptFilename: string) {
-    this.child = fork(nodeWorkerEntry);
+  private dispatchMessage(msg: any) {
+    if (this.onmessage) {
+      this.onmessage({ data: msg });
+    } else {
+      console.warn("no handler for worker event 'message' defined")
+    }
+  }
+
+  private dispatchError(msg: any) {
+    if (this.onerror) {
+      this.onerror({ data: msg });
+    } else {
+      console.warn("no handler for worker event 'error' defined")
+    }
+  }
+
+  constructor() {
+    this.child = child_process.fork(nodeWorkerEntry);
     this.onerror = undefined;
     this.onmessage = undefined;
 
     this.child.on("error", (e: any) => {
-      if (this.onerror) {
-        this.onerror(e);
-      }
+      this.dispatchError(e);
     });
 
     this.child.on("message", (msg: any) => {
-      const message = JSON.parse(msg);
-
-      if (!message.error && this.onmessage) {
-        this.onmessage(message);
-      }
-
-      if (message.error && this.onerror) {
-        const error = new Error(message.error);
-        error.stack = message.stack;
-
-        this.onerror(error);
-      }
+      console.log("nodeProcessWorker got child message", msg);
+      this.dispatchMessage(msg);
     });
-
-    this.child.send({scriptFilename, cwd: process.cwd()});
   }
 
   /**
diff --git a/src/crypto/nodeWorkerEntry.ts b/src/crypto/nodeWorkerEntry.ts
index 9e2647ff..1ed16f87 100644
--- a/src/crypto/nodeWorkerEntry.ts
+++ b/src/crypto/nodeWorkerEntry.ts
@@ -17,61 +17,64 @@
 
 // tslint:disable:no-var-requires
 
-const fs = require("fs");
-const vm = require("vm");
-
-process.once("message", (obj: any) => {
-  const g: any = global as any;
-
-  (g as any).self = {
-    addEventListener: (event: "error" | "message", fn: (x: any) => void) => {
-      if (event === "error") {
-        g.onerror = fn;
-      } else if (event === "message") {
-        g.onmessage = fn;
-      }
-    },
-    close: () => {
-      process.exit(0);
-    },
-    onerror: (err: any) => {
-      const str: string = JSON.stringify({error: err.message, stack: 
err.stack});
-      if (process.send) {
-        process.send(str);
-      }
-    },
-    onmessage: undefined,
-    postMessage: (msg: any) => {
-      const str: string = JSON.stringify({data: msg});
-      if (process.send) {
-        process.send(str);
-      }
-    },
-  };
-
-  g.__dirname = obj.cwd;
-  g.__filename = __filename;
-  g.importScripts = (...files: string[]) => {
-    if (files.length > 0) {
-      vm.createScript(files.map((file) => fs.readFileSync(file, 
"utf8")).join("\n")).runInThisContext();
-    }
-  };
+import fs = require("fs");
+import vm = require("vm");
+import { NodeEmscriptenLoader } from "./nodeEmscriptenLoader";
+import { CryptoImplementation } from "./cryptoImplementation";
 
-  Object.keys(g.self).forEach((key) => {
-    g[key] = g.self[key];
-  });
+const loader = new NodeEmscriptenLoader();
+
+async function handleRequest(operation: string, id: number, args: string[]) {
+  let emsc = await loader.getEmscriptenEnvironment();
 
-  process.on("message", (msg: any) => {
-    try {
-      (g.onmessage || g.self.onmessage || (() => undefined))(JSON.parse(msg));
-    } catch (err) {
-      (g.onerror || g.self.onerror || (() => undefined))(err);
+  const impl = new CryptoImplementation(emsc);
+
+  if (!(operation in impl)) {
+    console.error(`crypto operation '${operation}' not found`);
+    return;
+  }
+
+  try {
+    const result = (impl as any)[operation](...args);
+    if (process.send) {
+      process.send({ result, id });
+    } else {
+      console.error("process.send not available");
     }
-  });
+  } catch (e) {
+    console.log("error during operation", e);
+    return;
+  }
+}
 
-  process.on("uncaughtException", (err: any) => {
-    (g.onerror || g.self.onerror || (() => undefined))(err);
+process.on("message", (msgStr: any) => {
+  console.log("got message in node worker entry", msgStr);
+
+  console.log("typeof msg", typeof msgStr);
+
+  const msg = JSON.parse(msgStr);
+
+  const args = msg.data.args;
+  if (!Array.isArray(args)) {
+    console.error("args must be array");
+    return;
+  }
+  const id = msg.data.id;
+  if (typeof id !== "number") {
+    console.error("RPC id must be number");
+    return;
+  }
+  const operation = msg.data.operation;
+  if (typeof operation !== "string") {
+    console.error("RPC operation must be string");
+    return;
+  }
+
+  handleRequest(operation, id, args).catch((e) => {
+    console.error("error in node worker", e);
   });
+});
 
-  require(obj.scriptFilename);
+process.on("uncaughtException", (err: any) => {
+  console.log("uncaught exception in node worker entry", err);
 });
diff --git a/src/crypto/synchronousWorker.ts b/src/crypto/synchronousWorker.ts
index 7d115f1d..4369612a 100644
--- a/src/crypto/synchronousWorker.ts
+++ b/src/crypto/synchronousWorker.ts
@@ -14,19 +14,37 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { EmscEnvironment } from "./emscInterface";
 import { CryptoImplementation } from "./cryptoImplementation";
 
+import { NodeEmscriptenLoader } from "./nodeEmscriptenLoader";
+
 import fs = require("fs");
+import { CryptoWorkerFactory } from "./cryptoApi";
+import { CryptoWorker } from "./cryptoWorker";
+
+/**
+ * The synchronous crypto worker produced by this factory doesn't run in the
+ * background, but actually blocks the caller until the operation is done.
+ */
+export class SynchronousCryptoWorkerFactory implements CryptoWorkerFactory {
+  startWorker(): CryptoWorker {
+    if (typeof require === "undefined") {
+      throw Error("cannot make worker, require(...) not defined");
+    }
+    const workerCtor = require("./synchronousWorker").SynchronousCryptoWorker;
+    return new workerCtor();
+  }
+
+  getConcurrency(): number {
+    return 1;
+  }
+}
+
 
 /**
  * Worker implementation that uses node subprocesses.
  */
 export class SynchronousCryptoWorker {
-  private cachedEmscEnvironment: EmscEnvironment | undefined = undefined;
-  private cachedEmscEnvironmentPromise:
-    | Promise<EmscEnvironment>
-    | undefined = undefined;
 
   /**
    * Function to be called when we receive a message from the worker thread.
@@ -38,6 +56,8 @@ export class SynchronousCryptoWorker {
    */
   onerror: undefined | ((m: any) => void);
 
+  private emscriptenLoader = new NodeEmscriptenLoader();
+
   constructor() {
     this.onerror = undefined;
     this.onmessage = undefined;
@@ -57,96 +77,6 @@ export class SynchronousCryptoWorker {
     }
   }
 
-  private async getWasmBinary(): Promise<Uint8Array> {
-    // @ts-ignore
-    const akonoGetData = global.__akono_getData;
-    if (akonoGetData) {
-      // We're running embedded node on Android
-      console.log("reading wasm binary from akono");
-      const data = akonoGetData("taler-emscripten-lib.wasm");
-      // The data we get is base64-encoded binary data
-      let buf = new Buffer(data, 'base64');
-      return new Uint8Array(buf);
-
-    } else {
-      // We're in a normal node environment
-      const binaryPath = __dirname + 
"/../../../emscripten/taler-emscripten-lib.wasm";
-      console.log("reading from", binaryPath);
-      const wasmBinary = new Uint8Array(fs.readFileSync(binaryPath));
-      return wasmBinary;
-    }
-  }
-
-  private async getEmscriptenEnvironment(): Promise<EmscEnvironment> {
-    if (this.cachedEmscEnvironment) {
-      return this.cachedEmscEnvironment;
-    }
-
-    if (this.cachedEmscEnvironmentPromise) {
-      return this.cachedEmscEnvironmentPromise;
-    }
-
-    let lib: any;
-
-    const wasmBinary = await this.getWasmBinary();
-
-    return new Promise((resolve, reject) => {
-      // Arguments passed to the emscripten prelude
-      const libArgs = {
-        wasmBinary,
-        onRuntimeInitialized: () => {
-          if (!lib) {
-            console.error("fatal emscripten initialization error");
-            return;
-          }
-          this.cachedEmscEnvironmentPromise = undefined;
-          this.cachedEmscEnvironment = new EmscEnvironment(lib);
-          resolve(this.cachedEmscEnvironment);
-        },
-      };
-
-      // Make sure that TypeScript doesn't try
-      // to check the taler-emscripten-lib.
-      const indirectRequire = require;
-
-      const g = global;
-
-      // unavoidable hack, so that emscripten detects
-      // the environment as node even though importScripts
-      // is present.
-
-      // @ts-ignore
-      const savedImportScripts = g.importScripts;
-      // @ts-ignore
-      delete g.importScripts;
-      // @ts-ignore
-      const savedCrypto = g.crypto;
-      // @ts-ignore
-      delete g.crypto;
-
-      // Assume that the code is run from the build/ directory.
-      const libFn = indirectRequire(
-        "../../../emscripten/taler-emscripten-lib.js",
-      );
-      lib = libFn(libArgs);
-
-      // @ts-ignore
-      g.importScripts = savedImportScripts;
-      // @ts-ignore
-      g.crypto = savedCrypto;
-
-      if (!lib) {
-        throw Error("could not load taler-emscripten-lib.js");
-      }
-
-      if (!lib.ccall) {
-        throw Error(
-          "sanity check failed: taler-emscripten lib does not have 'ccall'",
-        );
-      }
-    });
-  }
-
   private dispatchMessage(msg: any) {
     if (this.onmessage) {
       this.onmessage({ data: msg });
@@ -154,7 +84,7 @@ export class SynchronousCryptoWorker {
   }
 
   private async handleRequest(operation: string, id: number, args: string[]) {
-    let emsc = await this.getEmscriptenEnvironment();
+    let emsc = await this.emscriptenLoader.getEmscriptenEnvironment();
 
     const impl = new CryptoImplementation(emsc);
 
diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts
index 1e501cdc..49cc608d 100644
--- a/src/headless/taler-wallet-cli.ts
+++ b/src/headless/taler-wallet-cli.ts
@@ -26,7 +26,7 @@ import URI = require("urijs");
 
 import querystring = require("querystring");
 import { CheckPaymentResponse } from "../talerTypes";
-import { NodeCryptoWorkerFactory, SynchronousCryptoWorkerFactory } from 
"../crypto/cryptoApi";
+import { SynchronousCryptoWorkerFactory } from "../crypto/synchronousWorker";
 
 const enableTracing = false;
 
diff --git a/tsconfig.json b/tsconfig.json
index 8104054a..c833ef9a 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -27,14 +27,14 @@
     "decl/urijs.d.ts",
     "src/amounts.ts",
     "src/checkable.ts",
+    "src/crypto/browserWorkerEntry.ts",
     "src/crypto/cryptoApi-test.ts",
     "src/crypto/cryptoApi.ts",
     "src/crypto/cryptoImplementation.ts",
     "src/crypto/cryptoWorker.ts",
     "src/crypto/emscInterface-test.ts",
     "src/crypto/emscInterface.ts",
-    "src/crypto/emscLoader.d.ts",
-    "src/crypto/emscLoader.js",
+    "src/crypto/nodeEmscriptenLoader.ts",
     "src/crypto/nodeProcessWorker.ts",
     "src/crypto/nodeWorkerEntry.ts",
     "src/crypto/synchronousWorker.ts",
diff --git a/webpack.config.js b/webpack.config.js
index 845b56fe..55014618 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -27,8 +27,7 @@ module.exports = function (env) {
         {
           test: /\.tsx?$/,
           loader: 'awesome-typescript-loader',
-          exclude: /node_modules/,
-          exclude: /taler-emscripten-lib/,
+          exclude: 
/node_modules|taler-emscripten-lib|nodeEmscriptenLoader|synchronousWorker/,
         }
       ]
     },
@@ -58,7 +57,7 @@ module.exports = function (env) {
     }
   }
   const configWebWorker = {
-    entry: {"cryptoWorker": "./src/crypto/cryptoWorker.ts"},
+    entry: {"cryptoWorker": "./src/crypto/browserWorkerEntry.ts"},
     target: "webworker",
     name: "webworker",
   };

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]