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