splitting syncWorker with the factory so the former do not require nodejs runtime
This commit is contained in:
parent
8b0294ee41
commit
bf0cb6ab13
@ -25,54 +25,24 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// FIXME: Crypto should not use DB Types!
|
// FIXME: Crypto should not use DB Types!
|
||||||
import { DenominationRecord, WireFee } from "../../db.js";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
buildSigPS,
|
AmountJson, Amounts, BenchmarkResult, buildSigPS,
|
||||||
CoinDepositPermission,
|
CoinDepositPermission, createEddsaKeyPair, createHashContext, decodeCrock,
|
||||||
DenomKeyType,
|
DenomKeyType, DepositInfo, eddsaGetPublic, eddsaSign, eddsaVerify,
|
||||||
ExchangeProtocolVersion,
|
encodeCrock, ExchangeProtocolVersion,
|
||||||
FreshCoin,
|
FreshCoin, hash, hashDenomPub, kdf, keyExchangeEcdheEddsa,
|
||||||
hashDenomPub,
|
// Logger,
|
||||||
RecoupRefreshRequest,
|
MakeSyncSignatureRequest, PlanchetCreationRequest, PlanchetCreationResult,
|
||||||
|
randomBytes, RecoupRefreshRequest,
|
||||||
RecoupRequest,
|
RecoupRequest,
|
||||||
RefreshPlanchetInfo,
|
RefreshPlanchetInfo, rsaBlind, rsaUnblind, rsaVerify, setupRefreshPlanchet,
|
||||||
TalerSignaturePurpose,
|
|
||||||
} from "@gnu-taler/taler-util";
|
|
||||||
// FIXME: These types should be internal to the wallet!
|
|
||||||
import {
|
|
||||||
BenchmarkResult,
|
|
||||||
PlanchetCreationResult,
|
|
||||||
PlanchetCreationRequest,
|
|
||||||
DepositInfo,
|
|
||||||
MakeSyncSignatureRequest,
|
|
||||||
} from "@gnu-taler/taler-util";
|
|
||||||
import { AmountJson, Amounts } from "@gnu-taler/taler-util";
|
|
||||||
import * as timer from "../../util/timer.js";
|
|
||||||
import {
|
|
||||||
encodeCrock,
|
|
||||||
decodeCrock,
|
|
||||||
createEddsaKeyPair,
|
|
||||||
hash,
|
|
||||||
rsaBlind,
|
|
||||||
eddsaVerify,
|
|
||||||
eddsaSign,
|
|
||||||
rsaUnblind,
|
|
||||||
stringToBytes,
|
|
||||||
createHashContext,
|
|
||||||
keyExchangeEcdheEddsa,
|
|
||||||
setupRefreshPlanchet,
|
|
||||||
rsaVerify,
|
|
||||||
setupRefreshTransferPub,
|
setupRefreshTransferPub,
|
||||||
setupTipPlanchet,
|
setupTipPlanchet,
|
||||||
setupWithdrawPlanchet,
|
setupWithdrawPlanchet, stringToBytes, TalerSignaturePurpose, Timestamp, timestampTruncateToSecond
|
||||||
eddsaGetPublic,
|
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
import { randomBytes } from "@gnu-taler/taler-util";
|
import bigint from "big-integer";
|
||||||
import { kdf } from "@gnu-taler/taler-util";
|
import { DenominationRecord, WireFee } from "../../db.js";
|
||||||
import { Timestamp, timestampTruncateToSecond } from "@gnu-taler/taler-util";
|
import * as timer from "../../util/timer.js";
|
||||||
|
|
||||||
import { Logger } from "@gnu-taler/taler-util";
|
|
||||||
import {
|
import {
|
||||||
CreateRecoupRefreshReqRequest,
|
CreateRecoupRefreshReqRequest,
|
||||||
CreateRecoupReqRequest,
|
CreateRecoupReqRequest,
|
||||||
@ -80,11 +50,10 @@ import {
|
|||||||
DerivedTipPlanchet,
|
DerivedTipPlanchet,
|
||||||
DeriveRefreshSessionRequest,
|
DeriveRefreshSessionRequest,
|
||||||
DeriveTipRequest,
|
DeriveTipRequest,
|
||||||
SignTrackTransactionRequest,
|
SignTrackTransactionRequest
|
||||||
} from "../cryptoTypes.js";
|
} from "../cryptoTypes.js";
|
||||||
import bigint from "big-integer";
|
|
||||||
|
|
||||||
const logger = new Logger("cryptoImplementation.ts");
|
// const logger = new Logger("cryptoImplementation.ts");
|
||||||
|
|
||||||
function amountToBuffer(amount: AmountJson): Uint8Array {
|
function amountToBuffer(amount: AmountJson): Uint8Array {
|
||||||
const buffer = new ArrayBuffer(8 + 4 + 12);
|
const buffer = new ArrayBuffer(8 + 4 + 12);
|
||||||
|
@ -14,135 +14,16 @@
|
|||||||
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 { Logger } from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
CryptoImplementation,
|
CryptoImplementation,
|
||||||
PrimitiveWorker,
|
PrimitiveWorker
|
||||||
} from "./cryptoImplementation.js";
|
} from "./cryptoImplementation.js";
|
||||||
|
|
||||||
import { CryptoWorkerFactory } from "./cryptoApi.js";
|
|
||||||
import { CryptoWorker } from "./cryptoWorkerInterface.js";
|
|
||||||
|
|
||||||
import child_process from "child_process";
|
|
||||||
import type internal from "stream";
|
|
||||||
import { OpenedPromise, openPromise } from "../../index.js";
|
|
||||||
import { j2s, Logger } from "@gnu-taler/taler-util";
|
|
||||||
|
|
||||||
const logger = new Logger("synchronousWorker.ts");
|
const logger = new Logger("synchronousWorker.ts");
|
||||||
|
|
||||||
class MyPrimitiveWorker implements PrimitiveWorker {
|
|
||||||
proc: child_process.ChildProcessByStdio<
|
|
||||||
internal.Writable,
|
|
||||||
internal.Readable,
|
|
||||||
null
|
|
||||||
>;
|
|
||||||
requests: Array<{
|
|
||||||
p: OpenedPromise<any>;
|
|
||||||
req: any;
|
|
||||||
}> = [];
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
const stdoutChunks: Buffer[] = [];
|
|
||||||
this.proc = child_process.spawn("taler-crypto-worker", {
|
|
||||||
//stdio: ["pipe", "pipe", "inherit"],
|
|
||||||
stdio: ["pipe", "pipe", "inherit"],
|
|
||||||
detached: true,
|
|
||||||
});
|
|
||||||
this.proc.on("close", function (code) {
|
|
||||||
logger.error("child process exited");
|
|
||||||
});
|
|
||||||
(this.proc.stdout as any).unref();
|
|
||||||
(this.proc.stdin as any).unref();
|
|
||||||
this.proc.unref();
|
|
||||||
|
|
||||||
this.proc.stdout.on("data", (x) => {
|
|
||||||
// console.log("got chunk", x.toString("utf-8"));
|
|
||||||
if (x instanceof Buffer) {
|
|
||||||
const nlIndex = x.indexOf("\n");
|
|
||||||
if (nlIndex >= 0) {
|
|
||||||
const before = x.slice(0, nlIndex);
|
|
||||||
const after = x.slice(nlIndex + 1);
|
|
||||||
stdoutChunks.push(after);
|
|
||||||
const str = Buffer.concat([...stdoutChunks, before]).toString(
|
|
||||||
"utf-8",
|
|
||||||
);
|
|
||||||
const req = this.requests.shift()!;
|
|
||||||
if (this.requests.length === 0) {
|
|
||||||
this.proc.unref();
|
|
||||||
}
|
|
||||||
//logger.info(`got response: ${str}`);
|
|
||||||
req.p.resolve(JSON.parse(str));
|
|
||||||
} else {
|
|
||||||
stdoutChunks.push(x);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw Error(`unexpected data chunk type (${typeof x})`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async setupRefreshPlanchet(req: {
|
|
||||||
transfer_secret: string;
|
|
||||||
coin_index: number;
|
|
||||||
}): Promise<{
|
|
||||||
coin_pub: string;
|
|
||||||
coin_priv: string;
|
|
||||||
blinding_key: string;
|
|
||||||
}> {
|
|
||||||
return this.queueRequest({
|
|
||||||
op: "setup_refresh_planchet",
|
|
||||||
args: req,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async queueRequest(req: any): Promise<any> {
|
|
||||||
const p = openPromise<any>();
|
|
||||||
if (this.requests.length === 0) {
|
|
||||||
this.proc.ref();
|
|
||||||
}
|
|
||||||
this.requests.push({ req, p });
|
|
||||||
this.proc.stdin.write(JSON.stringify(req) + "\n");
|
|
||||||
return p.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
async eddsaVerify(req: {
|
|
||||||
msg: string;
|
|
||||||
sig: string;
|
|
||||||
pub: string;
|
|
||||||
}): Promise<{ valid: boolean }> {
|
|
||||||
return this.queueRequest({
|
|
||||||
op: "eddsa_verify",
|
|
||||||
args: req,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async eddsaSign(req: {
|
|
||||||
msg: string;
|
|
||||||
priv: string;
|
|
||||||
}): Promise<{ sig: string }> {
|
|
||||||
return this.queueRequest({
|
|
||||||
op: "eddsa_sign",
|
|
||||||
args: req,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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");
|
|
||||||
}
|
|
||||||
return new SynchronousCryptoWorker();
|
|
||||||
}
|
|
||||||
|
|
||||||
getConcurrency(): number {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Worker implementation that uses node subprocesses.
|
* Worker implementation that uses node subprocesses.
|
||||||
*/
|
*/
|
||||||
@ -157,14 +38,9 @@ export class SynchronousCryptoWorker {
|
|||||||
*/
|
*/
|
||||||
onerror: undefined | ((m: any) => void);
|
onerror: undefined | ((m: any) => void);
|
||||||
|
|
||||||
primitiveWorker: PrimitiveWorker;
|
constructor(private primitiveWorker?: PrimitiveWorker) {
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.onerror = undefined;
|
this.onerror = undefined;
|
||||||
this.onmessage = undefined;
|
this.onmessage = undefined;
|
||||||
if (process.env["TALER_WALLET_PRIMITIVE_WORKER"]) {
|
|
||||||
this.primitiveWorker = new MyPrimitiveWorker();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
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 {
|
||||||
|
PrimitiveWorker,
|
||||||
|
} from "./cryptoImplementation.js";
|
||||||
|
|
||||||
|
import { CryptoWorkerFactory } from "./cryptoApi.js";
|
||||||
|
import { CryptoWorker } from "./cryptoWorkerInterface.js";
|
||||||
|
|
||||||
|
import child_process from "child_process";
|
||||||
|
import type internal from "stream";
|
||||||
|
import { OpenedPromise, openPromise } from "../../index.js";
|
||||||
|
import { Logger } from "@gnu-taler/taler-util";
|
||||||
|
import { SynchronousCryptoWorker } from "./synchronousWorker.js";
|
||||||
|
|
||||||
|
const logger = new Logger("synchronousWorkerFactory.ts");
|
||||||
|
|
||||||
|
class MyPrimitiveWorker implements PrimitiveWorker {
|
||||||
|
proc: child_process.ChildProcessByStdio<
|
||||||
|
internal.Writable,
|
||||||
|
internal.Readable,
|
||||||
|
null
|
||||||
|
>;
|
||||||
|
requests: Array<{
|
||||||
|
p: OpenedPromise<any>;
|
||||||
|
req: any;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
const stdoutChunks: Buffer[] = [];
|
||||||
|
this.proc = child_process.spawn("taler-crypto-worker", {
|
||||||
|
//stdio: ["pipe", "pipe", "inherit"],
|
||||||
|
stdio: ["pipe", "pipe", "inherit"],
|
||||||
|
detached: true,
|
||||||
|
});
|
||||||
|
this.proc.on("close", (): void => {
|
||||||
|
logger.error("child process exited");
|
||||||
|
});
|
||||||
|
(this.proc.stdout as any).unref();
|
||||||
|
(this.proc.stdin as any).unref();
|
||||||
|
this.proc.unref();
|
||||||
|
|
||||||
|
this.proc.stdout.on("data", (x) => {
|
||||||
|
// console.log("got chunk", x.toString("utf-8"));
|
||||||
|
if (x instanceof Buffer) {
|
||||||
|
const nlIndex = x.indexOf("\n");
|
||||||
|
if (nlIndex >= 0) {
|
||||||
|
const before = x.slice(0, nlIndex);
|
||||||
|
const after = x.slice(nlIndex + 1);
|
||||||
|
stdoutChunks.push(after);
|
||||||
|
const str = Buffer.concat([...stdoutChunks, before]).toString(
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
const req = this.requests.shift();
|
||||||
|
if (!req) {
|
||||||
|
throw Error("request was undefined")
|
||||||
|
}
|
||||||
|
if (this.requests.length === 0) {
|
||||||
|
this.proc.unref();
|
||||||
|
}
|
||||||
|
//logger.info(`got response: ${str}`);
|
||||||
|
req.p.resolve(JSON.parse(str));
|
||||||
|
} else {
|
||||||
|
stdoutChunks.push(x);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw Error(`unexpected data chunk type (${typeof x})`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async setupRefreshPlanchet(req: {
|
||||||
|
transfer_secret: string;
|
||||||
|
coin_index: number;
|
||||||
|
}): Promise<{
|
||||||
|
coin_pub: string;
|
||||||
|
coin_priv: string;
|
||||||
|
blinding_key: string;
|
||||||
|
}> {
|
||||||
|
return this.queueRequest({
|
||||||
|
op: "setup_refresh_planchet",
|
||||||
|
args: req,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async queueRequest(req: any): Promise<any> {
|
||||||
|
const p = openPromise<any>();
|
||||||
|
if (this.requests.length === 0) {
|
||||||
|
this.proc.ref();
|
||||||
|
}
|
||||||
|
this.requests.push({ req, p });
|
||||||
|
this.proc.stdin.write(`${JSON.stringify(req)}\n`);
|
||||||
|
return p.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
async eddsaVerify(req: {
|
||||||
|
msg: string;
|
||||||
|
sig: string;
|
||||||
|
pub: string;
|
||||||
|
}): Promise<{ valid: boolean }> {
|
||||||
|
return this.queueRequest({
|
||||||
|
op: "eddsa_verify",
|
||||||
|
args: req,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async eddsaSign(req: {
|
||||||
|
msg: string;
|
||||||
|
priv: string;
|
||||||
|
}): Promise<{ sig: string }> {
|
||||||
|
return this.queueRequest({
|
||||||
|
op: "eddsa_sign",
|
||||||
|
args: req,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
let primitiveWorker;
|
||||||
|
if (process.env["TALER_WALLET_PRIMITIVE_WORKER"]) {
|
||||||
|
primitiveWorker = new MyPrimitiveWorker();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SynchronousCryptoWorker(primitiveWorker);
|
||||||
|
}
|
||||||
|
|
||||||
|
getConcurrency(): number {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
@ -36,6 +36,7 @@ export * from "./db-utils.js";
|
|||||||
export { CryptoImplementation } from "./crypto/workers/cryptoImplementation.js";
|
export { CryptoImplementation } from "./crypto/workers/cryptoImplementation.js";
|
||||||
export type { CryptoWorker } from "./crypto/workers/cryptoWorkerInterface.js";
|
export type { CryptoWorker } from "./crypto/workers/cryptoWorkerInterface.js";
|
||||||
export { CryptoWorkerFactory, CryptoApi } from "./crypto/workers/cryptoApi.js";
|
export { CryptoWorkerFactory, CryptoApi } from "./crypto/workers/cryptoApi.js";
|
||||||
|
export { SynchronousCryptoWorker } from "./crypto/workers/synchronousWorker.js"
|
||||||
|
|
||||||
export * from "./pending-types.js";
|
export * from "./pending-types.js";
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user