gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-wallet-webex] branch master updated (88484a58 -> ea2


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] branch master updated (88484a58 -> ea2fb677)
Date: Thu, 15 Aug 2019 23:34:16 +0200

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

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

    from 88484a58 npmignore
     new 05e32fd8 npmignore / package.json
     new 61bc36b9 use factory instead of startWorker
     new 78f885db missing structuredClone caused unintended mutations
     new ea2fb677 worker refactoring / sync worker

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .npmignore                                         |   1 +
 package.json                                       |   8 +-
 packages/idb-bridge/package.json                   |   2 +-
 packages/idb-bridge/src/MemoryBackend.ts           |   8 +-
 src/crypto/cryptoApi-test.ts                       |   8 +-
 src/crypto/cryptoApi.ts                            | 112 +++-
 .../{cryptoWorker.ts => cryptoImplementation.ts}   | 342 +++++-----
 src/crypto/cryptoWorker.ts                         | 741 +--------------------
 src/crypto/emscInterface-test.ts                   |  83 ++-
 src/crypto/emscInterface.ts                        | 402 ++++++-----
 src/crypto/{nodeWorker.ts => nodeProcessWorker.ts} |   0
 src/crypto/startWorker.js                          |  41 --
 src/crypto/synchronousWorker.ts                    | 162 +++++
 src/headless/taler-wallet-cli.ts                   |   4 +-
 src/wallet.ts                                      |  27 +-
 src/webex/wxBackend.ts                             |   3 +-
 tsconfig.json                                      |   5 +-
 yarn.lock                                          |   8 +-
 18 files changed, 724 insertions(+), 1233 deletions(-)
 copy src/crypto/{cryptoWorker.ts => cryptoImplementation.ts} (67%)
 rename src/crypto/{nodeWorker.ts => nodeProcessWorker.ts} (100%)
 delete mode 100644 src/crypto/startWorker.js
 create mode 100644 src/crypto/synchronousWorker.ts

diff --git a/.npmignore b/.npmignore
index 66b1e73c..169a8752 100644
--- a/.npmignore
+++ b/.npmignore
@@ -12,6 +12,7 @@
 /build
 
 /Makefile
+/*.js
 
 /dist/*
 !/dist/node
diff --git a/package.json b/package.json
index 28711f70..d72316e7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "taler-wallet",
-  "version": "0.0.1",
+  "version": "0.0.2",
   "description": "",
   "main": "dist/node/index.js",
   "repository": {
@@ -38,7 +38,6 @@
     "typedoc": "^0.14.2",
     "typescript": "^3.3.4000",
     "uglify-js": "^3.0.27",
-    "urijs": "^1.18.10",
     "vinyl": "^2.2.0",
     "vinyl-fs": "^3.0.3",
     "webpack": "^4.29.6",
@@ -49,7 +48,8 @@
   "dependencies": {
     "axios": "^0.19.0",
     "commander": "^2.20.0",
-    "idb-bridge": "^0.0.1",
-    "source-map-support": "^0.5.12"
+    "idb-bridge": "0.0.2",
+    "source-map-support": "^0.5.12",
+    "urijs": "^1.18.10"
   }
 }
diff --git a/packages/idb-bridge/package.json b/packages/idb-bridge/package.json
index 940f1747..e69341c3 100644
--- a/packages/idb-bridge/package.json
+++ b/packages/idb-bridge/package.json
@@ -1,6 +1,6 @@
 {
   "name": "idb-bridge",
-  "version": "0.0.1",
+  "version": "0.0.2",
   "description": "IndexedDB implementation that uses SQLite3 as storage",
   "main": "./build/index.js",
   "types": "./build/index.d.ts",
diff --git a/packages/idb-bridge/src/MemoryBackend.ts 
b/packages/idb-bridge/src/MemoryBackend.ts
index 9cdfd8a0..d14d63a7 100644
--- a/packages/idb-bridge/src/MemoryBackend.ts
+++ b/packages/idb-bridge/src/MemoryBackend.ts
@@ -1027,7 +1027,7 @@ export class MemoryBackend implements Backend {
           if (!result) {
             throw Error("invariant violated");
           }
-          values.push(result.value);
+          values.push(structuredClone(result.value));
         }
       }
     } else {
@@ -1086,7 +1086,7 @@ export class MemoryBackend implements Backend {
         }
 
         if (req.resultLevel >= ResultLevel.Full) {
-          values.push(res.value);
+          values.push(structuredClone(res.value));
         }
 
         numResults++;
@@ -1168,8 +1168,8 @@ export class MemoryBackend implements Backend {
     }
 
     const objectStoreRecord: ObjectStoreRecord = {
-      primaryKey: key,
-      value: value,
+      primaryKey: structuredClone(key),
+      value: structuredClone(value),
     };
 
     objectStore.modifiedData = modifiedData.with(key, objectStoreRecord, true);
diff --git a/src/crypto/cryptoApi-test.ts b/src/crypto/cryptoApi-test.ts
index 6d43e2e6..9c592412 100644
--- a/src/crypto/cryptoApi-test.ts
+++ b/src/crypto/cryptoApi-test.ts
@@ -24,7 +24,7 @@ import {
   ReserveRecord,
 } from "../dbTypes";
 
-import { CryptoApi } from "./cryptoApi";
+import { CryptoApi, NodeCryptoWorkerFactory } from "./cryptoApi";
 
 const masterPub1: string =
   "CQQZ9DY3MZ1ARMN5K1VKDETS04Y2QCKMMCFHZSWJWWVN82BTTH00";
@@ -73,7 +73,7 @@ const denomInvalid1 = JSON.parse(JSON.stringify(denomValid1));
 denomInvalid1.value.value += 1;
 
 test("string hashing", async t => {
-  const crypto = new CryptoApi();
+  const crypto = new CryptoApi(new NodeCryptoWorkerFactory());
   const s = await crypto.hashString("hello taler");
   const sh =
     
"8RDMADB3YNF3QZBS3V467YZVJAMC2QAQX0TZGVZ6Q5PFRRAJFT70HHN0QF661QR9QWKYMMC7YEMPD679D2RADXCYK8Y669A2A5MKQFR";
@@ -82,7 +82,7 @@ test("string hashing", async t => {
 });
 
 test("precoin creation", async t => {
-  const crypto = new CryptoApi();
+  const crypto = new CryptoApi(new NodeCryptoWorkerFactory());
   const { priv, pub } = await crypto.createEddsaKeypair();
   const r: ReserveRecord = {
     created: 0,
@@ -103,7 +103,7 @@ test("precoin creation", async t => {
 });
 
 test("denom validation", async t => {
-  const crypto = new CryptoApi();
+  const crypto = new CryptoApi(new NodeCryptoWorkerFactory());
   let v: boolean;
   v = await crypto.isValidDenom(denomValid1, masterPub1);
   t.true(v);
diff --git a/src/crypto/cryptoApi.ts b/src/crypto/cryptoApi.ts
index 5e323783..c68b1070 100644
--- a/src/crypto/cryptoApi.ts
+++ b/src/crypto/cryptoApi.ts
@@ -40,9 +40,6 @@ import { BenchmarkResult, CoinWithDenom, PayCoinInfo } from 
"../walletTypes";
 
 import * as timer from "../timer";
 
-import { startWorker } from "./startWorker";
-import { throws } from "assert";
-
 /**
  * State of a crypto worker.
  */
@@ -50,7 +47,7 @@ interface WorkerState {
   /**
    * The actual worker thread.
    */
-  w: Worker | null;
+  w: CryptoWorker | null;
 
   /**
    * Work we're currently executing or null if not busy.
@@ -86,6 +83,82 @@ 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.
+   */
+  startWorker(): CryptoWorker;
+
+  /**
+   * Query the number of workers that should be
+   * run at the same time.
+   */
+  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;
+    const workerPath = "/dist/cryptoWorker-bundle.js";
+    return new workerCtor(workerPath) as CryptoWorker;
+  }
+
+  getConcurrency(): number {
+    let concurrency = 2;
+    try {
+      // only works in the browser
+      // tslint:disable-next-line:no-string-literal
+      concurrency = (navigator as any)["hardwareConcurrency"];
+      concurrency = Math.max(1, Math.ceil(concurrency / 2));
+    } catch (e) {
+      concurrency = 2;
+    }
+    return concurrency;
+  }
+}
+
+/**
+ * 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.
@@ -94,6 +167,9 @@ export class CryptoApi {
   private nextRpcId: number = 1;
   private workers: WorkerState[];
   private workQueues: WorkItem[][];
+
+  private workerFactory: CryptoWorkerFactory;
+
   /**
    * Number of busy workers.
    */
@@ -104,7 +180,7 @@ export class CryptoApi {
    */
   private stopped: boolean = false;
 
-  public enableTracing = false;
+  public enableTracing = true;
 
   /**
    * Terminate all worker threads.
@@ -146,7 +222,7 @@ export class CryptoApi {
     ws.currentWorkItem = work;
     this.numBusy++;
     if (!ws.w) {
-      const w = startWorker();
+      const w = this.workerFactory.startWorker();
       w.onmessage = (m: MessageEvent) => this.handleWorkerMessage(ws, m);
       w.onerror = (e: ErrorEvent) => this.handleWorkerError(ws, e);
       ws.w = w;
@@ -232,24 +308,17 @@ export class CryptoApi {
       return;
     }
 
-    this.enableTracing && console.log(
-      `rpc ${currentWorkItem.operation} took ${timer.performanceNow() -
-        currentWorkItem.startTime}ms`,
-    );
+    this.enableTracing &&
+      console.log(
+        `rpc ${currentWorkItem.operation} took ${timer.performanceNow() -
+          currentWorkItem.startTime}ms`,
+      );
     currentWorkItem.resolve(msg.data.result);
   }
 
-  constructor() {
-    let concurrency = 2;
-    try {
-      // only works in the browser
-      // tslint:disable-next-line:no-string-literal
-      concurrency = (navigator as any)["hardwareConcurrency"];
-      concurrency = Math.max(1, Math.ceil(concurrency / 2));
-    } catch (e) {
-      // ignore
-    }
-    this.workers = new Array<WorkerState>(concurrency);
+  constructor(workerFactory: CryptoWorkerFactory) {
+    this.workerFactory = workerFactory;
+    this.workers = new Array<WorkerState>(workerFactory.getConcurrency());
 
     for (let i = 0; i < this.workers.length; i++) {
       this.workers[i] = {
@@ -258,6 +327,7 @@ export class CryptoApi {
         w: null,
       };
     }
+
     this.workQueues = [];
     for (let i = 0; i < NUM_PRIO; i++) {
       this.workQueues.push([]);
diff --git a/src/crypto/cryptoWorker.ts b/src/crypto/cryptoImplementation.ts
similarity index 67%
copy from src/crypto/cryptoWorker.ts
copy to src/crypto/cryptoImplementation.ts
index 5acda905..d50d4002 100644
--- a/src/crypto/cryptoWorker.ts
+++ b/src/crypto/cryptoImplementation.ts
@@ -1,6 +1,6 @@
 /*
- This file is part of TALER
- (C) 2016 GNUnet e.V.
+ This file is part of GNU Taler
+ (C) 2019 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
@@ -14,17 +14,16 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+
 /**
- * Web worker for crypto operations.
+ * Synchronous implementation of crypto-related functions for the wallet.
+ * 
+ * The functionality is parameterized over an Emscripten environment.
  */
 
 /**
  * Imports.
  */
-import * as Amounts from "../amounts";
-import { AmountJson } from "../amounts";
-
-import * as timer from "../timer";
 
 import {
   CoinRecord,
@@ -39,42 +38,35 @@ import {
 } from "../dbTypes";
 
 import { CoinPaySig, ContractTerms, PaybackRequest } from "../talerTypes";
-
 import { BenchmarkResult, CoinWithDenom, PayCoinInfo } from "../walletTypes";
-
 import { canonicalJson } from "../helpers";
-
-import * as emscLoader from "./emscLoader";
-
-import {
-  Amount,
-  EddsaPublicKey,
-  HashCode,
-  HashContext,
-  RefreshMeltCoinAffirmationPS,
-} from "./emscInterface";
+import { EmscEnvironment } from "./emscInterface";
 import * as native from "./emscInterface";
+import { AmountJson } from "../amounts";
+import * as Amounts from "../amounts";
+import * as timer from "../timer";
 
-namespace RpcFunctions {
+export class CryptoImplementation {
+  static enableTracing: boolean = false;
 
-  export let enableTracing: boolean = false;
+  constructor(private emsc: EmscEnvironment) {}
 
   /**
    * Create a pre-coin of the given denomination to be withdrawn from then 
given
    * reserve.
    */
-  export function createPreCoin(
+  createPreCoin(
     denom: DenominationRecord,
     reserve: ReserveRecord,
   ): PreCoinRecord {
-    const reservePriv = new native.EddsaPrivateKey();
+    const reservePriv = new native.EddsaPrivateKey(this.emsc);
     reservePriv.loadCrock(reserve.reserve_priv);
-    const reservePub = new native.EddsaPublicKey();
+    const reservePub = new native.EddsaPublicKey(this.emsc);
     reservePub.loadCrock(reserve.reserve_pub);
-    const denomPub = native.RsaPublicKey.fromCrock(denom.denomPub);
-    const coinPriv = native.EddsaPrivateKey.create();
+    const denomPub = native.RsaPublicKey.fromCrock(this.emsc, denom.denomPub);
+    const coinPriv = native.EddsaPrivateKey.create(this.emsc);
     const coinPub = coinPriv.getPublicKey();
-    const blindingFactor = native.RsaBlindingKeySecret.create();
+    const blindingFactor = native.RsaBlindingKeySecret.create(this.emsc);
     const pubHash: native.HashCode = coinPub.hash();
     const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
 
@@ -86,14 +78,14 @@ namespace RpcFunctions {
       throw Error("Field fee_withdraw missing");
     }
 
-    const amountWithFee = new native.Amount(denom.value);
-    amountWithFee.add(new native.Amount(denom.feeWithdraw));
-    const withdrawFee = new native.Amount(denom.feeWithdraw);
+    const amountWithFee = new native.Amount(this.emsc, denom.value);
+    amountWithFee.add(new native.Amount(this.emsc, denom.feeWithdraw));
+    const withdrawFee = new native.Amount(this.emsc, denom.feeWithdraw);
 
     const denomPubHash = denomPub.encode().hash();
 
     // Signature
-    const withdrawRequest = new native.WithdrawRequestPS({
+    const withdrawRequest = new native.WithdrawRequestPS(this.emsc, {
       amount_with_fee: amountWithFee.toNbo(),
       h_coin_envelope: ev.hash(),
       h_denomination_pub: denomPubHash,
@@ -122,11 +114,11 @@ namespace RpcFunctions {
   /**
    * Create a planchet used for tipping, including the private keys.
    */
-  export function createTipPlanchet(denom: DenominationRecord): TipPlanchet {
-    const denomPub = native.RsaPublicKey.fromCrock(denom.denomPub);
-    const coinPriv = native.EddsaPrivateKey.create();
+  createTipPlanchet(denom: DenominationRecord): TipPlanchet {
+    const denomPub = native.RsaPublicKey.fromCrock(this.emsc, denom.denomPub);
+    const coinPriv = native.EddsaPrivateKey.create(this.emsc);
     const coinPub = coinPriv.getPublicKey();
-    const blindingFactor = native.RsaBlindingKeySecret.create();
+    const blindingFactor = native.RsaBlindingKeySecret.create(this.emsc);
     const pubHash: native.HashCode = coinPub.hash();
     const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
 
@@ -156,15 +148,18 @@ namespace RpcFunctions {
   /**
    * Create and sign a message to request payback for a coin.
    */
-  export function createPaybackRequest(coin: CoinRecord): PaybackRequest {
-    const p = new native.PaybackRequestPS({
-      coin_blind: native.RsaBlindingKeySecret.fromCrock(coin.blindingKey),
-      coin_pub: native.EddsaPublicKey.fromCrock(coin.coinPub),
-      h_denom_pub: native.RsaPublicKey.fromCrock(coin.denomPub)
+  createPaybackRequest(coin: CoinRecord): PaybackRequest {
+    const p = new native.PaybackRequestPS(this.emsc, {
+      coin_blind: native.RsaBlindingKeySecret.fromCrock(
+        this.emsc,
+        coin.blindingKey,
+      ),
+      coin_pub: native.EddsaPublicKey.fromCrock(this.emsc, coin.coinPub),
+      h_denom_pub: native.RsaPublicKey.fromCrock(this.emsc, coin.denomPub)
         .encode()
         .hash(),
     });
-    const coinPriv = native.EddsaPrivateKey.fromCrock(coin.coinPriv);
+    const coinPriv = native.EddsaPrivateKey.fromCrock(this.emsc, 
coin.coinPriv);
     const coinSig = native.eddsaSign(p.toPurpose(), coinPriv);
     const paybackRequest: PaybackRequest = {
       coin_blind_key_secret: coin.blindingKey,
@@ -179,17 +174,17 @@ namespace RpcFunctions {
   /**
    * Check if a payment signature is valid.
    */
-  export function isValidPaymentSignature(
+  isValidPaymentSignature(
     sig: string,
     contractHash: string,
     merchantPub: string,
   ): boolean {
-    const p = new native.PaymentSignaturePS({
-      contract_hash: native.HashCode.fromCrock(contractHash),
+    const p = new native.PaymentSignaturePS(this.emsc, {
+      contract_hash: native.HashCode.fromCrock(this.emsc, contractHash),
     });
-    const nativeSig = new native.EddsaSignature();
+    const nativeSig = new native.EddsaSignature(this.emsc);
     nativeSig.loadCrock(sig);
-    const nativePub = native.EddsaPublicKey.fromCrock(merchantPub);
+    const nativePub = native.EddsaPublicKey.fromCrock(this.emsc, merchantPub);
     return native.eddsaVerify(
       native.SignaturePurpose.MERCHANT_PAYMENT_OK,
       p.toPurpose(),
@@ -201,22 +196,24 @@ namespace RpcFunctions {
   /**
    * Check if a wire fee is correctly signed.
    */
-  export function isValidWireFee(
-    type: string,
-    wf: WireFee,
-    masterPub: string,
-  ): boolean {
-    const p = new native.MasterWireFeePS({
-      closing_fee: new native.Amount(wf.closingFee).toNbo(),
-      end_date: native.AbsoluteTimeNbo.fromStampSeconds(wf.endStamp),
-      h_wire_method: native.ByteArray.fromStringWithNull(type).hash(),
-      start_date: native.AbsoluteTimeNbo.fromStampSeconds(wf.startStamp),
-      wire_fee: new native.Amount(wf.wireFee).toNbo(),
+  isValidWireFee(type: string, wf: WireFee, masterPub: string): boolean {
+    const p = new native.MasterWireFeePS(this.emsc, {
+      closing_fee: new native.Amount(this.emsc, wf.closingFee).toNbo(),
+      end_date: native.AbsoluteTimeNbo.fromStampSeconds(this.emsc, 
wf.endStamp),
+      h_wire_method: native.ByteArray.fromStringWithNull(
+        this.emsc,
+        type,
+      ).hash(),
+      start_date: native.AbsoluteTimeNbo.fromStampSeconds(
+        this.emsc,
+        wf.startStamp,
+      ),
+      wire_fee: new native.Amount(this.emsc, wf.wireFee).toNbo(),
     });
 
-    const nativeSig = new native.EddsaSignature();
+    const nativeSig = new native.EddsaSignature(this.emsc);
     nativeSig.loadCrock(wf.sig);
-    const nativePub = native.EddsaPublicKey.fromCrock(masterPub);
+    const nativePub = native.EddsaPublicKey.fromCrock(this.emsc, masterPub);
 
     return native.eddsaVerify(
       native.SignaturePurpose.MASTER_WIRE_FEES,
@@ -229,36 +226,39 @@ namespace RpcFunctions {
   /**
    * Check if the signature of a denomination is valid.
    */
-  export function isValidDenom(
-    denom: DenominationRecord,
-    masterPub: string,
-  ): boolean {
-    const p = new native.DenominationKeyValidityPS({
-      denom_hash: native.RsaPublicKey.fromCrock(denom.denomPub)
+  isValidDenom(denom: DenominationRecord, masterPub: string): boolean {
+    const p = new native.DenominationKeyValidityPS(this.emsc, {
+      denom_hash: native.RsaPublicKey.fromCrock(this.emsc, denom.denomPub)
         .encode()
         .hash(),
       expire_legal: native.AbsoluteTimeNbo.fromTalerString(
+        this.emsc,
         denom.stampExpireLegal,
       ),
       expire_spend: native.AbsoluteTimeNbo.fromTalerString(
+        this.emsc,
         denom.stampExpireDeposit,
       ),
       expire_withdraw: native.AbsoluteTimeNbo.fromTalerString(
+        this.emsc,
         denom.stampExpireWithdraw,
       ),
-      fee_deposit: new native.Amount(denom.feeDeposit).toNbo(),
-      fee_refresh: new native.Amount(denom.feeRefresh).toNbo(),
-      fee_refund: new native.Amount(denom.feeRefund).toNbo(),
-      fee_withdraw: new native.Amount(denom.feeWithdraw).toNbo(),
-      master: native.EddsaPublicKey.fromCrock(masterPub),
-      start: native.AbsoluteTimeNbo.fromTalerString(denom.stampStart),
-      value: new native.Amount(denom.value).toNbo(),
+      fee_deposit: new native.Amount(this.emsc, denom.feeDeposit).toNbo(),
+      fee_refresh: new native.Amount(this.emsc, denom.feeRefresh).toNbo(),
+      fee_refund: new native.Amount(this.emsc, denom.feeRefund).toNbo(),
+      fee_withdraw: new native.Amount(this.emsc, denom.feeWithdraw).toNbo(),
+      master: native.EddsaPublicKey.fromCrock(this.emsc, masterPub),
+      start: native.AbsoluteTimeNbo.fromTalerString(
+        this.emsc,
+        denom.stampStart,
+      ),
+      value: new native.Amount(this.emsc, denom.value).toNbo(),
     });
 
-    const nativeSig = new native.EddsaSignature();
+    const nativeSig = new native.EddsaSignature(this.emsc);
     nativeSig.loadCrock(denom.masterSig);
 
-    const nativePub = native.EddsaPublicKey.fromCrock(masterPub);
+    const nativePub = native.EddsaPublicKey.fromCrock(this.emsc, masterPub);
 
     return native.eddsaVerify(
       native.SignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY,
@@ -271,8 +271,8 @@ namespace RpcFunctions {
   /**
    * Create a new EdDSA key pair.
    */
-  export function createEddsaKeypair(): { priv: string; pub: string } {
-    const priv = native.EddsaPrivateKey.create();
+  createEddsaKeypair(): { priv: string; pub: string } {
+    const priv = native.EddsaPrivateKey.create(this.emsc);
     const pub = priv.getPublicKey();
     return { priv: priv.toCrock(), pub: pub.toCrock() };
   }
@@ -280,11 +280,11 @@ namespace RpcFunctions {
   /**
    * Unblind a blindly signed value.
    */
-  export function rsaUnblind(sig: string, bk: string, pk: string): string {
+  rsaUnblind(sig: string, bk: string, pk: string): string {
     const denomSig = native.rsaUnblind(
-      native.RsaSignature.fromCrock(sig),
-      native.RsaBlindingKeySecret.fromCrock(bk),
-      native.RsaPublicKey.fromCrock(pk),
+      native.RsaSignature.fromCrock(this.emsc, sig),
+      native.RsaBlindingKeySecret.fromCrock(this.emsc, bk),
+      native.RsaPublicKey.fromCrock(this.emsc, pk),
     );
     return denomSig.encode().toCrock();
   }
@@ -293,7 +293,7 @@ namespace RpcFunctions {
    * Generate updated coins (to store in the database)
    * and deposit permissions for each given coin.
    */
-  export function signDeposit(
+  signDeposit(
     contractTerms: ContractTerms,
     cds: CoinWithDenom[],
     totalAmount: AmountJson,
@@ -304,7 +304,7 @@ namespace RpcFunctions {
       updatedCoins: [],
     };
 
-    const contractTermsHash = hashString(canonicalJson(contractTerms));
+    const contractTermsHash = this.hashString(canonicalJson(contractTerms));
 
     const feeList: AmountJson[] = cds.map(x => x.denom.feeDeposit);
     let fees = Amounts.add(Amounts.getZero(feeList[0].currency), ...feeList)
@@ -315,27 +315,35 @@ namespace RpcFunctions {
     const total = Amounts.add(fees, totalAmount).amount;
 
     const amountSpent = native.Amount.getZero(
+      this.emsc,
       cds[0].coin.currentAmount.currency,
     );
-    const amountRemaining = new native.Amount(total);
+    const amountRemaining = new native.Amount(this.emsc, total);
     for (const cd of cds) {
-      let coinSpend: Amount;
+      let coinSpend: native.Amount;
       const originalCoin = { ...cd.coin };
 
       if (amountRemaining.value === 0 && amountRemaining.fraction === 0) {
         break;
       }
 
-      if (amountRemaining.cmp(new native.Amount(cd.coin.currentAmount)) < 0) {
-        coinSpend = new native.Amount(amountRemaining.toJson());
+      if (
+        amountRemaining.cmp(
+          new native.Amount(this.emsc, cd.coin.currentAmount),
+        ) < 0
+      ) {
+        coinSpend = new native.Amount(this.emsc, amountRemaining.toJson());
       } else {
-        coinSpend = new native.Amount(cd.coin.currentAmount);
+        coinSpend = new native.Amount(this.emsc, cd.coin.currentAmount);
       }
 
       amountSpent.add(coinSpend);
       amountRemaining.sub(coinSpend);
 
-      const feeDeposit: Amount = new native.Amount(cd.denom.feeDeposit);
+      const feeDeposit: native.Amount = new native.Amount(
+        this.emsc,
+        cd.denom.feeDeposit,
+      );
 
       // Give the merchant at least the deposit fee, otherwise it'll reject
       // the coin.
@@ -343,22 +351,27 @@ namespace RpcFunctions {
         coinSpend = feeDeposit;
       }
 
-      const newAmount = new native.Amount(cd.coin.currentAmount);
+      const newAmount = new native.Amount(this.emsc, cd.coin.currentAmount);
       newAmount.sub(coinSpend);
       cd.coin.currentAmount = newAmount.toJson();
       cd.coin.status = CoinStatus.PurchasePending;
 
-      const d = new native.DepositRequestPS({
+      const d = new native.DepositRequestPS(this.emsc, {
         amount_with_fee: coinSpend.toNbo(),
-        coin_pub: native.EddsaPublicKey.fromCrock(cd.coin.coinPub),
-        deposit_fee: new native.Amount(cd.denom.feeDeposit).toNbo(),
-        h_contract: native.HashCode.fromCrock(contractTermsHash),
-        h_wire: native.HashCode.fromCrock(contractTerms.H_wire),
-        merchant: native.EddsaPublicKey.fromCrock(contractTerms.merchant_pub),
+        coin_pub: native.EddsaPublicKey.fromCrock(this.emsc, cd.coin.coinPub),
+        deposit_fee: new native.Amount(this.emsc, cd.denom.feeDeposit).toNbo(),
+        h_contract: native.HashCode.fromCrock(this.emsc, contractTermsHash),
+        h_wire: native.HashCode.fromCrock(this.emsc, contractTerms.H_wire),
+        merchant: native.EddsaPublicKey.fromCrock(
+          this.emsc,
+          contractTerms.merchant_pub,
+        ),
         refund_deadline: native.AbsoluteTimeNbo.fromTalerString(
+          this.emsc,
           contractTerms.refund_deadline,
         ),
         timestamp: native.AbsoluteTimeNbo.fromTalerString(
+          this.emsc,
           contractTerms.timestamp,
         ),
       });
@@ -366,7 +379,7 @@ namespace RpcFunctions {
       const coinSig = native
         .eddsaSign(
           d.toPurpose(),
-          native.EddsaPrivateKey.fromCrock(cd.coin.coinPriv),
+          native.EddsaPrivateKey.fromCrock(this.emsc, cd.coin.coinPriv),
         )
         .toCrock();
 
@@ -388,7 +401,7 @@ namespace RpcFunctions {
   /**
    * Create a new refresh session.
    */
-  export function createRefreshSession(
+  createRefreshSession(
     exchangeBaseUrl: string,
     kappa: number,
     meltCoin: CoinRecord,
@@ -405,7 +418,7 @@ namespace RpcFunctions {
     // melt fee
     valueWithFee = Amounts.add(valueWithFee, meltFee).amount;
 
-    const sessionHc = new HashContext();
+    const sessionHc = new native.HashContext(this.emsc);
 
     const transferPubs: string[] = [];
     const transferPrivs: string[] = [];
@@ -413,7 +426,7 @@ namespace RpcFunctions {
     const preCoinsForGammas: RefreshPreCoinRecord[][] = [];
 
     for (let i = 0; i < kappa; i++) {
-      const t = native.EcdhePrivateKey.create();
+      const t = native.EcdhePrivateKey.create(this.emsc);
       const pub = t.getPublicKey();
       sessionHc.read(pub);
       transferPrivs.push(t.toCrock());
@@ -421,18 +434,26 @@ namespace RpcFunctions {
     }
 
     for (const denom of newCoinDenoms) {
-      const r = native.RsaPublicKey.fromCrock(denom.denomPub);
+      const r = native.RsaPublicKey.fromCrock(this.emsc, denom.denomPub);
       sessionHc.read(r.encode());
     }
 
-    sessionHc.read(native.EddsaPublicKey.fromCrock(meltCoin.coinPub));
-    sessionHc.read(new native.Amount(valueWithFee).toNbo());
+    sessionHc.read(
+      native.EddsaPublicKey.fromCrock(this.emsc, meltCoin.coinPub),
+    );
+    sessionHc.read(new native.Amount(this.emsc, valueWithFee).toNbo());
 
     for (let i = 0; i < kappa; i++) {
       const preCoins: RefreshPreCoinRecord[] = [];
       for (let j = 0; j < newCoinDenoms.length; j++) {
-        const transferPriv = 
native.EcdhePrivateKey.fromCrock(transferPrivs[i]);
-        const oldCoinPub = native.EddsaPublicKey.fromCrock(meltCoin.coinPub);
+        const transferPriv = native.EcdhePrivateKey.fromCrock(
+          this.emsc,
+          transferPrivs[i],
+        );
+        const oldCoinPub = native.EddsaPublicKey.fromCrock(
+          this.emsc,
+          meltCoin.coinPub,
+        );
         const transferSecret = native.ecdhEddsa(transferPriv, oldCoinPub);
 
         const fresh = native.setupFreshCoin(transferSecret, j);
@@ -442,6 +463,7 @@ namespace RpcFunctions {
         const blindingFactor = fresh.blindingKey;
         const pubHash: native.HashCode = coinPub.hash();
         const denomPub = native.RsaPublicKey.fromCrock(
+          this.emsc,
           newCoinDenoms[j].denomPub,
         );
         const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
@@ -460,21 +482,21 @@ namespace RpcFunctions {
       preCoinsForGammas.push(preCoins);
     }
 
-    const sessionHash = new HashCode();
+    const sessionHash = new native.HashCode(this.emsc);
     sessionHash.alloc();
     sessionHc.finish(sessionHash);
 
-    const confirmData = new RefreshMeltCoinAffirmationPS({
-      amount_with_fee: new Amount(valueWithFee).toNbo(),
-      coin_pub: EddsaPublicKey.fromCrock(meltCoin.coinPub),
-      melt_fee: new Amount(meltFee).toNbo(),
+    const confirmData = new native.RefreshMeltCoinAffirmationPS(this.emsc, {
+      amount_with_fee: new native.Amount(this.emsc, valueWithFee).toNbo(),
+      coin_pub: native.EddsaPublicKey.fromCrock(this.emsc, meltCoin.coinPub),
+      melt_fee: new native.Amount(this.emsc, meltFee).toNbo(),
       session_hash: sessionHash,
     });
 
     const confirmSig: string = native
       .eddsaSign(
         confirmData.toPurpose(),
-        native.EddsaPrivateKey.fromCrock(meltCoin.coinPriv),
+        native.EddsaPrivateKey.fromCrock(this.emsc, meltCoin.coinPriv),
       )
       .toCrock();
 
@@ -505,54 +527,54 @@ namespace RpcFunctions {
   /**
    * Hash a string including the zero terminator.
    */
-  export function hashString(str: string): string {
-    const b = native.ByteArray.fromStringWithNull(str);
+  hashString(str: string): string {
+    const b = native.ByteArray.fromStringWithNull(this.emsc, str);
     return b.hash().toCrock();
   }
 
   /**
    * Hash a denomination public key.
    */
-  export function hashDenomPub(denomPub: string): string {
-    return native.RsaPublicKey.fromCrock(denomPub)
+  hashDenomPub(denomPub: string): string {
+    return native.RsaPublicKey.fromCrock(this.emsc, denomPub)
       .encode()
       .hash()
       .toCrock();
   }
 
-  export function signCoinLink(
+  signCoinLink(
     oldCoinPriv: string,
     newDenomHash: string,
     oldCoinPub: string,
     transferPub: string,
     coinEv: string,
   ): string {
-    const coinEvHash = native.ByteArray.fromCrock(coinEv).hash();
+    const coinEvHash = native.ByteArray.fromCrock(this.emsc, coinEv).hash();
 
-    const coinLink = new native.CoinLinkSignaturePS({
+    const coinLink = new native.CoinLinkSignaturePS(this.emsc, {
       coin_envelope_hash: coinEvHash,
-      h_denom_pub: native.HashCode.fromCrock(newDenomHash),
-      old_coin_pub: native.EddsaPublicKey.fromCrock(oldCoinPub),
-      transfer_pub: native.EcdhePublicKey.fromCrock(transferPub),
+      h_denom_pub: native.HashCode.fromCrock(this.emsc, newDenomHash),
+      old_coin_pub: native.EddsaPublicKey.fromCrock(this.emsc, oldCoinPub),
+      transfer_pub: native.EcdhePublicKey.fromCrock(this.emsc, transferPub),
     });
 
-    const coinPriv = native.EddsaPrivateKey.fromCrock(oldCoinPriv);
+    const coinPriv = native.EddsaPrivateKey.fromCrock(this.emsc, oldCoinPriv);
 
     const sig = native.eddsaSign(coinLink.toPurpose(), coinPriv);
 
     return sig.toCrock();
   }
 
-  export function benchmark(repetitions: number): BenchmarkResult {
+  benchmark(repetitions: number): BenchmarkResult {
     let time_hash = 0;
     for (let i = 0; i < repetitions; i++) {
       const start = timer.performanceNow();
-      hashString("hello world");
+      this.hashString("hello world");
       time_hash += timer.performanceNow() - start;
     }
 
     let time_hash_big = 0;
-    const ba = new native.ByteArray(4096);
+    const ba = new native.ByteArray(this.emsc, 4096);
     for (let i = 0; i < repetitions; i++) {
       ba.randomize(native.RandomQuality.WEAK);
       const start = timer.performanceNow();
@@ -563,19 +585,23 @@ namespace RpcFunctions {
     let time_eddsa_create = 0;
     for (let i = 0; i < repetitions; i++) {
       const start = timer.performanceNow();
-      const priv: native.EddsaPrivateKey = native.EddsaPrivateKey.create();
+      const priv: native.EddsaPrivateKey = native.EddsaPrivateKey.create(
+        this.emsc,
+      );
       time_eddsa_create += timer.performanceNow() - start;
       priv.destroy();
     }
 
     let time_eddsa_sign = 0;
-    const eddsaPriv: native.EddsaPrivateKey = native.EddsaPrivateKey.create();
+    const eddsaPriv: native.EddsaPrivateKey = native.EddsaPrivateKey.create(
+      this.emsc,
+    );
     const eddsaPub: native.EddsaPublicKey = eddsaPriv.getPublicKey();
-    const h: native.HashCode = new native.HashCode();
+    const h: native.HashCode = new native.HashCode(this.emsc);
     h.alloc();
     h.random(native.RandomQuality.WEAK);
 
-    const ps = new native.PaymentSignaturePS({
+    const ps = new native.PaymentSignaturePS(this.emsc, {
       contract_hash: h,
     });
 
@@ -592,7 +618,9 @@ namespace RpcFunctions {
     let time_ecdsa_create = 0;
     for (let i = 0; i < repetitions; i++) {
       const start = timer.performanceNow();
-      const priv: native.EcdsaPrivateKey = native.EcdsaPrivateKey.create();
+      const priv: native.EcdsaPrivateKey = native.EcdsaPrivateKey.create(
+        this.emsc,
+      );
       time_ecdsa_create += timer.performanceNow() - start;
       priv.destroy();
     }
@@ -612,9 +640,12 @@ namespace RpcFunctions {
     /* rsa 2048 */
 
     let time_rsa_2048_blind = 0;
-    const rsaPriv2048: native.RsaPrivateKey = 
native.RsaPrivateKey.create(2048);
+    const rsaPriv2048: native.RsaPrivateKey = native.RsaPrivateKey.create(
+      this.emsc,
+      2048,
+    );
     const rsaPub2048 = rsaPriv2048.getPublicKey();
-    const blindingSecret2048 = native.RsaBlindingKeySecret.create();
+    const blindingSecret2048 = native.RsaBlindingKeySecret.create(this.emsc);
     for (let i = 0; i < repetitions; i++) {
       const start = timer.performanceNow();
       native.rsaBlind(h, blindingSecret2048, rsaPub2048);
@@ -657,9 +688,12 @@ namespace RpcFunctions {
     /* rsa 4096 */
 
     let time_rsa_4096_blind = 0;
-    const rsaPriv4096: native.RsaPrivateKey = 
native.RsaPrivateKey.create(4096);
+    const rsaPriv4096: native.RsaPrivateKey = native.RsaPrivateKey.create(
+      this.emsc,
+      4096,
+    );
     const rsaPub4096 = rsaPriv4096.getPublicKey();
-    const blindingSecret4096 = native.RsaBlindingKeySecret.create();
+    const blindingSecret4096 = native.RsaBlindingKeySecret.create(this.emsc);
     for (let i = 0; i < repetitions; i++) {
       const start = timer.performanceNow();
       native.rsaBlind(h, blindingSecret4096, rsaPub4096);
@@ -718,45 +752,3 @@ namespace RpcFunctions {
     };
   }
 }
-
-const worker: Worker = (self as any) as Worker;
-
-worker.onmessage = (msg: MessageEvent) => {
-  if (!Array.isArray(msg.data.args)) {
-    console.error("args must be array");
-    return;
-  }
-  if (typeof msg.data.id !== "number") {
-    console.error("RPC id must be number");
-  }
-  if (typeof msg.data.operation !== "string") {
-    console.error("RPC operation must be string");
-  }
-  const f = (RpcFunctions as any)[msg.data.operation];
-  if (!f) {
-    console.error(`unknown operation: '${msg.data.operation}'`);
-    return;
-  }
-
-  if (RpcFunctions.enableTracing) {
-    console.log("onmessage with", msg.data.operation);
-  }
-
-  emscLoader.getLib().then(p => {
-    const lib = p.lib;
-    if (!native.isInitialized()) {
-      if (RpcFunctions.enableTracing) {
-        console.log("initializing emscripten for then first time with lib");
-      }
-      native.initialize(lib);
-    }
-    if (RpcFunctions.enableTracing) {
-      console.log("about to execute", msg.data.operation);
-    }
-    const res = f(...msg.data.args);
-    if (RpcFunctions.enableTracing) {
-      console.log("finished executing", msg.data.operation);
-    }
-    worker.postMessage({ result: res, id: msg.data.id });
-  });
-};
diff --git a/src/crypto/cryptoWorker.ts b/src/crypto/cryptoWorker.ts
index 5acda905..11e3d964 100644
--- a/src/crypto/cryptoWorker.ts
+++ b/src/crypto/cryptoWorker.ts
@@ -21,742 +21,57 @@
 /**
  * Imports.
  */
-import * as Amounts from "../amounts";
-import { AmountJson } from "../amounts";
-
-import * as timer from "../timer";
-
-import {
-  CoinRecord,
-  CoinStatus,
-  DenominationRecord,
-  PreCoinRecord,
-  RefreshPreCoinRecord,
-  RefreshSessionRecord,
-  ReserveRecord,
-  TipPlanchet,
-  WireFee,
-} from "../dbTypes";
-
-import { CoinPaySig, ContractTerms, PaybackRequest } from "../talerTypes";
-
-import { BenchmarkResult, CoinWithDenom, PayCoinInfo } from "../walletTypes";
-
-import { canonicalJson } from "../helpers";
 
 import * as emscLoader from "./emscLoader";
 
-import {
-  Amount,
-  EddsaPublicKey,
-  HashCode,
-  HashContext,
-  RefreshMeltCoinAffirmationPS,
-} from "./emscInterface";
-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.
-   */
-  export function createPreCoin(
-    denom: DenominationRecord,
-    reserve: ReserveRecord,
-  ): PreCoinRecord {
-    const reservePriv = new native.EddsaPrivateKey();
-    reservePriv.loadCrock(reserve.reserve_priv);
-    const reservePub = new native.EddsaPublicKey();
-    reservePub.loadCrock(reserve.reserve_pub);
-    const denomPub = native.RsaPublicKey.fromCrock(denom.denomPub);
-    const coinPriv = native.EddsaPrivateKey.create();
-    const coinPub = coinPriv.getPublicKey();
-    const blindingFactor = native.RsaBlindingKeySecret.create();
-    const pubHash: native.HashCode = coinPub.hash();
-    const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
-
-    if (!ev) {
-      throw Error("couldn't blind (malicious exchange key?)");
-    }
-
-    if (!denom.feeWithdraw) {
-      throw Error("Field fee_withdraw missing");
-    }
-
-    const amountWithFee = new native.Amount(denom.value);
-    amountWithFee.add(new native.Amount(denom.feeWithdraw));
-    const withdrawFee = new native.Amount(denom.feeWithdraw);
-
-    const denomPubHash = denomPub.encode().hash();
-
-    // Signature
-    const withdrawRequest = new native.WithdrawRequestPS({
-      amount_with_fee: amountWithFee.toNbo(),
-      h_coin_envelope: ev.hash(),
-      h_denomination_pub: denomPubHash,
-      reserve_pub: reservePub,
-      withdraw_fee: withdrawFee.toNbo(),
-    });
-
-    const sig = native.eddsaSign(withdrawRequest.toPurpose(), reservePriv);
-
-    const preCoin: PreCoinRecord = {
-      blindingKey: blindingFactor.toCrock(),
-      coinEv: ev.toCrock(),
-      coinPriv: coinPriv.toCrock(),
-      coinPub: coinPub.toCrock(),
-      coinValue: denom.value,
-      denomPub: denomPub.toCrock(),
-      denomPubHash: denomPubHash.toCrock(),
-      exchangeBaseUrl: reserve.exchange_base_url,
-      isFromTip: false,
-      reservePub: reservePub.toCrock(),
-      withdrawSig: sig.toCrock(),
-    };
-    return preCoin;
-  }
-
-  /**
-   * Create a planchet used for tipping, including the private keys.
-   */
-  export function createTipPlanchet(denom: DenominationRecord): TipPlanchet {
-    const denomPub = native.RsaPublicKey.fromCrock(denom.denomPub);
-    const coinPriv = native.EddsaPrivateKey.create();
-    const coinPub = coinPriv.getPublicKey();
-    const blindingFactor = native.RsaBlindingKeySecret.create();
-    const pubHash: native.HashCode = coinPub.hash();
-    const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
-
-    if (!ev) {
-      throw Error("couldn't blind (malicious exchange key?)");
-    }
-
-    if (!denom.feeWithdraw) {
-      throw Error("Field fee_withdraw missing");
-    }
-
-    const tipPlanchet: TipPlanchet = {
-      blindingKey: blindingFactor.toCrock(),
-      coinEv: ev.toCrock(),
-      coinPriv: coinPriv.toCrock(),
-      coinPub: coinPub.toCrock(),
-      coinValue: denom.value,
-      denomPub: denomPub.encode().toCrock(),
-      denomPubHash: denomPub
-        .encode()
-        .hash()
-        .toCrock(),
-    };
-    return tipPlanchet;
-  }
-
-  /**
-   * Create and sign a message to request payback for a coin.
-   */
-  export function createPaybackRequest(coin: CoinRecord): PaybackRequest {
-    const p = new native.PaybackRequestPS({
-      coin_blind: native.RsaBlindingKeySecret.fromCrock(coin.blindingKey),
-      coin_pub: native.EddsaPublicKey.fromCrock(coin.coinPub),
-      h_denom_pub: native.RsaPublicKey.fromCrock(coin.denomPub)
-        .encode()
-        .hash(),
-    });
-    const coinPriv = native.EddsaPrivateKey.fromCrock(coin.coinPriv);
-    const coinSig = native.eddsaSign(p.toPurpose(), coinPriv);
-    const paybackRequest: PaybackRequest = {
-      coin_blind_key_secret: coin.blindingKey,
-      coin_pub: coin.coinPub,
-      coin_sig: coinSig.toCrock(),
-      denom_pub: coin.denomPub,
-      denom_sig: coin.denomSig,
-    };
-    return paybackRequest;
-  }
-
-  /**
-   * Check if a payment signature is valid.
-   */
-  export function isValidPaymentSignature(
-    sig: string,
-    contractHash: string,
-    merchantPub: string,
-  ): boolean {
-    const p = new native.PaymentSignaturePS({
-      contract_hash: native.HashCode.fromCrock(contractHash),
-    });
-    const nativeSig = new native.EddsaSignature();
-    nativeSig.loadCrock(sig);
-    const nativePub = native.EddsaPublicKey.fromCrock(merchantPub);
-    return native.eddsaVerify(
-      native.SignaturePurpose.MERCHANT_PAYMENT_OK,
-      p.toPurpose(),
-      nativeSig,
-      nativePub,
-    );
-  }
-
-  /**
-   * Check if a wire fee is correctly signed.
-   */
-  export function isValidWireFee(
-    type: string,
-    wf: WireFee,
-    masterPub: string,
-  ): boolean {
-    const p = new native.MasterWireFeePS({
-      closing_fee: new native.Amount(wf.closingFee).toNbo(),
-      end_date: native.AbsoluteTimeNbo.fromStampSeconds(wf.endStamp),
-      h_wire_method: native.ByteArray.fromStringWithNull(type).hash(),
-      start_date: native.AbsoluteTimeNbo.fromStampSeconds(wf.startStamp),
-      wire_fee: new native.Amount(wf.wireFee).toNbo(),
-    });
-
-    const nativeSig = new native.EddsaSignature();
-    nativeSig.loadCrock(wf.sig);
-    const nativePub = native.EddsaPublicKey.fromCrock(masterPub);
-
-    return native.eddsaVerify(
-      native.SignaturePurpose.MASTER_WIRE_FEES,
-      p.toPurpose(),
-      nativeSig,
-      nativePub,
-    );
-  }
-
-  /**
-   * Check if the signature of a denomination is valid.
-   */
-  export function isValidDenom(
-    denom: DenominationRecord,
-    masterPub: string,
-  ): boolean {
-    const p = new native.DenominationKeyValidityPS({
-      denom_hash: native.RsaPublicKey.fromCrock(denom.denomPub)
-        .encode()
-        .hash(),
-      expire_legal: native.AbsoluteTimeNbo.fromTalerString(
-        denom.stampExpireLegal,
-      ),
-      expire_spend: native.AbsoluteTimeNbo.fromTalerString(
-        denom.stampExpireDeposit,
-      ),
-      expire_withdraw: native.AbsoluteTimeNbo.fromTalerString(
-        denom.stampExpireWithdraw,
-      ),
-      fee_deposit: new native.Amount(denom.feeDeposit).toNbo(),
-      fee_refresh: new native.Amount(denom.feeRefresh).toNbo(),
-      fee_refund: new native.Amount(denom.feeRefund).toNbo(),
-      fee_withdraw: new native.Amount(denom.feeWithdraw).toNbo(),
-      master: native.EddsaPublicKey.fromCrock(masterPub),
-      start: native.AbsoluteTimeNbo.fromTalerString(denom.stampStart),
-      value: new native.Amount(denom.value).toNbo(),
-    });
-
-    const nativeSig = new native.EddsaSignature();
-    nativeSig.loadCrock(denom.masterSig);
-
-    const nativePub = native.EddsaPublicKey.fromCrock(masterPub);
-
-    return native.eddsaVerify(
-      native.SignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY,
-      p.toPurpose(),
-      nativeSig,
-      nativePub,
-    );
-  }
-
-  /**
-   * Create a new EdDSA key pair.
-   */
-  export function createEddsaKeypair(): { priv: string; pub: string } {
-    const priv = native.EddsaPrivateKey.create();
-    const pub = priv.getPublicKey();
-    return { priv: priv.toCrock(), pub: pub.toCrock() };
-  }
-
-  /**
-   * Unblind a blindly signed value.
-   */
-  export function rsaUnblind(sig: string, bk: string, pk: string): string {
-    const denomSig = native.rsaUnblind(
-      native.RsaSignature.fromCrock(sig),
-      native.RsaBlindingKeySecret.fromCrock(bk),
-      native.RsaPublicKey.fromCrock(pk),
-    );
-    return denomSig.encode().toCrock();
-  }
-
-  /**
-   * Generate updated coins (to store in the database)
-   * and deposit permissions for each given coin.
-   */
-  export function signDeposit(
-    contractTerms: ContractTerms,
-    cds: CoinWithDenom[],
-    totalAmount: AmountJson,
-  ): PayCoinInfo {
-    const ret: PayCoinInfo = {
-      originalCoins: [],
-      sigs: [],
-      updatedCoins: [],
-    };
-
-    const contractTermsHash = hashString(canonicalJson(contractTerms));
-
-    const feeList: AmountJson[] = cds.map(x => x.denom.feeDeposit);
-    let fees = Amounts.add(Amounts.getZero(feeList[0].currency), ...feeList)
-      .amount;
-    // okay if saturates
-    fees = Amounts.sub(fees, Amounts.parseOrThrow(contractTerms.max_fee))
-      .amount;
-    const total = Amounts.add(fees, totalAmount).amount;
-
-    const amountSpent = native.Amount.getZero(
-      cds[0].coin.currentAmount.currency,
-    );
-    const amountRemaining = new native.Amount(total);
-    for (const cd of cds) {
-      let coinSpend: Amount;
-      const originalCoin = { ...cd.coin };
-
-      if (amountRemaining.value === 0 && amountRemaining.fraction === 0) {
-        break;
-      }
-
-      if (amountRemaining.cmp(new native.Amount(cd.coin.currentAmount)) < 0) {
-        coinSpend = new native.Amount(amountRemaining.toJson());
-      } else {
-        coinSpend = new native.Amount(cd.coin.currentAmount);
-      }
-
-      amountSpent.add(coinSpend);
-      amountRemaining.sub(coinSpend);
-
-      const feeDeposit: Amount = new native.Amount(cd.denom.feeDeposit);
-
-      // Give the merchant at least the deposit fee, otherwise it'll reject
-      // the coin.
-      if (coinSpend.cmp(feeDeposit) < 0) {
-        coinSpend = feeDeposit;
-      }
-
-      const newAmount = new native.Amount(cd.coin.currentAmount);
-      newAmount.sub(coinSpend);
-      cd.coin.currentAmount = newAmount.toJson();
-      cd.coin.status = CoinStatus.PurchasePending;
-
-      const d = new native.DepositRequestPS({
-        amount_with_fee: coinSpend.toNbo(),
-        coin_pub: native.EddsaPublicKey.fromCrock(cd.coin.coinPub),
-        deposit_fee: new native.Amount(cd.denom.feeDeposit).toNbo(),
-        h_contract: native.HashCode.fromCrock(contractTermsHash),
-        h_wire: native.HashCode.fromCrock(contractTerms.H_wire),
-        merchant: native.EddsaPublicKey.fromCrock(contractTerms.merchant_pub),
-        refund_deadline: native.AbsoluteTimeNbo.fromTalerString(
-          contractTerms.refund_deadline,
-        ),
-        timestamp: native.AbsoluteTimeNbo.fromTalerString(
-          contractTerms.timestamp,
-        ),
-      });
-
-      const coinSig = native
-        .eddsaSign(
-          d.toPurpose(),
-          native.EddsaPrivateKey.fromCrock(cd.coin.coinPriv),
-        )
-        .toCrock();
-
-      const s: CoinPaySig = {
-        coin_pub: cd.coin.coinPub,
-        coin_sig: coinSig,
-        contribution: Amounts.toString(coinSpend.toJson()),
-        denom_pub: cd.coin.denomPub,
-        exchange_url: cd.denom.exchangeBaseUrl,
-        ub_sig: cd.coin.denomSig,
-      };
-      ret.sigs.push(s);
-      ret.updatedCoins.push(cd.coin);
-      ret.originalCoins.push(originalCoin);
-    }
-    return ret;
-  }
-
-  /**
-   * Create a new refresh session.
-   */
-  export function createRefreshSession(
-    exchangeBaseUrl: string,
-    kappa: number,
-    meltCoin: CoinRecord,
-    newCoinDenoms: DenominationRecord[],
-    meltFee: AmountJson,
-  ): RefreshSessionRecord {
-    let valueWithFee = Amounts.getZero(newCoinDenoms[0].value.currency);
-
-    for (const ncd of newCoinDenoms) {
-      valueWithFee = Amounts.add(valueWithFee, ncd.value, ncd.feeWithdraw)
-        .amount;
-    }
-
-    // melt fee
-    valueWithFee = Amounts.add(valueWithFee, meltFee).amount;
-
-    const sessionHc = new HashContext();
-
-    const transferPubs: string[] = [];
-    const transferPrivs: string[] = [];
-
-    const preCoinsForGammas: RefreshPreCoinRecord[][] = [];
-
-    for (let i = 0; i < kappa; i++) {
-      const t = native.EcdhePrivateKey.create();
-      const pub = t.getPublicKey();
-      sessionHc.read(pub);
-      transferPrivs.push(t.toCrock());
-      transferPubs.push(pub.toCrock());
-    }
-
-    for (const denom of newCoinDenoms) {
-      const r = native.RsaPublicKey.fromCrock(denom.denomPub);
-      sessionHc.read(r.encode());
-    }
+import { CryptoImplementation } from "./cryptoImplementation";
+import { EmscEnvironment } from "./emscInterface";
 
-    sessionHc.read(native.EddsaPublicKey.fromCrock(meltCoin.coinPub));
-    sessionHc.read(new native.Amount(valueWithFee).toNbo());
-
-    for (let i = 0; i < kappa; i++) {
-      const preCoins: RefreshPreCoinRecord[] = [];
-      for (let j = 0; j < newCoinDenoms.length; j++) {
-        const transferPriv = 
native.EcdhePrivateKey.fromCrock(transferPrivs[i]);
-        const oldCoinPub = native.EddsaPublicKey.fromCrock(meltCoin.coinPub);
-        const transferSecret = native.ecdhEddsa(transferPriv, oldCoinPub);
-
-        const fresh = native.setupFreshCoin(transferSecret, j);
-
-        const coinPriv = fresh.priv;
-        const coinPub = coinPriv.getPublicKey();
-        const blindingFactor = fresh.blindingKey;
-        const pubHash: native.HashCode = coinPub.hash();
-        const denomPub = native.RsaPublicKey.fromCrock(
-          newCoinDenoms[j].denomPub,
-        );
-        const ev = native.rsaBlind(pubHash, blindingFactor, denomPub);
-        if (!ev) {
-          throw Error("couldn't blind (malicious exchange key?)");
-        }
-        const preCoin: RefreshPreCoinRecord = {
-          blindingKey: blindingFactor.toCrock(),
-          coinEv: ev.toCrock(),
-          privateKey: coinPriv.toCrock(),
-          publicKey: coinPub.toCrock(),
-        };
-        preCoins.push(preCoin);
-        sessionHc.read(ev);
-      }
-      preCoinsForGammas.push(preCoins);
-    }
-
-    const sessionHash = new HashCode();
-    sessionHash.alloc();
-    sessionHc.finish(sessionHash);
-
-    const confirmData = new RefreshMeltCoinAffirmationPS({
-      amount_with_fee: new Amount(valueWithFee).toNbo(),
-      coin_pub: EddsaPublicKey.fromCrock(meltCoin.coinPub),
-      melt_fee: new Amount(meltFee).toNbo(),
-      session_hash: sessionHash,
-    });
-
-    const confirmSig: string = native
-      .eddsaSign(
-        confirmData.toPurpose(),
-        native.EddsaPrivateKey.fromCrock(meltCoin.coinPriv),
-      )
-      .toCrock();
-
-    let valueOutput = Amounts.getZero(newCoinDenoms[0].value.currency);
-    for (const denom of newCoinDenoms) {
-      valueOutput = Amounts.add(valueOutput, denom.value).amount;
-    }
-
-    const refreshSession: RefreshSessionRecord = {
-      confirmSig,
-      exchangeBaseUrl,
-      finished: false,
-      hash: sessionHash.toCrock(),
-      meltCoinPub: meltCoin.coinPub,
-      newDenomHashes: newCoinDenoms.map(d => d.denomPubHash),
-      newDenoms: newCoinDenoms.map(d => d.denomPub),
-      norevealIndex: undefined,
-      preCoinsForGammas,
-      transferPrivs,
-      transferPubs,
-      valueOutput,
-      valueWithFee,
-    };
-
-    return refreshSession;
-  }
-
-  /**
-   * Hash a string including the zero terminator.
-   */
-  export function hashString(str: string): string {
-    const b = native.ByteArray.fromStringWithNull(str);
-    return b.hash().toCrock();
-  }
-
-  /**
-   * Hash a denomination public key.
-   */
-  export function hashDenomPub(denomPub: string): string {
-    return native.RsaPublicKey.fromCrock(denomPub)
-      .encode()
-      .hash()
-      .toCrock();
-  }
-
-  export function signCoinLink(
-    oldCoinPriv: string,
-    newDenomHash: string,
-    oldCoinPub: string,
-    transferPub: string,
-    coinEv: string,
-  ): string {
-    const coinEvHash = native.ByteArray.fromCrock(coinEv).hash();
-
-    const coinLink = new native.CoinLinkSignaturePS({
-      coin_envelope_hash: coinEvHash,
-      h_denom_pub: native.HashCode.fromCrock(newDenomHash),
-      old_coin_pub: native.EddsaPublicKey.fromCrock(oldCoinPub),
-      transfer_pub: native.EcdhePublicKey.fromCrock(transferPub),
-    });
-
-    const coinPriv = native.EddsaPrivateKey.fromCrock(oldCoinPriv);
-
-    const sig = native.eddsaSign(coinLink.toPurpose(), coinPriv);
-
-    return sig.toCrock();
-  }
-
-  export function benchmark(repetitions: number): BenchmarkResult {
-    let time_hash = 0;
-    for (let i = 0; i < repetitions; i++) {
-      const start = timer.performanceNow();
-      hashString("hello world");
-      time_hash += timer.performanceNow() - start;
-    }
-
-    let time_hash_big = 0;
-    const ba = new native.ByteArray(4096);
-    for (let i = 0; i < repetitions; i++) {
-      ba.randomize(native.RandomQuality.WEAK);
-      const start = timer.performanceNow();
-      ba.hash();
-      time_hash_big += timer.performanceNow() - start;
-    }
-
-    let time_eddsa_create = 0;
-    for (let i = 0; i < repetitions; i++) {
-      const start = timer.performanceNow();
-      const priv: native.EddsaPrivateKey = native.EddsaPrivateKey.create();
-      time_eddsa_create += timer.performanceNow() - start;
-      priv.destroy();
-    }
-
-    let time_eddsa_sign = 0;
-    const eddsaPriv: native.EddsaPrivateKey = native.EddsaPrivateKey.create();
-    const eddsaPub: native.EddsaPublicKey = eddsaPriv.getPublicKey();
-    const h: native.HashCode = new native.HashCode();
-    h.alloc();
-    h.random(native.RandomQuality.WEAK);
-
-    const ps = new native.PaymentSignaturePS({
-      contract_hash: h,
-    });
-
-    const p = ps.toPurpose();
-
-    for (let i = 0; i < repetitions; i++) {
-      const start = timer.performanceNow();
-      native.eddsaSign(p, eddsaPriv);
-      time_eddsa_sign += timer.performanceNow() - start;
-    }
-
-    const eddsaSig = native.eddsaSign(p, eddsaPriv);
-
-    let time_ecdsa_create = 0;
-    for (let i = 0; i < repetitions; i++) {
-      const start = timer.performanceNow();
-      const priv: native.EcdsaPrivateKey = native.EcdsaPrivateKey.create();
-      time_ecdsa_create += timer.performanceNow() - start;
-      priv.destroy();
-    }
-
-    let time_eddsa_verify = 0;
-    for (let i = 0; i < repetitions; i++) {
-      const start = timer.performanceNow();
-      native.eddsaVerify(
-        native.SignaturePurpose.MERCHANT_PAYMENT_OK,
-        p,
-        eddsaSig,
-        eddsaPub,
-      );
-      time_eddsa_verify += timer.performanceNow() - start;
-    }
-
-    /* rsa 2048 */
-
-    let time_rsa_2048_blind = 0;
-    const rsaPriv2048: native.RsaPrivateKey = 
native.RsaPrivateKey.create(2048);
-    const rsaPub2048 = rsaPriv2048.getPublicKey();
-    const blindingSecret2048 = native.RsaBlindingKeySecret.create();
-    for (let i = 0; i < repetitions; i++) {
-      const start = timer.performanceNow();
-      native.rsaBlind(h, blindingSecret2048, rsaPub2048);
-      time_rsa_2048_blind += timer.performanceNow() - start;
-    }
-
-    const blindedMessage2048 = native.rsaBlind(
-      h,
-      blindingSecret2048,
-      rsaPub2048,
-    );
-    if (!blindedMessage2048) {
-      throw Error("should not happen");
-    }
-    const rsaBlindSig2048 = native.rsaSignBlinded(
-      rsaPriv2048,
-      blindedMessage2048,
-    );
-
-    let time_rsa_2048_unblind = 0;
-    for (let i = 0; i < repetitions; i++) {
-      const start = timer.performanceNow();
-      native.rsaUnblind(rsaBlindSig2048, blindingSecret2048, rsaPub2048);
-      time_rsa_2048_unblind += timer.performanceNow() - start;
-    }
-
-    const unblindedSig2048 = native.rsaUnblind(
-      rsaBlindSig2048,
-      blindingSecret2048,
-      rsaPub2048,
-    );
-
-    let time_rsa_2048_verify = 0;
-    for (let i = 0; i < repetitions; i++) {
-      const start = timer.performanceNow();
-      native.rsaVerify(h, unblindedSig2048, rsaPub2048);
-      time_rsa_2048_verify += timer.performanceNow() - start;
-    }
-
-    /* rsa 4096 */
-
-    let time_rsa_4096_blind = 0;
-    const rsaPriv4096: native.RsaPrivateKey = 
native.RsaPrivateKey.create(4096);
-    const rsaPub4096 = rsaPriv4096.getPublicKey();
-    const blindingSecret4096 = native.RsaBlindingKeySecret.create();
-    for (let i = 0; i < repetitions; i++) {
-      const start = timer.performanceNow();
-      native.rsaBlind(h, blindingSecret4096, rsaPub4096);
-      time_rsa_4096_blind += timer.performanceNow() - start;
-    }
-
-    const blindedMessage4096 = native.rsaBlind(
-      h,
-      blindingSecret4096,
-      rsaPub4096,
-    );
-    if (!blindedMessage4096) {
-      throw Error("should not happen");
-    }
-    const rsaBlindSig4096 = native.rsaSignBlinded(
-      rsaPriv4096,
-      blindedMessage4096,
-    );
-
-    let time_rsa_4096_unblind = 0;
-    for (let i = 0; i < repetitions; i++) {
-      const start = timer.performanceNow();
-      native.rsaUnblind(rsaBlindSig4096, blindingSecret4096, rsaPub4096);
-      time_rsa_4096_unblind += timer.performanceNow() - start;
-    }
-
-    const unblindedSig4096 = native.rsaUnblind(
-      rsaBlindSig4096,
-      blindingSecret4096,
-      rsaPub4096,
-    );
-
-    let time_rsa_4096_verify = 0;
-    for (let i = 0; i < repetitions; i++) {
-      const start = timer.performanceNow();
-      native.rsaVerify(h, unblindedSig4096, rsaPub4096);
-      time_rsa_4096_verify += timer.performanceNow() - start;
-    }
+const worker: Worker = (self as any) as Worker;
 
-    return {
-      repetitions,
-      time: {
-        hash_small: time_hash,
-        hash_big: time_hash_big,
-        eddsa_create: time_eddsa_create,
-        eddsa_sign: time_eddsa_sign,
-        eddsa_verify: time_eddsa_verify,
-        ecdsa_create: time_ecdsa_create,
-        rsa_2048_blind: time_rsa_2048_blind,
-        rsa_2048_unblind: time_rsa_2048_unblind,
-        rsa_2048_verify: time_rsa_2048_verify,
-        rsa_4096_blind: time_rsa_4096_blind,
-        rsa_4096_unblind: time_rsa_4096_unblind,
-        rsa_4096_verify: time_rsa_4096_verify,
-      },
-    };
-  }
-}
+let impl: CryptoImplementation | undefined;
 
-const worker: Worker = (self as any) as Worker;
 
 worker.onmessage = (msg: MessageEvent) => {
-  if (!Array.isArray(msg.data.args)) {
+  const args = msg.data.args;
+  if (!Array.isArray(args)) {
     console.error("args must be array");
     return;
   }
-  if (typeof msg.data.id !== "number") {
+  const id = msg.data.id;
+  if (typeof id !== "number") {
     console.error("RPC id must be number");
+    return;
   }
-  if (typeof msg.data.operation !== "string") {
+  const operation = msg.data.operation;
+  if (typeof operation !== "string") {
     console.error("RPC operation must be string");
-  }
-  const f = (RpcFunctions as any)[msg.data.operation];
-  if (!f) {
-    console.error(`unknown operation: '${msg.data.operation}'`);
     return;
   }
 
-  if (RpcFunctions.enableTracing) {
-    console.log("onmessage with", msg.data.operation);
+  if (CryptoImplementation.enableTracing) {
+    console.log("onmessage with", operation);
   }
 
   emscLoader.getLib().then(p => {
     const lib = p.lib;
-    if (!native.isInitialized()) {
-      if (RpcFunctions.enableTracing) {
-        console.log("initializing emscripten for then first time with lib");
-      }
-      native.initialize(lib);
+    const emsc = new EmscEnvironment(lib);
+    const impl = new CryptoImplementation(emsc);
+
+    if (!(operation in impl)) {
+      console.error(`unknown operation: '${operation}'`);
+      return;
     }
-    if (RpcFunctions.enableTracing) {
-      console.log("about to execute", msg.data.operation);
+
+    if (CryptoImplementation.enableTracing) {
+      console.log("about to execute", operation);
     }
-    const res = f(...msg.data.args);
-    if (RpcFunctions.enableTracing) {
-      console.log("finished executing", msg.data.operation);
+
+    const result = (impl as any)[operation](...args);
+
+    if (CryptoImplementation.enableTracing) {
+      console.log("finished executing", operation);
     }
-    worker.postMessage({ result: res, id: msg.data.id });
+    worker.postMessage({ result, id });
   });
 };
diff --git a/src/crypto/emscInterface-test.ts b/src/crypto/emscInterface-test.ts
index 305e50ff..51f2d58b 100644
--- a/src/crypto/emscInterface-test.ts
+++ b/src/crypto/emscInterface-test.ts
@@ -20,13 +20,12 @@ import test from "ava";
 import * as emscLoader from "./emscLoader";
 import * as native from "./emscInterface";
 
-test.before(async () => {
+
+test("string hashing", async (t) => {
   const { lib } = await emscLoader.getLib();
-  native.initialize(lib);
-});
+  const emsc = new native.EmscEnvironment(lib);
 
-test("string hashing", (t) => {
-  const x = native.ByteArray.fromStringWithNull("hello taler");
+  const x = native.ByteArray.fromStringWithNull(emsc, "hello taler");
   const h = 
"8RDMADB3YNF3QZBS3V467YZVJAMC2QAQX0TZGVZ6Q5PFRRAJFT70HHN0QF661QR9QWKYMMC7YEMPD679D2RADXCYK8Y669A2A5MKQFR";
   const hc = x.hash().toCrock();
   console.log(`# hc ${hc}`);
@@ -35,28 +34,34 @@ test("string hashing", (t) => {
 });
 
 
-test("signing", (t) => {
-  const x = native.ByteArray.fromStringWithNull("hello taler");
-  const priv = native.EddsaPrivateKey.create();
+test("signing", async (t) => {
+  const { lib } = await emscLoader.getLib();
+  const emsc = new native.EmscEnvironment(lib);
+
+  const x = native.ByteArray.fromStringWithNull(emsc, "hello taler");
+  const priv = native.EddsaPrivateKey.create(emsc, );
   const pub = priv.getPublicKey();
-  const purpose = new native.EccSignaturePurpose(native.SignaturePurpose.TEST, 
x);
+  const purpose = new native.EccSignaturePurpose(emsc, 
native.SignaturePurpose.TEST, x);
   const sig = native.eddsaSign(purpose, priv);
   t.true(native.eddsaVerify(native.SignaturePurpose.TEST, purpose, sig, pub));
   t.pass();
 });
 
 
-test("signing-fixed-data", (t) => {
-  const x = native.ByteArray.fromStringWithNull("hello taler");
-  const purpose = new native.EccSignaturePurpose(native.SignaturePurpose.TEST, 
x);
+test("signing-fixed-data", async (t) => {
+  const { lib } = await emscLoader.getLib();
+  const emsc = new native.EmscEnvironment(lib);
+
+  const x = native.ByteArray.fromStringWithNull(emsc, "hello taler");
+  const purpose = new native.EccSignaturePurpose(emsc, 
native.SignaturePurpose.TEST, x);
   const privStr = "G9R8KRRCAFKPD0KW7PW48CC2T03VQ8K2AN9J6J6K2YW27J5MHN90";
   const pubStr = "YHCZB442FQFJ0ET20MWA8YJ53M61EZGJ6QKV1KTJZMRNXDY45WT0";
   const sigStr = 
"7V6XY4QGC1406GPMT305MZQ1HDCR7R0S5BP02GTGDQFPSXB6YD2YDN5ZS7NJQCNP61Y39MRHXNXQ1Z15JY4CJY4CPDA6CKQ3313WG38";
-  const priv = native.EddsaPrivateKey.fromCrock(privStr);
+  const priv = native.EddsaPrivateKey.fromCrock(emsc, privStr);
   t.true(privStr === priv.toCrock());
   const pub = priv.getPublicKey();
   t.true(pubStr === pub.toCrock());
-  const sig = native.EddsaSignature.fromCrock(sigStr);
+  const sig = native.EddsaSignature.fromCrock(emsc, sigStr);
   t.true(sigStr === sig.toCrock());
   const sig2 = native.eddsaSign(purpose, priv);
   t.true(sig.toCrock() === sig2.toCrock());
@@ -68,27 +73,33 @@ test("signing-fixed-data", (t) => {
 const denomPubStr1 = 
"51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30G9R64VK6HHS6MW42DSN8MVKJGHK6WR3CGT18MWMCDSM75138E1K8S0MADSQ68W34DHH6MW4CHA270W4CG9J6GW48DHG8MVK4E9S7523GEA56H0K4E1Q891KCCSG752KGC1M88VMCDSQ6D23CHHG8H33AGHG6MSK8GT26CRKAC1M64V3JCJ56CVKCC228MWMCHA26MS30H1J8MVKEDHJ70TMADHK892KJC1H60TKJDHM710KGGT584T38H9K851KCDHG60W30HJ28CT4CC1G8CR3JGJ28H236DJ28H330H9S890M2D9S8S14AGA369344GA36S248CHS70RKEDSS6MWKGDJ26D136GT465348CSS8S232CHM6GS34C9N8CS3GD9H60W36H1R8MSK2GSQ8MSM6C9R70SKCHHN6MW3AC
 [...]
 
 
-test("rsa-encode", (t) => {
+test("rsa-encode", async (t) => {
+  const { lib } = await emscLoader.getLib();
+  const emsc = new native.EmscEnvironment(lib);
+
   const pubHashStr = 
"JM63YM5X7X547164QJ3MGJZ4WDD47GEQR5DW5SH35G4JFZXEJBHE5JBNZM5K8XN5C4BRW25BE6GSVAYBF790G2BZZ13VW91D41S4DS0";
-  const denomPub = native.RsaPublicKey.fromCrock(denomPubStr1);
+  const denomPub = native.RsaPublicKey.fromCrock(emsc, denomPubStr1);
   const pubHash = denomPub.encode().hash();
   t.true(pubHashStr === pubHash.toCrock());
   t.pass();
 });
 
 
-test("withdraw-request", (t) => {
+test("withdraw-request", async (t) => {
+  const { lib } = await emscLoader.getLib();
+  const emsc = new native.EmscEnvironment(lib);
+
   const reservePrivStr = 
"G9R8KRRCAFKPD0KW7PW48CC2T03VQ8K2AN9J6J6K2YW27J5MHN90";
-  const reservePriv = native.EddsaPrivateKey.fromCrock(reservePrivStr);
+  const reservePriv = native.EddsaPrivateKey.fromCrock(emsc, reservePrivStr);
   const reservePub = reservePriv.getPublicKey();
-  const amountWithFee = new native.Amount({currency: "KUDOS", value: 1, 
fraction: 10000});
-  amountWithFee.add(new native.Amount({currency: "KUDOS", value: 0, fraction: 
20000}));
-  const withdrawFee = new native.Amount({currency: "KUDOS", value: 0, 
fraction: 20000});
-  const denomPub = native.RsaPublicKey.fromCrock(denomPubStr1);
-  const ev = native.ByteArray.fromStringWithNull("hello, world");
+  const amountWithFee = new native.Amount(emsc, {currency: "KUDOS", value: 1, 
fraction: 10000});
+  amountWithFee.add(new native.Amount(emsc, {currency: "KUDOS", value: 0, 
fraction: 20000}));
+  const withdrawFee = new native.Amount(emsc, {currency: "KUDOS", value: 0, 
fraction: 20000});
+  const denomPub = native.RsaPublicKey.fromCrock(emsc, denomPubStr1);
+  const ev = native.ByteArray.fromStringWithNull(emsc, "hello, world");
 
   // Signature
-  const withdrawRequest = new native.WithdrawRequestPS({
+  const withdrawRequest = new native.WithdrawRequestPS(emsc, {
     amount_with_fee: amountWithFee.toNbo(),
     h_coin_envelope: ev.hash(),
     h_denomination_pub: denomPub.encode().hash(),
@@ -105,9 +116,13 @@ 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});
+test("currency-conversion", async (t) => {
+
+  const { lib } = await emscLoader.getLib();
+  const emsc = new native.EmscEnvironment(lib);
+
+  const a1 = new native.Amount(emsc, {currency: "KUDOS", value: 1, fraction: 
50000000});
+  const a2 = new native.Amount(emsc, {currency: "KUDOS", value: 1, fraction: 
50000000});
   a1.add(a2);
   const x = a1.toJson();
   t.true(x.currency === "KUDOS");
@@ -117,8 +132,11 @@ test("currency-conversion", (t) => {
 });
 
 
-test("ecdsa", (t) => {
-  const priv = native.EcdsaPrivateKey.create();
+test("ecdsa", async (t) => {
+  const { lib } = await emscLoader.getLib();
+  const emsc = new native.EmscEnvironment(lib);
+
+  const priv = native.EcdsaPrivateKey.create(emsc);
   const pub1 = priv.getPublicKey();
   t.truthy(priv);
   t.truthy(pub1);
@@ -126,8 +144,11 @@ test("ecdsa", (t) => {
 });
 
 
-test("ecdhe", (t) => {
-  const priv = native.EcdhePrivateKey.create();
+test("ecdhe", async (t) => {
+  const { lib } = await emscLoader.getLib();
+  const emsc = new native.EmscEnvironment(lib);
+
+  const priv = native.EcdhePrivateKey.create(emsc);
   const pub = priv.getPublicKey();
   t.truthy(priv);
   t.truthy(pub);
diff --git a/src/crypto/emscInterface.ts b/src/crypto/emscInterface.ts
index 70a85cda..96e94e3f 100644
--- a/src/crypto/emscInterface.ts
+++ b/src/crypto/emscInterface.ts
@@ -30,37 +30,6 @@ import { AmountJson } from "../amounts";
 
 import { EmscFunGen, EmscLib } from "./emscLoader";
 
-
-// Will be set only after initialization.
-let maybeEmscEnv: EmscEnvironment | undefined = undefined;
-
-export function isInitialized() {
-  return !!maybeEmscEnv;
-}
-
-
-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");
-  }
-  maybeEmscEnv = new EmscEnvironment(lib);
-}
-
-
-function emsc() {
-  if (maybeEmscEnv) {
-    return maybeEmscEnv;
-  }
-  throw Error("cannot use taler emscripten before initialization");
-}
-
-
 /**
  * Size of a native pointer.  Must match the size
  * use when compiling via emscripten.
@@ -132,7 +101,7 @@ interface EmscAllocFunctions {
   rsa_unblind(a1: number, a2: number, a3: number): number;
 }
 
-class EmscEnvironment {
+export class EmscEnvironment {
 
   /**
    * Emscripten functions that don't do any memory allocations.
@@ -254,8 +223,8 @@ interface ArenaObject {
 export class HashContext implements ArenaObject {
   private hashContextPtr: number | undefined;
 
-  constructor() {
-    this.hashContextPtr = emsc().allocFuncs.hash_context_start();
+  constructor(private emsc: EmscEnvironment) {
+    this.hashContextPtr = emsc.allocFuncs.hash_context_start();
   }
 
   /**
@@ -265,7 +234,7 @@ export class HashContext implements ArenaObject {
     if (!this.hashContextPtr) {
       throw Error("assertion failed");
     }
-    emsc().funcs.hash_context_read(this.hashContextPtr, obj.nativePtr, 
obj.size());
+    this.emsc.funcs.hash_context_read(this.hashContextPtr, obj.nativePtr, 
obj.size());
   }
 
   /**
@@ -276,7 +245,7 @@ export class HashContext implements ArenaObject {
       throw Error("assertion failed");
     }
     h.alloc();
-    emsc().funcs.hash_context_finish(this.hashContextPtr, h.nativePtr);
+    this.emsc.funcs.hash_context_finish(this.hashContextPtr, h.nativePtr);
   }
 
   /**
@@ -284,7 +253,7 @@ export class HashContext implements ArenaObject {
    */
   destroy(): void {
     if (this.hashContextPtr) {
-      emsc().funcs.hash_context_abort(this.hashContextPtr);
+      this.emsc.funcs.hash_context_abort(this.hashContextPtr);
     }
     this.hashContextPtr = undefined;
   }
@@ -304,12 +273,12 @@ abstract class MallocArenaObject implements ArenaObject {
 
   destroy(): void {
     if (this._nativePtr && !this.isWeak) {
-      emsc().funcs.free(this.nativePtr);
+      this.emsc.funcs.free(this.nativePtr);
       this._nativePtr = undefined;
     }
   }
 
-  constructor(arena?: Arena) {
+  constructor(public emsc: EmscEnvironment, arena?: Arena) {
     if (!arena) {
       if (arenaStack.length === 0) {
         throw Error("No arena available");
@@ -323,7 +292,7 @@ abstract class MallocArenaObject implements ArenaObject {
     if (this._nativePtr !== undefined) {
       throw Error("Double allocation");
     }
-    this.nativePtr = emsc().allocFuncs.malloc(size);
+    this.nativePtr = this.emsc.allocFuncs.malloc(size);
   }
 
   set nativePtr(v: number) {
@@ -414,21 +383,21 @@ arenaStack.push(new SyncArena());
  * Representation of monetary value in a given currency.
  */
 export class Amount extends MallocArenaObject {
-  constructor(args?: AmountJson, arena?: Arena) {
-    super(arena);
+  constructor(emsc: EmscEnvironment, args?: AmountJson, arena?: Arena) {
+    super(emsc, arena);
     if (args) {
-      this.nativePtr = emsc().allocFuncs.get_amount(args.value,
+      this.nativePtr = emsc.allocFuncs.get_amount(args.value,
                                             0,
                                             args.fraction,
                                             args.currency);
     } else {
-      this.nativePtr = emsc().allocFuncs.get_amount(0, 0, 0, "");
+      this.nativePtr = emsc.allocFuncs.get_amount(0, 0, 0, "");
     }
   }
 
-  static getZero(currency: string, a?: Arena): Amount {
-    const am = new Amount(undefined, a);
-    const r = emsc().funcs.amount_get_zero(currency, am.nativePtr);
+  static getZero(emsc: EmscEnvironment, currency: string, a?: Arena): Amount {
+    const am = new Amount(emsc, undefined, a);
+    const r = emsc.funcs.amount_get_zero(currency, am.nativePtr);
     if (r !== GNUNET_OK) {
       throw Error("invalid currency");
     }
@@ -437,33 +406,33 @@ export class Amount extends MallocArenaObject {
 
 
   toNbo(a?: Arena): AmountNbo {
-    const x = new AmountNbo(a);
+    const x = new AmountNbo(this.emsc, a);
     x.alloc();
-    emsc().funcs.amount_hton(x.nativePtr, this.nativePtr);
+    this.emsc.funcs.amount_hton(x.nativePtr, this.nativePtr);
     return x;
   }
 
   fromNbo(nbo: AmountNbo): void {
-    emsc().funcs.amount_ntoh(this.nativePtr, nbo.nativePtr);
+    this.emsc.funcs.amount_ntoh(this.nativePtr, nbo.nativePtr);
   }
 
   get value() {
-    return emsc().funcs.get_value(this.nativePtr);
+    return this.emsc.funcs.get_value(this.nativePtr);
   }
 
   get fraction() {
-    return emsc().funcs.get_fraction(this.nativePtr);
+    return this.emsc.funcs.get_fraction(this.nativePtr);
   }
 
   get currency(): string {
-    return emsc().funcs.get_currency(this.nativePtr);
+    return this.emsc.funcs.get_currency(this.nativePtr);
   }
 
   toJson(): AmountJson {
     return {
-      currency: emsc().funcs.get_currency(this.nativePtr),
-      fraction: emsc().funcs.get_fraction(this.nativePtr),
-      value: emsc().funcs.get_value(this.nativePtr),
+      currency: this.emsc.funcs.get_currency(this.nativePtr),
+      fraction: this.emsc.funcs.get_fraction(this.nativePtr),
+      value: this.emsc.funcs.get_value(this.nativePtr),
     };
   }
 
@@ -471,7 +440,7 @@ export class Amount extends MallocArenaObject {
    * Add an amount to this amount.
    */
   add(a: Amount) {
-    const res = emsc().funcs.amount_add(this.nativePtr, a.nativePtr, 
this.nativePtr);
+    const res = this.emsc.funcs.amount_add(this.nativePtr, a.nativePtr, 
this.nativePtr);
     if (res < 1) {
       // Overflow
       return false;
@@ -484,7 +453,7 @@ export class Amount extends MallocArenaObject {
    */
   sub(a: Amount) {
     // this = this - a
-    const res = emsc().funcs.amount_subtract(this.nativePtr, this.nativePtr, 
a.nativePtr);
+    const res = this.emsc.funcs.amount_subtract(this.nativePtr, 
this.nativePtr, a.nativePtr);
     if (res === 0) {
       // Underflow
       return false;
@@ -500,11 +469,11 @@ export class Amount extends MallocArenaObject {
     if (this.currency !== a.currency) {
       throw Error(`incomparable currencies (${this.currency} and 
${a.currency})`);
     }
-    return emsc().funcs.amount_cmp(this.nativePtr, a.nativePtr);
+    return this.emsc.funcs.amount_cmp(this.nativePtr, a.nativePtr);
   }
 
   normalize() {
-    emsc().funcs.amount_normalize(this.nativePtr);
+    this.emsc.funcs.amount_normalize(this.nativePtr);
   }
 }
 
@@ -541,18 +510,18 @@ function countUtf8Bytes(str: string): number {
 abstract class PackedArenaObject extends MallocArenaObject {
   abstract size(): number;
 
-  constructor(a?: Arena) {
-    super(a);
+  constructor(emsc: EmscEnvironment, a?: Arena) {
+    super(emsc, a);
   }
 
   randomize(qual: RandomQuality = RandomQuality.STRONG): void {
-    emsc().funcs.random_block(qual, this.nativePtr, this.size());
+    this.emsc.funcs.random_block(qual, this.nativePtr, this.size());
   }
 
   toCrock(): string {
-    const d = emsc().allocFuncs.data_to_string_alloc(this.nativePtr, 
this.size());
-    const s = emsc().lib.Pointer_stringify(d);
-    emsc().funcs.free(d);
+    const d = this.emsc.allocFuncs.data_to_string_alloc(this.nativePtr, 
this.size());
+    const s = this.emsc.lib.Pointer_stringify(d);
+    this.emsc.funcs.free(d);
     return s;
   }
 
@@ -567,8 +536,8 @@ abstract class PackedArenaObject extends MallocArenaObject {
     this.alloc();
     // We need to get the javascript string
     // to the emscripten heap first.
-    const buf = ByteArray.fromStringWithNull(s);
-    const res = emsc().funcs.string_to_data(buf.nativePtr,
+    const buf = ByteArray.fromStringWithNull(this.emsc, s);
+    const res = this.emsc.funcs.string_to_data(buf.nativePtr,
                                   s.length,
                                   this.nativePtr,
                                   this.size());
@@ -581,21 +550,21 @@ abstract class PackedArenaObject extends 
MallocArenaObject {
   alloc() {
     // FIXME: should the client be allowed to call alloc multiple times?
     if (!this._nativePtr) {
-      this.nativePtr = emsc().allocFuncs.malloc(this.size());
+      this.nativePtr = this.emsc.allocFuncs.malloc(this.size());
     }
   }
 
   hash(): HashCode {
-    const x = new HashCode();
+    const x = new HashCode(this.emsc);
     x.alloc();
-    emsc().funcs.hash(this.nativePtr, this.size(), x.nativePtr);
+    this.emsc.funcs.hash(this.nativePtr, this.size(), x.nativePtr);
     return x;
   }
 
   hexdump() {
     const bytes: string[] = [];
     for (let i = 0; i < this.size(); i++) {
-      let b = emsc().lib.getValue(this.nativePtr + i, "i8");
+      let b = this.emsc.lib.getValue(this.nativePtr + i, "i8");
       b = (b + 256) % 256;
       bytes.push("0".concat(b.toString(16)).slice(-2));
     }
@@ -618,7 +587,7 @@ export class AmountNbo extends PackedArenaObject {
 
   toJson(): any {
     const a = new SimpleArena();
-    const am = new Amount(undefined, a);
+    const am = new Amount(this.emsc, undefined, a);
     am.fromNbo(this);
     const json = am.toJson();
     a.destroy();
@@ -630,8 +599,8 @@ export class AmountNbo extends PackedArenaObject {
 /**
  * Create a packed arena object from the base32 crockford encoding.
  */
-function fromCrock<T extends PackedArenaObject>(s: string, ctor: Ctor<T>): T {
-  const x: T = new ctor();
+function fromCrock<T extends PackedArenaObject>(emsc: EmscEnvironment, s: 
string, ctor: Ctor<T>): T {
+  const x: T = new ctor(emsc);
   x.alloc();
   x.loadCrock(s);
   return x;
@@ -642,11 +611,11 @@ function fromCrock<T extends PackedArenaObject>(s: 
string, ctor: Ctor<T>): T {
  * Create a packed arena object from the base32 crockford encoding for objects
  * that have a special decoding function.
  */
-function fromCrockDecoded<T extends MallocArenaObject>(s: string,
+function fromCrockDecoded<T extends MallocArenaObject>(emsc: EmscEnvironment, 
s: string,
                                                        ctor: Ctor<T>,
                                                        decodeFn: (p: number, 
s: number) => number): T {
-  const obj = new ctor();
-  const buf = ByteArray.fromCrock(s);
+  const obj = new ctor(emsc);
+  const buf = ByteArray.fromCrock(emsc, s);
   obj.nativePtr = decodeFn(buf.nativePtr, buf.size());
   buf.destroy();
   return obj;
@@ -657,11 +626,11 @@ function fromCrockDecoded<T extends MallocArenaObject>(s: 
string,
  * Encode an object using a special encoding function.
  */
 function encode<T extends MallocArenaObject>(obj: T, encodeFn: any, arena?: 
Arena): ByteArray {
-  const ptr = emsc().allocFuncs.malloc(PTR_SIZE);
+  const ptr = obj.emsc.allocFuncs.malloc(PTR_SIZE);
   const len = encodeFn(obj.nativePtr, ptr);
-  const res = new ByteArray(len, undefined, arena);
-  res.nativePtr = emsc().lib.getValue(ptr, "*");
-  emsc().funcs.free(ptr);
+  const res = new ByteArray(obj.emsc, len, undefined, arena);
+  res.nativePtr = obj.emsc.lib.getValue(ptr, "*");
+  obj.emsc.funcs.free(ptr);
   return res;
 }
 
@@ -670,9 +639,9 @@ function encode<T extends MallocArenaObject>(obj: T, 
encodeFn: any, arena?: Aren
  * Private EdDSA key.
  */
 export class EddsaPrivateKey extends PackedArenaObject {
-  static create(a?: Arena): EddsaPrivateKey {
-    const obj = new EddsaPrivateKey(a);
-    obj.nativePtr = emsc().allocFuncs.eddsa_key_create();
+  static create(emsc: EmscEnvironment, a?: Arena): EddsaPrivateKey {
+    const obj = new EddsaPrivateKey(emsc, a);
+    obj.nativePtr = emsc.allocFuncs.eddsa_key_create();
     return obj;
   }
 
@@ -681,13 +650,13 @@ export class EddsaPrivateKey extends PackedArenaObject {
   }
 
   getPublicKey(a?: Arena): EddsaPublicKey {
-    const obj = new EddsaPublicKey(a);
-    obj.nativePtr = 
emsc().allocFuncs.eddsa_public_key_from_private(this.nativePtr);
+    const obj = new EddsaPublicKey(this.emsc, a);
+    obj.nativePtr = 
this.emsc.allocFuncs.eddsa_public_key_from_private(this.nativePtr);
     return obj;
   }
 
-  static fromCrock(s: string): EddsaPrivateKey {
-    return fromCrock(s, this);
+  static fromCrock(emsc: EmscEnvironment, s: string): EddsaPrivateKey {
+    return fromCrock(emsc, s, this);
   }
 }
 
@@ -696,9 +665,9 @@ export class EddsaPrivateKey extends PackedArenaObject {
  * Low-level handle to an EdDSA private key.
  */
 export class EcdsaPrivateKey extends PackedArenaObject {
-  static create(a?: Arena): EcdsaPrivateKey {
-    const obj = new EcdsaPrivateKey(a);
-    obj.nativePtr = emsc().allocFuncs.ecdsa_key_create();
+  static create(emsc: EmscEnvironment, a?: Arena): EcdsaPrivateKey {
+    const obj = new EcdsaPrivateKey(emsc, a);
+    obj.nativePtr = emsc.allocFuncs.ecdsa_key_create();
     return obj;
   }
 
@@ -707,13 +676,13 @@ export class EcdsaPrivateKey extends PackedArenaObject {
   }
 
   getPublicKey(a?: Arena): EcdsaPublicKey {
-    const obj = new EcdsaPublicKey(a);
-    obj.nativePtr = 
emsc().allocFuncs.ecdsa_public_key_from_private(this.nativePtr);
+    const obj = new EcdsaPublicKey(this.emsc, a);
+    obj.nativePtr = 
this.emsc.allocFuncs.ecdsa_public_key_from_private(this.nativePtr);
     return obj;
   }
 
-  static fromCrock(s: string): EcdsaPrivateKey {
-    return fromCrock(s, this);
+  static fromCrock(emsc: EmscEnvironment, s: string): EcdsaPrivateKey {
+    return fromCrock(emsc, s, this);
   }
 }
 
@@ -722,9 +691,9 @@ export class EcdsaPrivateKey extends PackedArenaObject {
  * Low-level handle to an ECDHE private key.
  */
 export class EcdhePrivateKey extends PackedArenaObject {
-  static create(a?: Arena): EcdhePrivateKey {
-    const obj = new EcdhePrivateKey(a);
-    obj.nativePtr = emsc().allocFuncs.ecdhe_key_create();
+  static create(emsc: EmscEnvironment, a?: Arena): EcdhePrivateKey {
+    const obj = new EcdhePrivateKey(emsc, a);
+    obj.nativePtr = emsc.allocFuncs.ecdhe_key_create();
     return obj;
   }
 
@@ -733,13 +702,13 @@ export class EcdhePrivateKey extends PackedArenaObject {
   }
 
   getPublicKey(a?: Arena): EcdhePublicKey {
-    const obj = new EcdhePublicKey(a);
-    obj.nativePtr = 
emsc().allocFuncs.ecdhe_public_key_from_private(this.nativePtr);
+    const obj = new EcdhePublicKey(this.emsc, a);
+    obj.nativePtr = 
this.emsc.allocFuncs.ecdhe_public_key_from_private(this.nativePtr);
     return obj;
   }
 
-  static fromCrock(s: string): EcdhePrivateKey {
-    return fromCrock(s, this);
+  static fromCrock(emsc: EmscEnvironment, s: string): EcdhePrivateKey {
+    return fromCrock(emsc, s, this);
   }
 }
 
@@ -748,7 +717,7 @@ export class EcdhePrivateKey extends PackedArenaObject {
  * Constructor for a given type.
  */
 interface Ctor<T> {
-  new(): T;
+  new(emsc: EmscEnvironment): T;
 }
 
 
@@ -760,8 +729,8 @@ export class EddsaPublicKey extends PackedArenaObject {
     return 32;
   }
 
-  static fromCrock(s: string): EddsaPublicKey {
-    return fromCrock(s, this);
+  static fromCrock(emsc: EmscEnvironment, s: string): EddsaPublicKey {
+    return fromCrock(emsc, s, this);
   }
 }
 
@@ -773,8 +742,8 @@ export class EcdsaPublicKey extends PackedArenaObject {
     return 32;
   }
 
-  static fromCrock(s: string): EcdsaPublicKey {
-    return fromCrock(s, this);
+  static fromCrock(emsc: EmscEnvironment, s: string): EcdsaPublicKey {
+    return fromCrock(emsc, s, this);
   }
 }
 
@@ -787,8 +756,8 @@ export class EcdhePublicKey extends PackedArenaObject {
     return 32;
   }
 
-  static fromCrock(s: string): EcdhePublicKey {
-    return fromCrock(s, this);
+  static fromCrock(emsc: EmscEnvironment, s: string): EcdhePublicKey {
+    return fromCrock(emsc, s, this);
   }
 }
 
@@ -804,15 +773,15 @@ export class RsaBlindingKeySecret extends 
PackedArenaObject {
   /**
    * Create a random blinding key secret.
    */
-  static create(a?: Arena): RsaBlindingKeySecret {
-    const o = new RsaBlindingKeySecret(a);
+  static create(emsc: EmscEnvironment, a?: Arena): RsaBlindingKeySecret {
+    const o = new RsaBlindingKeySecret(emsc, a);
     o.alloc();
     o.randomize();
     return o;
   }
 
-  static fromCrock(s: string): RsaBlindingKeySecret {
-    return fromCrock(s, this);
+  static fromCrock(emsc: EmscEnvironment, s: string): RsaBlindingKeySecret {
+    return fromCrock(emsc, s, this);
   }
 }
 
@@ -825,13 +794,13 @@ export class HashCode extends PackedArenaObject {
     return 64;
   }
 
-  static fromCrock(s: string): HashCode {
-    return fromCrock(s, this);
+  static fromCrock(emsc: EmscEnvironment, s: string): HashCode {
+    return fromCrock(emsc, s, this);
   }
 
   random(qual: RandomQuality = RandomQuality.STRONG) {
     this.alloc();
-    emsc().funcs.hash_create_random(qual, this.nativePtr);
+    this.emsc.funcs.hash_create_random(qual, this.nativePtr);
   }
 }
 
@@ -846,42 +815,42 @@ export class ByteArray extends PackedArenaObject {
     return this.allocatedSize;
   }
 
-  constructor(desiredSize: number, init?: number, a?: Arena) {
-    super(a);
+  constructor(public emsc: EmscEnvironment, desiredSize: number, init?: 
number, a?: Arena) {
+    super(emsc, a);
     if (init === undefined) {
-      this.nativePtr = emsc().allocFuncs.malloc(desiredSize);
+      this.nativePtr = this.emsc.allocFuncs.malloc(desiredSize);
     } else {
       this.nativePtr = init;
     }
     this.allocatedSize = desiredSize;
   }
 
-  static fromStringWithoutNull(s: string, a?: Arena): ByteArray {
+  static fromStringWithoutNull(emsc: EmscEnvironment, s: string, a?: Arena): 
ByteArray {
     // UTF-8 bytes, including 0-terminator
     const terminatedByteLength = countUtf8Bytes(s) + 1;
-    const hstr = emsc().allocFuncs.malloc(terminatedByteLength);
-    emsc().lib.stringToUTF8(s, hstr, terminatedByteLength);
-    return new ByteArray(terminatedByteLength - 1, hstr, a);
+    const hstr = emsc.allocFuncs.malloc(terminatedByteLength);
+    emsc.lib.stringToUTF8(s, hstr, terminatedByteLength);
+    return new ByteArray(emsc, terminatedByteLength - 1, hstr, a);
   }
 
-  static fromStringWithNull(s: string, a?: Arena): ByteArray {
+  static fromStringWithNull(emsc: EmscEnvironment, s: string, a?: Arena): 
ByteArray {
     // UTF-8 bytes, including 0-terminator
     const terminatedByteLength = countUtf8Bytes(s) + 1;
-    const hstr = emsc().allocFuncs.malloc(terminatedByteLength);
-    emsc().lib.stringToUTF8(s, hstr, terminatedByteLength);
-    return new ByteArray(terminatedByteLength, hstr, a);
+    const hstr = emsc.allocFuncs.malloc(terminatedByteLength);
+    emsc.lib.stringToUTF8(s, hstr, terminatedByteLength);
+    return new ByteArray(emsc, terminatedByteLength, hstr, a);
   }
 
-  static fromCrock(s: string, a?: Arena): ByteArray {
+  static fromCrock(emsc: EmscEnvironment, s: string, a?: Arena): ByteArray {
     // this one is a bit more complicated than the other fromCrock functions,
     // since we don't have a fixed size
     const byteLength = countUtf8Bytes(s);
-    const hstr = emsc().allocFuncs.malloc(byteLength + 1);
-    emsc().lib.stringToUTF8(s, hstr, byteLength + 1);
+    const hstr = emsc.allocFuncs.malloc(byteLength + 1);
+    emsc.lib.stringToUTF8(s, hstr, byteLength + 1);
     const decodedLen = Math.floor((byteLength * 5) / 8);
-    const ba = new ByteArray(decodedLen, undefined, a);
-    const res = emsc().funcs.string_to_data(hstr, byteLength, ba.nativePtr, 
decodedLen);
-    emsc().funcs.free(hstr);
+    const ba = new ByteArray(emsc, decodedLen, undefined, a);
+    const res = emsc.funcs.string_to_data(hstr, byteLength, ba.nativePtr, 
decodedLen);
+    emsc.funcs.free(hstr);
     if (res !== GNUNET_OK) {
       throw Error("decoding failed");
     }
@@ -901,11 +870,12 @@ export class EccSignaturePurpose extends 
PackedArenaObject {
 
   private payloadSize: number;
 
-  constructor(purpose: SignaturePurpose,
+  constructor(emsc: EmscEnvironment,
+              purpose: SignaturePurpose,
               payload: PackedArenaObject,
               a?: Arena) {
-    super(a);
-    this.nativePtr = emsc().allocFuncs.purpose_create(purpose,
+    super(emsc, a);
+    this.nativePtr = emsc.allocFuncs.purpose_create(purpose,
                                                       payload.nativePtr,
                                                       payload.size());
     this.payloadSize = payload.size();
@@ -920,7 +890,7 @@ abstract class SignatureStruct {
 
   private members: any = {};
 
-  constructor(x: { [name: string]: any }) {
+  constructor(public emsc: EmscEnvironment, x: { [name: string]: any }) {
     for (const k in x) {
       this.set(k, x[k]);
     }
@@ -937,17 +907,17 @@ abstract class SignatureStruct {
       totalSize += member.size();
     }
 
-    const buf = emsc().allocFuncs.malloc(totalSize);
+    const buf = this.emsc.allocFuncs.malloc(totalSize);
     let ptr = buf;
     for (const f of this.fieldTypes()) {
       const name = f[0];
       const member = this.members[name];
       const size = member.size();
-      emsc().funcs.memmove(ptr, member.nativePtr, size);
+      this.emsc.funcs.memmove(ptr, member.nativePtr, size);
       ptr += size;
     }
-    const ba = new ByteArray(totalSize, buf, a);
-    return new EccSignaturePurpose(this.purpose(), ba);
+    const ba = new ByteArray(this.emsc, totalSize, buf, a);
+    return new EccSignaturePurpose(this.emsc, this.purpose(), ba);
   }
 
 
@@ -1012,8 +982,8 @@ export interface WithdrawRequestPS_Args {
  * Low-level handle to a WithdrawRequest signature structure.
  */
 export class WithdrawRequestPS extends SignatureStruct {
-  constructor(w: WithdrawRequestPS_Args) {
-    super(w);
+  constructor(emsc: EmscEnvironment, w: WithdrawRequestPS_Args) {
+    super(emsc, w);
   }
 
   purpose() {
@@ -1046,8 +1016,8 @@ export interface PaybackRequestPS_args {
  * Low-level handle to a PaybackRequest signature structure.
  */
 export class PaybackRequestPS extends SignatureStruct {
-  constructor(w: PaybackRequestPS_args) {
-    super(w);
+  constructor(emsc: EmscEnvironment, w: PaybackRequestPS_args) {
+    super(emsc, w);
   }
 
   purpose() {
@@ -1078,8 +1048,8 @@ interface RefreshMeltCoinAffirmationPS_Args {
  * Low-level handle to a RefreshMeltCoinAffirmationPS signature structure.
  */
 export class RefreshMeltCoinAffirmationPS extends SignatureStruct {
-  constructor(w: RefreshMeltCoinAffirmationPS_Args) {
-    super(w);
+  constructor(emsc: EmscEnvironment, w: RefreshMeltCoinAffirmationPS_Args) {
+    super(emsc, w);
   }
 
   purpose() {
@@ -1128,8 +1098,8 @@ interface MasterWireFeePS_Args {
  * Low-level handle to a structure being signed over.
  */
 export class MasterWireFeePS extends SignatureStruct {
-  constructor(w: MasterWireFeePS_Args) {
-    super(w);
+  constructor(emsc: EmscEnvironment, w: MasterWireFeePS_Args) {
+    super(emsc, w);
   }
 
   purpose() {
@@ -1152,8 +1122,8 @@ export class MasterWireFeePS extends SignatureStruct {
  * Low-level handle to an absolute time in network byte order (NBO).
  */
 export class AbsoluteTimeNbo extends PackedArenaObject {
-  static fromTalerString(s: string): AbsoluteTimeNbo {
-    const x = new AbsoluteTimeNbo();
+  static fromTalerString(emsc: EmscEnvironment, s: string): AbsoluteTimeNbo {
+    const x = new AbsoluteTimeNbo(emsc);
     x.alloc();
     const r = /Date\(([0-9]+)\)/;
     const m = r.exec(s);
@@ -1162,15 +1132,15 @@ export class AbsoluteTimeNbo extends PackedArenaObject {
     }
     const n = parseInt(m[1], 10) * 1000000;
     // XXX: This only works up to 54 bit numbers.
-    set64(x.nativePtr, n);
+    set64(emsc, x.nativePtr, n);
     return x;
   }
 
-  static fromStampSeconds(stamp: number): AbsoluteTimeNbo {
-    const x = new AbsoluteTimeNbo();
+  static fromStampSeconds(emsc: EmscEnvironment, stamp: number): 
AbsoluteTimeNbo {
+    const x = new AbsoluteTimeNbo(emsc);
     x.alloc();
     // XXX: This only works up to 54 bit numbers.
-    set64(x.nativePtr, stamp * 1000000);
+    set64(emsc, x.nativePtr, stamp * 1000000);
     return x;
   }
 
@@ -1182,17 +1152,17 @@ export class AbsoluteTimeNbo extends PackedArenaObject {
 
 
 // XXX: This only works up to 54 bit numbers.
-function set64(p: number, n: number) {
+function set64(emsc: EmscEnvironment, p: number, n: number) {
   for (let i = 0; i < 8; ++i) {
-    emsc().lib.setValue(p + (7 - i), n & 0xFF, "i8");
+    emsc.lib.setValue(p + (7 - i), n & 0xFF, "i8");
     n = Math.floor(n / 256);
   }
 }
 
 // XXX: This only works up to 54 bit numbers.
-function set32(p: number, n: number) {
+function set32(emsc: EmscEnvironment, p: number, n: number) {
   for (let i = 0; i < 4; ++i) {
-    emsc().lib.setValue(p + (3 - i), n & 0xFF, "i8");
+    emsc.lib.setValue(p + (3 - i), n & 0xFF, "i8");
     n = Math.floor(n / 256);
   }
 }
@@ -1202,10 +1172,10 @@ function set32(p: number, n: number) {
  * Low-level handle to an unsigned 64-bit value.
  */
 export class UInt64 extends PackedArenaObject {
-  static fromNumber(n: number): UInt64 {
-    const x = new UInt64();
+  static fromNumber(emsc: EmscEnvironment, n: number): UInt64 {
+    const x = new UInt64(emsc);
     x.alloc();
-    set64(x.nativePtr, n);
+    set64(emsc, x.nativePtr, n);
     return x;
   }
 
@@ -1219,10 +1189,10 @@ export class UInt64 extends PackedArenaObject {
  * Low-level handle to an unsigned 32-bit value.
  */
 export class UInt32 extends PackedArenaObject {
-  static fromNumber(n: number): UInt32 {
-    const x = new UInt32();
+  static fromNumber(emsc: EmscEnvironment, n: number): UInt32 {
+    const x = new UInt32(emsc);
     x.alloc();
-    set32(x.nativePtr, n);
+    set32(emsc, x.nativePtr, n);
     return x;
   }
 
@@ -1275,8 +1245,8 @@ export interface DepositRequestPS_Args {
  * Low-level handle to a struct being signed over.
  */
 export class DepositRequestPS extends SignatureStruct {
-  constructor(w: DepositRequestPS_Args) {
-    super(w);
+  constructor(emsc: EmscEnvironment, w: DepositRequestPS_Args) {
+    super(emsc, w);
   }
 
   purpose() {
@@ -1307,8 +1277,8 @@ interface CoinLinkSignaturePS_args {
 
 
 export class CoinLinkSignaturePS extends SignatureStruct {
-  constructor(w: CoinLinkSignaturePS_args) {
-    super(w);
+  constructor(emsc: EmscEnvironment, w: CoinLinkSignaturePS_args) {
+    super(emsc, w);
   }
 
   purpose() {
@@ -1348,8 +1318,8 @@ export interface DenominationKeyValidityPS_args {
  * Low-level handle to a structure being signed over.
  */
 export class DenominationKeyValidityPS extends SignatureStruct {
-  constructor(w: DenominationKeyValidityPS_args) {
-    super(w);
+  constructor(emsc: EmscEnvironment, w: DenominationKeyValidityPS_args) {
+    super(emsc, w);
   }
 
   purpose() {
@@ -1388,8 +1358,8 @@ export interface PaymentSignaturePS_args {
  * Low-level handle to a structure being signed over.
  */
 export class PaymentSignaturePS extends SignatureStruct {
-  constructor(w: PaymentSignaturePS_args) {
-    super(w);
+  constructor(emsc: EmscEnvironment, w: PaymentSignaturePS_args) {
+    super(emsc, w);
   }
 
   purpose() {
@@ -1408,13 +1378,13 @@ export class PaymentSignaturePS extends SignatureStruct 
{
  * Low-level handle to an RsaPrivateKey.
  */
 export class RsaPrivateKey extends MallocArenaObject {
-  static fromCrock(s: string): RsaPrivateKey {
-    return fromCrockDecoded(s, this, emsc().allocFuncs.rsa_private_key_decode);
+  static fromCrock(emsc: EmscEnvironment, s: string): RsaPrivateKey {
+    return fromCrockDecoded(emsc, s, this, 
emsc.allocFuncs.rsa_private_key_decode);
   }
 
-  static create(bitLen: number, a?: Arena): RsaPrivateKey {
-    const obj = new RsaPrivateKey(a);
-    obj.nativePtr = emsc().allocFuncs.rsa_private_key_create(bitLen);
+  static create(emsc: EmscEnvironment, bitLen: number, a?: Arena): 
RsaPrivateKey {
+    const obj = new RsaPrivateKey(emsc, a);
+    obj.nativePtr = emsc.allocFuncs.rsa_private_key_create(bitLen);
     return obj;
   }
 
@@ -1424,18 +1394,18 @@ export class RsaPrivateKey extends MallocArenaObject {
 
 
   getPublicKey(a?: Arena): RsaPublicKey {
-    const obj = new RsaPublicKey(a);
-    obj.nativePtr = 
emsc().allocFuncs.rsa_private_key_get_public(this.nativePtr);
+    const obj = new RsaPublicKey(this.emsc, a);
+    obj.nativePtr = 
this.emsc.allocFuncs.rsa_private_key_get_public(this.nativePtr);
     return obj;
   }
 
   destroy() {
-    emsc().funcs.rsa_public_key_free(this.nativePtr);
+    this.emsc.funcs.rsa_public_key_free(this.nativePtr);
     this.nativePtr = 0;
   }
 
   encode(arena?: Arena): ByteArray {
-    return encode(this, emsc().allocFuncs.rsa_private_key_encode);
+    return encode(this, this.emsc.allocFuncs.rsa_private_key_encode);
   }
 }
 
@@ -1444,8 +1414,8 @@ export class RsaPrivateKey extends MallocArenaObject {
  * Low-level handle to an RsaPublicKey.
  */
 export class RsaPublicKey extends MallocArenaObject {
-  static fromCrock(s: string): RsaPublicKey {
-    return fromCrockDecoded(s, this, emsc().allocFuncs.rsa_public_key_decode);
+  static fromCrock(emsc: EmscEnvironment, s: string): RsaPublicKey {
+    return fromCrockDecoded(emsc, s, this, 
emsc.allocFuncs.rsa_public_key_decode);
   }
 
   toCrock() {
@@ -1453,12 +1423,12 @@ export class RsaPublicKey extends MallocArenaObject {
   }
 
   destroy() {
-    emsc().funcs.rsa_public_key_free(this.nativePtr);
+    this.emsc.funcs.rsa_public_key_free(this.nativePtr);
     this.nativePtr = 0;
   }
 
   encode(arena?: Arena): ByteArray {
-    return encode(this, emsc().allocFuncs.rsa_public_key_encode);
+    return encode(this, this.emsc.allocFuncs.rsa_public_key_encode);
   }
 }
 
@@ -1470,8 +1440,8 @@ export class EddsaSignature extends PackedArenaObject {
   size() {
     return 64;
   }
-  static fromCrock(s: string): EddsaSignature {
-    return fromCrock(s, this);
+  static fromCrock(emsc: EmscEnvironment, s: string): EddsaSignature {
+    return fromCrock(emsc, s, this);
   }
 }
 
@@ -1480,16 +1450,16 @@ export class EddsaSignature extends PackedArenaObject {
  * Low-level handle to an RsaSignature.
  */
 export class RsaSignature extends MallocArenaObject {
-  static fromCrock(s: string, a?: Arena) {
-    return fromCrockDecoded(s, this, emsc().allocFuncs.rsa_signature_decode);
+  static fromCrock(emsc: EmscEnvironment, s: string, a?: Arena) {
+    return fromCrockDecoded(emsc, s, this, 
emsc.allocFuncs.rsa_signature_decode);
   }
 
   encode(arena?: Arena): ByteArray {
-    return encode(this, emsc().allocFuncs.rsa_signature_encode);
+    return encode(this, this.emsc.allocFuncs.rsa_signature_encode);
   }
 
   destroy() {
-    emsc().funcs.rsa_signature_free(this.nativePtr);
+    this.emsc.funcs.rsa_signature_free(this.nativePtr);
     this.nativePtr = 0;
   }
 }
@@ -1502,22 +1472,23 @@ export function rsaBlind(hashCode: HashCode,
                          blindingKey: RsaBlindingKeySecret,
                          pkey: RsaPublicKey,
                          arena?: Arena): ByteArray|null {
-  const buf_ptr_out = emsc().allocFuncs.malloc(PTR_SIZE);
-  const buf_size_out = emsc().allocFuncs.malloc(PTR_SIZE);
-  const res = emsc().allocFuncs.rsa_blind(hashCode.nativePtr,
+  const emsc: EmscEnvironment = hashCode.emsc;
+  const buf_ptr_out = emsc.allocFuncs.malloc(PTR_SIZE);
+  const buf_size_out = emsc.allocFuncs.malloc(PTR_SIZE);
+  const res = emsc.allocFuncs.rsa_blind(hashCode.nativePtr,
                                           blindingKey.nativePtr,
                                           pkey.nativePtr,
                                           buf_ptr_out,
                                           buf_size_out);
-  const buf_ptr = emsc().lib.getValue(buf_ptr_out, "*");
-  const buf_size = emsc().lib.getValue(buf_size_out, "*");
-  emsc().funcs.free(buf_ptr_out);
-  emsc().funcs.free(buf_size_out);
+  const buf_ptr = emsc.lib.getValue(buf_ptr_out, "*");
+  const buf_size = emsc.lib.getValue(buf_size_out, "*");
+  emsc.funcs.free(buf_ptr_out);
+  emsc.funcs.free(buf_size_out);
   if (res !== GNUNET_OK) {
     // malicious key
     return null;
   }
-  return new ByteArray(buf_size, buf_ptr, arena);
+  return new ByteArray(emsc, buf_size, buf_ptr, arena);
 }
 
 
@@ -1527,9 +1498,9 @@ export function rsaBlind(hashCode: HashCode,
 export function eddsaSign(purpose: EccSignaturePurpose,
                           priv: EddsaPrivateKey,
                           a?: Arena): EddsaSignature {
-  const sig = new EddsaSignature(a);
+  const sig = new EddsaSignature(purpose.emsc, a);
   sig.alloc();
-  const res = emsc().funcs.eddsa_sign(priv.nativePtr, purpose.nativePtr, 
sig.nativePtr);
+  const res = purpose.emsc.funcs.eddsa_sign(priv.nativePtr, purpose.nativePtr, 
sig.nativePtr);
   if (res < 1) {
     throw Error("EdDSA signing failed");
   }
@@ -1545,7 +1516,7 @@ export function eddsaVerify(purposeNum: number,
                             sig: EddsaSignature,
                             pub: EddsaPublicKey,
                             a?: Arena): boolean {
-  const r = emsc().funcs.eddsa_verify(purposeNum,
+  const r = verify.emsc.funcs.eddsa_verify(purposeNum,
                                       verify.nativePtr,
                                       sig.nativePtr,
                                       pub.nativePtr);
@@ -1556,7 +1527,7 @@ export function eddsaVerify(purposeNum: number,
 export function rsaVerify(h: HashCode,
                           sig: RsaSignature,
                           pub: RsaPublicKey) {
-  const r = emsc().funcs.rsa_verify(h.nativePtr,
+  const r = h.emsc.funcs.rsa_verify(h.nativePtr,
                                     sig.nativePtr,
                                     pub.nativePtr);
   return r === GNUNET_OK;
@@ -1570,8 +1541,8 @@ export function rsaUnblind(sig: RsaSignature,
                            bk: RsaBlindingKeySecret,
                            pk: RsaPublicKey,
                            a?: Arena): RsaSignature {
-  const x = new RsaSignature(a);
-  x.nativePtr = emsc().allocFuncs.rsa_unblind(sig.nativePtr,
+  const x = new RsaSignature(sig.emsc, a);
+  x.nativePtr = sig.emsc.allocFuncs.rsa_unblind(sig.nativePtr,
                                               bk.nativePtr,
                                               pk.nativePtr);
   return x;
@@ -1600,9 +1571,9 @@ export interface FreshCoin {
  */
 export function ecdhEddsa(priv: EcdhePrivateKey,
                           pub: EddsaPublicKey): HashCode {
-  const h = new HashCode();
+  const h = new HashCode(priv.emsc);
   h.alloc();
-  const res = emsc().funcs.ecdh_eddsa(priv.nativePtr, pub.nativePtr, 
h.nativePtr);
+  const res = priv.emsc.funcs.ecdh_eddsa(priv.nativePtr, pub.nativePtr, 
h.nativePtr);
   if (res !== GNUNET_OK) {
     throw Error("ecdh_eddsa failed");
   }
@@ -1611,8 +1582,8 @@ export function ecdhEddsa(priv: EcdhePrivateKey,
 
 export function rsaSignBlinded(priv: RsaPrivateKey,
                                msg: ByteArray): RsaSignature {
-  const sig = new RsaSignature();
-  sig.nativePtr = emsc().allocFuncs.rsa_sign_blinded (priv.nativePtr,
+  const sig = new RsaSignature(priv.emsc);
+  sig.nativePtr = priv.emsc.allocFuncs.rsa_sign_blinded (priv.nativePtr,
                                                       msg.nativePtr,
                                                       msg.size());
   return sig;
@@ -1625,13 +1596,14 @@ export function rsaSignBlinded(priv: RsaPrivateKey,
  */
 export function setupFreshCoin(secretSeed: TransferSecretP,
                                coinIndex: number): FreshCoin {
-  const priv = new EddsaPrivateKey();
+  const emsc: EmscEnvironment = secretSeed.emsc; 
+  const priv = new EddsaPrivateKey(emsc);
   priv.isWeak = true;
-  const blindingKey = new RsaBlindingKeySecret();
+  const blindingKey = new RsaBlindingKeySecret(emsc);
   blindingKey.isWeak = true;
-  const buf = new ByteArray(priv.size() + blindingKey.size());
+  const buf = new ByteArray(emsc, priv.size() + blindingKey.size());
 
-  emsc().funcs.setup_fresh_coin(secretSeed.nativePtr, coinIndex, 
buf.nativePtr);
+  emsc.funcs.setup_fresh_coin(secretSeed.nativePtr, coinIndex, buf.nativePtr);
 
   priv.nativePtr = buf.nativePtr;
   blindingKey.nativePtr = buf.nativePtr + priv.size();
diff --git a/src/crypto/nodeWorker.ts b/src/crypto/nodeProcessWorker.ts
similarity index 100%
rename from src/crypto/nodeWorker.ts
rename to src/crypto/nodeProcessWorker.ts
diff --git a/src/crypto/startWorker.js b/src/crypto/startWorker.js
deleted file mode 100644
index a64152c6..00000000
--- a/src/crypto/startWorker.js
+++ /dev/null
@@ -1,41 +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
-
-
-/**
- * Start a crypto worker, using different worker
- * mechanisms depending on the environment.
- *
- * @returns {Worker}
- */
-export function startWorker() {
-  let workerCtor;
-  let workerPath;
-  if (typeof Worker !== "undefined") {
-    // we're in the browser
-    workerCtor = Worker;
-    workerPath = "/dist/cryptoWorker-bundle.js";
-  } else if (typeof "require" !== "undefined") {
-    workerCtor = require("./nodeWorker").Worker;
-    workerPath = __dirname + "/cryptoWorker.js";
-  } else {
-    throw Error("Can't create worker, unknown environment");
-  }
-  return new workerCtor(workerPath);
-}
diff --git a/src/crypto/synchronousWorker.ts b/src/crypto/synchronousWorker.ts
new file mode 100644
index 00000000..d8a3d83c
--- /dev/null
+++ b/src/crypto/synchronousWorker.ts
@@ -0,0 +1,162 @@
+/*
+ This file is part of GNU Taler
+ (C) 2019 GNUnet e.V.
+
+ 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/>
+ */
+
+import { EmscEnvironment } from "./emscInterface";
+import { CryptoImplementation } from "./cryptoImplementation";
+
+/**
+ * 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.
+   */
+  onmessage: undefined | ((m: any) => void);
+
+  /**
+   * Function to be called when we receive an error from the worker thread.
+   */
+  onerror: undefined | ((m: any) => void);
+
+  constructor() {
+    this.onerror = undefined;
+    this.onmessage = undefined;
+  }
+
+  /**
+   * Add an event listener for either an "error" or "message" event.
+   */
+  addEventListener(event: "message" | "error", fn: (x: any) => void): void {
+    switch (event) {
+      case "message":
+        this.onmessage = fn;
+        break;
+      case "error":
+        this.onerror = fn;
+        break;
+    }
+  }
+
+  private getEmscriptenEnvironment(): Promise<EmscEnvironment> {
+    if (this.cachedEmscEnvironment) {
+      return Promise.resolve(this.cachedEmscEnvironment);
+    }
+
+    if (this.cachedEmscEnvironmentPromise) {
+      return this.cachedEmscEnvironmentPromise;
+    }
+
+    // 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;
+
+    // Assume that the code is run from the build/ directory.
+    const libFn = indirectRequire(
+      "../../../emscripten/taler-emscripten-lib.js",
+    );
+    const lib = libFn();
+    // @ts-ignore
+    g.importScripts = savedImportScripts;
+
+    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'",
+      );
+    }
+
+    this.cachedEmscEnvironmentPromise = new Promise((resolve, reject) => {
+      lib.onRuntimeInitialized = () => {
+        this.cachedEmscEnvironmentPromise = undefined;
+        this.cachedEmscEnvironment = new EmscEnvironment(lib);
+        resolve(this.cachedEmscEnvironment);
+      };
+    });
+    return this.cachedEmscEnvironmentPromise;
+  }
+
+  private dispatchMessage(msg: any) {
+    if (this.onmessage) {
+      this.onmessage({ data: msg });
+    }
+  }
+
+  private async handleRequest(operation: string, id: number, args: string[]) {
+    let emsc = await this.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);
+      this.dispatchMessage({ result, id });
+    } catch (e) {
+      console.log("error during operation", e);
+      return;
+    }
+  }
+
+  /**
+   * Send a message to the worker thread.
+   */
+  postMessage(msg: any) {
+    const args = msg.args;
+    if (!Array.isArray(args)) {
+      console.error("args must be array");
+      return;
+    }
+    const id = msg.id;
+    if (typeof id !== "number") {
+      console.error("RPC id must be number");
+      return;
+    }
+    const operation = msg.operation;
+    if (typeof operation !== "string") {
+      console.error("RPC operation must be string");
+      return;
+    }
+
+    this.handleRequest(operation, id, args);
+  }
+
+  /**
+   * Forcibly terminate the worker thread.
+   */
+  terminate() {
+    console.log("terminating synchronous worker (no-op)");
+  }
+}
diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts
index 0c6c2ba9..1e501cdc 100644
--- a/src/headless/taler-wallet-cli.ts
+++ b/src/headless/taler-wallet-cli.ts
@@ -26,6 +26,7 @@ import URI = require("urijs");
 
 import querystring = require("querystring");
 import { CheckPaymentResponse } from "../talerTypes";
+import { NodeCryptoWorkerFactory, SynchronousCryptoWorkerFactory } from 
"../crypto/cryptoApi";
 
 const enableTracing = false;
 
@@ -268,7 +269,8 @@ export async function main() {
     myUnsupportedUpgrade,
   );
 
-  const myWallet = new Wallet(myDb, myHttpLib, myBadge, myNotifier);
+  const myWallet = new Wallet(myDb, myHttpLib, myBadge, myNotifier, new 
SynchronousCryptoWorkerFactory());
+  //const myWallet = new Wallet(myDb, myHttpLib, myBadge, myNotifier, new 
NodeCryptoWorkerFactory());
 
   const reserveResponse = await myWallet.createReserve({
     amount: amounts.parseOrThrow("TESTKUDOS:10.0"),
diff --git a/src/wallet.ts b/src/wallet.ts
index 6d4eeb26..ffdb60fa 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -22,7 +22,7 @@
 /**
  * Imports.
  */
-import { CryptoApi } from "./crypto/cryptoApi";
+import { CryptoApi, CryptoWorkerFactory } from "./crypto/cryptoApi";
 import {
   amountToPretty,
   canonicalJson,
@@ -242,7 +242,7 @@ export function selectPayCoins(
       ) >= 0;
     const isBelowFee = Amounts.cmp(accDepositFee, depositFeeLimit) <= 0;
 
-    console.log("coin selection", {
+    console.log("candidate coin selection", {
       coversAmount,
       isBelowFee,
       accDepositFee,
@@ -360,12 +360,13 @@ export class Wallet {
     http: HttpRequestLibrary,
     badge: Badge,
     notifier: Notifier,
+    cryptoWorkerFactory: CryptoWorkerFactory,
   ) {
     this.db = db;
     this.http = http;
     this.badge = badge;
     this.notifier = notifier;
-    this.cryptoApi = new CryptoApi();
+    this.cryptoApi = new CryptoApi(cryptoWorkerFactory);
     this.timerGroup = new TimerGroup();
 
     const init = async () => {
@@ -535,8 +536,8 @@ export class Wallet {
   }
 
   /**
-   * Get exchanges and associated coins that are still spendable,
-   * but only if the sum the coins' remaining value exceeds the payment amount.
+   * Get exchanges and associated coins that are still spendable, but only
+   * if the sum the coins' remaining value covers the payment amount and fees.
    */
   private async getCoinsForPayment(
     args: CoinsForPaymentArgs,
@@ -591,6 +592,7 @@ export class Wallet {
       const coins: CoinRecord[] = await this.q()
         .iterIndex(Stores.coins.exchangeBaseUrlIndex, exchange.baseUrl)
         .toArray();
+
       const denoms = await this.q()
         .iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl)
         .toArray();
@@ -643,12 +645,6 @@ export class Wallet {
         continue;
       }
 
-      console.log("payment coins: wireFeeLimit", wireFeeLimit);
-      console.log("payment coins: wireFeeAmortization", wireFeeAmortization);
-      console.log("payment coins: fees", fees);
-      console.log("payment coins: wireMethod", wireMethod);
-      console.log("payment coins: wireFeeTime", wireFeeTime);
-
       let totalFees = Amounts.getZero(currency);
       let wireFee: AmountJson | undefined;
       for (const fee of fees.feesForType[wireMethod] || []) {
@@ -658,8 +654,6 @@ export class Wallet {
         }
       }
 
-      console.log("payment coins: current wire fees", wireFee);
-
       if (wireFee) {
         const amortizedWireFee = Amounts.divide(wireFee, wireFeeAmortization);
         if (Amounts.cmp(wireFeeLimit, amortizedWireFee) < 0) {
@@ -670,6 +664,7 @@ export class Wallet {
       }
 
       const res = selectPayCoins(denoms, cds, remainingAmount, 
depositFeeLimit);
+
       if (res) {
         totalFees = Amounts.add(totalFees, res.totalFees).amount;
         return {
@@ -747,7 +742,6 @@ export class Wallet {
       console.log("contract download failed", e);
       throw e;
     }
-    console.log("got response", resp);
 
     const proposal = Proposal.checked(resp.data);
 
@@ -918,7 +912,6 @@ export class Wallet {
       wireMethod: proposal.contractTerms.wire_method,
     });
 
-    console.log("max_fee", proposal.contractTerms.max_fee);
     console.log("coin selection result", res);
 
     if (!res) {
@@ -1032,6 +1025,8 @@ export class Wallet {
       return { status: "insufficient-balance" };
     }
 
+    console.log("checkPay: payment possible!");
+
     // Only create speculative signature if we don't already have one for this 
proposal
     if (
       !this.speculativePayData ||
@@ -1050,6 +1045,7 @@ export class Wallet {
         proposal,
         proposalId,
       };
+      console.log("created speculative pay data for payment");
     }
 
     return { status: "payment-possible", coinSelection: res };
@@ -1155,7 +1151,6 @@ export class Wallet {
         return op.promise;
       }
 
-      //console.log("executing processPreCoin", preCoin);
       this.processPreCoinConcurrent++;
 
       try {
diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts
index 0baa7d41..83149535 100644
--- a/src/webex/wxBackend.ts
+++ b/src/webex/wxBackend.ts
@@ -58,6 +58,7 @@ import URI = require("urijs");
 import Port = chrome.runtime.Port;
 import MessageSender = chrome.runtime.MessageSender;
 import { TipToken } from "../talerTypes";
+import { BrowserCryptoWorkerFactory } from "../crypto/cryptoApi";
 
 const NeedsWallet = Symbol("NeedsWallet");
 
@@ -711,7 +712,7 @@ async function reinitWallet() {
   const http = new BrowserHttpLib();
   const notifier = new ChromeNotifier();
   console.log("setting wallet");
-  const wallet = new Wallet(db, http, badge, notifier);
+  const wallet = new Wallet(db, http, badge, notifier, new 
BrowserCryptoWorkerFactory());
   // Useful for debugging in the background page.
   (window as any).talerWallet = wallet;
   currentWallet = wallet;
diff --git a/tsconfig.json b/tsconfig.json
index 3f29f420..8104054a 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -29,14 +29,15 @@
     "src/checkable.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/nodeWorker.ts",
+    "src/crypto/nodeProcessWorker.ts",
     "src/crypto/nodeWorkerEntry.ts",
-    "src/crypto/startWorker.js",
+    "src/crypto/synchronousWorker.ts",
     "src/db.ts",
     "src/dbTypes.ts",
     "src/headless/taler-wallet-cli.ts",
diff --git a/yarn.lock b/yarn.lock
index f779b403..c61d6fd2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3216,10 +3216,10 @@ iconv-lite@^0.4.4, iconv-lite@~0.4.13:
   dependencies:
     safer-buffer ">= 2.1.2 < 3"
 
-idb-bridge@^0.0.1:
-  version "0.0.1"
-  resolved 
"https://registry.yarnpkg.com/idb-bridge/-/idb-bridge-0.0.1.tgz#4498704b79f354dcd3a628825656967939003614";
-  integrity 
sha512-GTFqRkjFk4C98zvPA65cKpB1JnBt+bFftn+Kkwucoy+hLmxVfdmbwZ6hj2ZieBHOppMtRs0il3zbzSzLofWrDg==
+idb-bridge@0.0.2:
+  version "0.0.2"
+  resolved 
"https://registry.yarnpkg.com/idb-bridge/-/idb-bridge-0.0.2.tgz#daa46d75060bd6a116b26155c314446bea355570";
+  integrity 
sha512-PEfZmdbIQUV4vxJRSSXhan7niclJDJGPGUSJ2WlHCYCgdFK6n25UD8z/lsLoqWKfcp+xPuL+9MI+h9Ql8XFzkw==
 
 ieee754@^1.1.4:
   version "1.1.13"

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



reply via email to

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