Merge pull request #121 from ChainSafe/cayman/esm

ESM Support
This commit is contained in:
Cayman 2022-05-05 15:04:19 +02:00 committed by GitHub
commit a112b21347
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 1594 additions and 2849 deletions

View File

@ -13,13 +13,15 @@ module.exports = {
}, },
parser: "@typescript-eslint/parser", parser: "@typescript-eslint/parser",
parserOptions: { parserOptions: {
ecmaVersion: 10, ecmaVersion: "latest",
project: "./tsconfig.json" project: "./tsconfig.json",
sourceType: "module",
}, },
plugins: [ plugins: [
"@typescript-eslint", "@typescript-eslint",
"eslint-plugin-import", "eslint-plugin-import",
"prettier" "prettier",
"@chainsafe/eslint-plugin-node"
], ],
extends: [ extends: [
"eslint:recommended", "eslint:recommended",
@ -33,13 +35,13 @@ module.exports = {
"prettier/prettier": "error", "prettier/prettier": "error",
//doesnt work, it reports false errors //doesnt work, it reports false errors
"constructor-super": "off", "constructor-super": "off",
"@typescript-eslint/class-name-casing": "error", //"@typescript-eslint/class-name-casing": "error",
"@typescript-eslint/explicit-function-return-type": ["error", { "@typescript-eslint/explicit-function-return-type": ["error", {
"allowExpressions": true "allowExpressions": true
}], }],
"@typescript-eslint/func-call-spacing": "error", "@typescript-eslint/func-call-spacing": "error",
"@typescript-eslint/indent": ["error", 2], "@typescript-eslint/indent": ["error", 2],
"@typescript-eslint/interface-name-prefix": ["error", "always"], //"@typescript-eslint/interface-name-prefix": ["error", "always"],
"@typescript-eslint/member-ordering": "error", "@typescript-eslint/member-ordering": "error",
"@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-require-imports": "error", "@typescript-eslint/no-require-imports": "error",
@ -47,7 +49,7 @@ module.exports = {
"varsIgnorePattern": "^_", "varsIgnorePattern": "^_",
"argsIgnorePattern": "^_", "argsIgnorePattern": "^_",
}], }],
"@typescript-eslint/ban-ts-ignore": "warn", "@typescript-eslint/ban-ts-comment": "warn",
"@typescript-eslint/no-use-before-define": "off", "@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/semi": "error", "@typescript-eslint/semi": "error",
"@typescript-eslint/type-annotation-spacing": "error", "@typescript-eslint/type-annotation-spacing": "error",
@ -76,7 +78,15 @@ module.exports = {
"no-prototype-builtins": 0, "no-prototype-builtins": 0,
"prefer-const": "error", "prefer-const": "error",
"quotes": ["error", "double"], "quotes": ["error", "double"],
"semi": "off" "semi": "off",
"@chainsafe/node/file-extension-in-import": [
"error",
"always",
{
"esm": true
}
],
"import/no-unresolved": "off",
}, },
"overrides": [ "overrides": [
{ {

View File

@ -9,7 +9,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
node: [12, 14] node: [14, 16]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-node@v1 - uses: actions/setup-node@v1

View File

@ -1,2 +1,6 @@
extension: ["ts"]
colors: true colors: true
require: ts-node/register node-option:
- "experimental-specifier-resolution=node"
- "loader=ts-node/esm"

View File

@ -2,8 +2,8 @@
[![codecov](https://codecov.io/gh/ChainSafe/lodestar/branch/master/graph/badge.svg)](https://codecov.io/gh/ChainSafe/lodestar) [![codecov](https://codecov.io/gh/ChainSafe/lodestar/branch/master/graph/badge.svg)](https://codecov.io/gh/ChainSafe/lodestar)
![ETH2.0_Spec_Version 1.0.0](https://img.shields.io/badge/ETH2.0_Spec_Version-1.0.0-2e86c1.svg) ![ETH2.0_Spec_Version 1.0.0](https://img.shields.io/badge/ETH2.0_Spec_Version-1.0.0-2e86c1.svg)
![ES Version](https://img.shields.io/badge/ES-2017-yellow) ![ES Version](https://img.shields.io/badge/ES-2022-yellow)
![Node Version](https://img.shields.io/badge/node-12.x-green) ![Node Version](https://img.shields.io/badge/node-14.8-green)
Javascript library for BLS (Boneh-Lynn-Shacham) signatures and signature aggregation, tailored for use in Eth2. Javascript library for BLS (Boneh-Lynn-Shacham) signatures and signature aggregation, tailored for use in Eth2.
@ -19,10 +19,10 @@ To use native bindings you must install peer dependency `@chainsafe/blst`
yarn add @chainsafe/bls @chainsafe/blst yarn add @chainsafe/bls @chainsafe/blst
``` ```
You must initialize the library once in your application before using it. The result is cached and use across all your imports By default, native bindings will be used if in NodeJS and they are installed. A WASM implementation ("herumi") is used as a fallback in case any error occurs.
```ts ```ts
import {init, SecretKey, secretKeyToPublicKey, sign, verify} from "@chainsafe/bls"; import {SecretKey, secretKeyToPublicKey, sign, verify} from "@chainsafe/bls";
(async () => { (async () => {
await init("herumi"); await init("herumi");
@ -45,48 +45,52 @@ import {init, SecretKey, secretKeyToPublicKey, sign, verify} from "@chainsafe/bl
### Browser ### Browser
If you are in the browser, import from `/browser` to import directly the WASM version If you are in the browser, import from `/herumi` to explicitly import the WASM version
```ts ```ts
import bls from "@chainsafe/bls/browser"; import bls from "@chainsafe/bls/herumi";
``` ```
### Native bindings only ### Native bindings only
If you are in NodeJS, import from `/node` to skip browser specific code. Also install peer dependency `@chainsafe/blst` which has the native bindings If you are in NodeJS, import from `/blst-native` to explicitly import the native bindings. Also install peer dependency `@chainsafe/blst` which has the native bindings
```bash ```bash
yarn add @chainsafe/bls @chainsafe/blst yarn add @chainsafe/bls @chainsafe/blst
``` ```
```ts ```ts
import bls from "@chainsafe/bls/node"; import bls from "@chainsafe/bls/blst-native";
``` ```
### Native bindings + WASM fallback ### Get implementation at runtime
If you want to offer a fallback in NodeJS, first try to load native bindings and then fallback to WASM. Also install peer dependency `@chainsafe/blst` which has the native bindings If you need to get a bls implementation at runtime, import from `/getImplementation`.
```bash
yarn add @chainsafe/bls @chainsafe/blst
```
```ts ```ts
import {init} from "@chainsafe/bls"; import {getImplementation} from "@chainsafe/bls/getImplementation";
try { const bls = await getImplementation("herumi");
await init("blst-native"); ```
} catch (e) {
### Switchable singleton
If you need a singleton that is switchable at runtime (the default behavior in <=v6), import from `/switchable`.
```ts
import bls, {init} from "@chainsafe/bls/switchable";
// here `bls` is uninitialized
await init("herumi"); await init("herumi");
console.warn("Using WASM"); // here `bls` is initialized
} // now other modules can `import bls from "@chainsafe/bls/switchable"` and it will be initialized
``` ```
The API is identical for all implementations. The API is identical for all implementations.
## Benchmarks ## Benchmarks
- `blst`: [src/blst](src/blst) (node.js-only, bindings to C via node-gyp) - `blst`: [src/blst-native](src/blst-native) (node.js-only, bindings to C via node-gyp)
- `herumi`: [src/herumi](src/herumi) (node.js & browser, wasm) - `herumi`: [src/herumi](src/herumi) (node.js & browser, wasm)
- `noble`: [noble-bls12-381](https://github.com/paulmillr/noble-bls12-381) (node.js & browser, pure JS) - `noble`: [noble-bls12-381](https://github.com/paulmillr/noble-bls12-381) (node.js & browser, pure JS)

View File

@ -1,7 +1,7 @@
import {runBenchmark} from "./runner"; import {runBenchmark} from "./runner.js";
import {runForAllImplementations} from "../test/switch"; import {runForAllImplementations} from "../test/switch.js";
import {PublicKey, Signature} from "../src/interface"; import {PublicKey, Signature} from "../src/types.js";
import {aggCount} from "./params"; import {aggCount} from "./params.js";
(async function () { (async function () {
await runForAllImplementations(async (bls, implementation) => { await runForAllImplementations(async (bls, implementation) => {

View File

@ -1,8 +1,8 @@
import {runBenchmark} from "./runner"; import {runBenchmark} from "./runner.js";
import {range, randomMessage} from "../test/util"; import {range, randomMessage} from "../test/util.js";
import {generateRandomSecretKey} from "@chainsafe/bls-keygen"; import {generateRandomSecretKey} from "@chainsafe/bls-keygen";
import * as noble from "noble-bls12-381"; import * as noble from "noble-bls12-381";
import {aggCount, runsNoble} from "./params"; import {aggCount, runsNoble} from "./params.js";
(async function () { (async function () {
{ {

View File

@ -1,11 +1,12 @@
{ {
"name": "bls-libs-benchmark", "name": "bls-libs-benchmark",
"version": "1.0.0", "version": "1.0.0",
"main": "index.js", "type": "module",
"exports": "./index.js",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"benchmark": "ts-node index", "benchmark": "ts-node-esm index",
"benchmark:all": "ts-node index && ts-node noble && ts-node verifyMultipleSignaturesSavings" "benchmark:all": "ts-node-esm index && ts-node-esm noble && ts-node-esm verifyMultipleSignaturesSavings"
}, },
"dependencies": { "dependencies": {
"noble-bls12-381": "^0.7.2" "noble-bls12-381": "^0.7.2"

View File

@ -1,5 +1,5 @@
import {runForAllImplementations} from "../test/switch"; import {runForAllImplementations} from "../test/switch.js";
import {range, randomMessage} from "../test/util"; import {range, randomMessage} from "../test/util.js";
(async function () { (async function () {
console.log("verifyMultipleSignatures savings"); console.log("verifyMultipleSignatures savings");

1
blst-native.d.ts vendored
View File

@ -1 +0,0 @@
export * from "./lib/blst";

View File

@ -1 +0,0 @@
module.exports = require("./lib/blst");

1
browser.d.ts vendored
View File

@ -1 +0,0 @@
export * from "./lib/herumi";

View File

@ -1 +0,0 @@
export * from "./lib/herumi";

1
herumi.d.ts vendored
View File

@ -1 +0,0 @@
export * from "./lib/herumi";

View File

@ -1 +0,0 @@
module.exports = require("./lib/herumi");

View File

@ -1,20 +1,29 @@
// eslint-disable-next-line @typescript-eslint/no-require-imports // eslint-disable-next-line @typescript-eslint/no-require-imports
const webpackConfig = require("./webpack.config"); const webpackConfig = require("./webpack.config.cjs");
module.exports = function (config) { module.exports = function (config) {
config.set({ config.set({
basePath: "", basePath: "",
frameworks: ["mocha", "chai"], frameworks: [
files: ["test/unit-web/run-web-implementation.test.ts", "test/unit/index-named-exports.test.ts"], "webpack",
"mocha",
"chai",
],
files: [
"test/unit-web/run-web-implementation.test.ts",
"test/unit/index-named-exports.test.ts",
],
exclude: [], exclude: [],
preprocessors: { preprocessors: {
"test/**/*.ts": ["webpack"], "test/**/*.ts": ["webpack"],
}, },
webpack: { webpack: {
mode: "production", mode: "production",
node: webpackConfig.node,
module: webpackConfig.module, module: webpackConfig.module,
resolve: webpackConfig.resolve, resolve: webpackConfig.resolve,
experiments: webpackConfig.experiments,
optimization: webpackConfig.optimization,
stats: {warnings:false},
}, },
reporters: ["spec"], reporters: ["spec"],

1
node.d.ts vendored
View File

@ -1 +0,0 @@
export * from "./lib/blst";

View File

@ -1 +0,0 @@
module.exports = require("./lib/blst");

View File

@ -2,10 +2,42 @@
"name": "@chainsafe/bls", "name": "@chainsafe/bls",
"version": "6.0.3", "version": "6.0.3",
"description": "Implementation of bls signature verification for ethereum 2.0", "description": "Implementation of bls signature verification for ethereum 2.0",
"main": "lib/index.js", "engines": {
"node": ">=14.8.0"
},
"type": "module",
"exports": {
".": {
"import": "./lib/index.js"
},
"./types": {
"import": "./lib/types.js"
},
"./getImplementation": {
"import": "./lib/getImplementation.js"
},
"./switchable": {
"import": "./lib/switchable.js"
},
"./blst-native": {
"import": "./lib/blst-native/index.js"
},
"./herumi": {
"import": "./lib/herumi/index.js"
}
},
"types": "lib/index.d.ts", "types": "lib/index.d.ts",
"module": "./browser", "typesVersions": {
"browser": "./browser", "*": {
"*": [
"*",
"lib/*",
"lib/*/index"
]
}
},
"module": "./lib/index.js",
"browser": "./lib/herumi.js",
"homepage": "https://github.com/chainsafe/bls", "homepage": "https://github.com/chainsafe/bls",
"author": "ChainSafe Systems", "author": "ChainSafe Systems",
"license": "Apache-2.0", "license": "Apache-2.0",
@ -24,18 +56,19 @@
], ],
"scripts": { "scripts": {
"clean": "rm -rf lib && rm -rf dist && rm -f tsconfig.tsbuildinfo", "clean": "rm -rf lib && rm -rf dist && rm -f tsconfig.tsbuildinfo",
"check-build": "node -e \"(async function() { await import('./lib/index.js') })()\"",
"build": "tsc --incremental --project tsconfig.build.json", "build": "tsc --incremental --project tsconfig.build.json",
"lint": "eslint --color --ext .ts src/ test/", "lint": "eslint --color --ext .ts src/ test/",
"lint:fix": "yarn run lint --fix", "lint:fix": "yarn run lint --fix",
"prepublishOnly": "yarn build", "prepublishOnly": "yarn build",
"test:web": "karma start", "test:web": "karma start karma.conf.cjs",
"test:unit": "mocha 'test/unit/**/*.test.ts'", "test:unit": "mocha 'test/unit/**/*.test.ts'",
"test:coverage": "nyc --cache-dir .nyc_output/.cache -r lcov -e .ts mocha 'test/unit/**/*.test.ts' && nyc report", "test:coverage": "nyc --cache-dir .nyc_output/.cache -r lcov -e .ts mocha 'test/unit/**/*.test.ts' && nyc report",
"test:spec": "mocha 'test/spec/**/*.test.ts'", "test:spec": "mocha 'test/spec/**/*.test.ts'",
"test": "yarn run test:unit && yarn run test:spec", "test": "yarn run test:unit && yarn run test:spec",
"download-test-cases": "ts-node test/downloadSpecTests.ts", "download-test-cases": "ts-node-esm test/downloadSpecTests.ts",
"coverage": "codecov -F bls", "coverage": "codecov -F bls",
"benchmark": "ts-node benchmark", "benchmark": "ts-node-esm benchmark",
"benchmark:all": "cd benchmark && yarn install && yarn benchmark:all" "benchmark:all": "cd benchmark && yarn install && yarn benchmark:all"
}, },
"dependencies": { "dependencies": {
@ -45,35 +78,38 @@
}, },
"devDependencies": { "devDependencies": {
"@chainsafe/blst": "^0.2.0", "@chainsafe/blst": "^0.2.0",
"@chainsafe/eslint-plugin-node": "^11.2.3",
"@chainsafe/lodestar-spec-test-util": "^0.18.0", "@chainsafe/lodestar-spec-test-util": "^0.18.0",
"@chainsafe/threads": "^1.9.0",
"@types/chai": "^4.2.9", "@types/chai": "^4.2.9",
"@types/mocha": "^8.0.4", "@types/mocha": "^8.0.4",
"@types/randombytes": "^2.0.0", "@types/randombytes": "^2.0.0",
"@typescript-eslint/eslint-plugin": "^2.20.0", "@typescript-eslint/eslint-plugin": "^4.31.1",
"@typescript-eslint/parser": "^2.20.0", "@typescript-eslint/parser": "^4.31.1",
"chai": "^4.2.0", "buffer": "^6.0.3",
"eslint": "^6.8.0", "chai": "^4.3.6",
"eslint": "^7.14.0",
"eslint-plugin-import": "^2.20.1", "eslint-plugin-import": "^2.20.1",
"eslint-plugin-prettier": "^3.1.4", "eslint-plugin-prettier": "^3.1.4",
"karma": "^6.3.16", "karma": "^6.3.18",
"karma-chai": "^0.1.0", "karma-chai": "^0.1.0",
"karma-chrome-launcher": "^3.1.0", "karma-chrome-launcher": "^3.1.0",
"karma-cli": "^2.0.0", "karma-cli": "^2.0.0",
"karma-mocha": "^1.3.0", "karma-mocha": "^2.0.1",
"karma-spec-reporter": "^0.0.32", "karma-spec-reporter": "^0.0.32",
"karma-webpack": "^4.0.2", "karma-webpack": "^5.0.0",
"mocha": "^8.2.1", "mocha": "^9.2.2",
"nyc": "^15.0.0", "nyc": "^15.0.0",
"prettier": "^2.1.2", "prettier": "^2.1.2",
"threads": "^1.6.3", "resolve-typescript-plugin": "^1.2.0",
"ts-loader": "^6.2.1", "ts-loader": "^9.2.8",
"ts-node": "^8.6.2", "ts-node": "^10.7.0",
"typescript": "^3.7.5", "typescript": "^4.6.3",
"webpack": "^4.30.0", "webpack": "^5.72.0",
"webpack-cli": "^3.3.2" "webpack-cli": "^4.9.2"
}, },
"resolutions": { "resolutions": {
"mocha": "^8.2.1", "mocha": "^9.2.2",
"v8-profiler-next": "1.3.0" "v8-profiler-next": "1.3.0"
}, },
"peerDependencies": { "peerDependencies": {

View File

@ -1,14 +0,0 @@
const {init} = require("./lib");
// -----------------------------------------
// To be used in NodeJS testing environments
// node -r @chainsafe/bls/register
// -----------------------------------------
// blst-native initialization is syncronous
// Initialize bls here instead of in before() so it's available inside describe() blocks
init("blst-native").catch((e) => {
// eslint-disable-next-line no-console
console.error(e);
process.exit(1);
});

19
src/blst-native/index.ts Normal file
View File

@ -0,0 +1,19 @@
import {SecretKey} from "./secretKey.js";
import {PublicKey} from "./publicKey.js";
import {Signature} from "./signature.js";
import {IBls} from "../types.js";
import {functionalInterfaceFactory} from "../functional.js";
export * from "../constants.js";
export {SecretKey, PublicKey, Signature};
export const bls: IBls = {
implementation: "blst-native",
SecretKey,
PublicKey,
Signature,
...functionalInterfaceFactory({SecretKey, PublicKey, Signature}),
};
export default bls;

View File

@ -1,7 +1,7 @@
import * as blst from "@chainsafe/blst"; import * as blst from "@chainsafe/blst";
import {EmptyAggregateError} from "../errors"; import {EmptyAggregateError} from "../errors.js";
import {bytesToHex, hexToBytes} from "../helpers"; import {bytesToHex, hexToBytes} from "../helpers/index.js";
import {PointFormat, PublicKey as IPublicKey} from "../interface"; import {PointFormat, PublicKey as IPublicKey} from "../types.js";
export class PublicKey extends blst.PublicKey implements IPublicKey { export class PublicKey extends blst.PublicKey implements IPublicKey {
constructor(value: ConstructorParameters<typeof blst.PublicKey>[0]) { constructor(value: ConstructorParameters<typeof blst.PublicKey>[0]) {

View File

@ -1,10 +1,10 @@
import * as blst from "@chainsafe/blst"; import * as blst from "@chainsafe/blst";
import {bytesToHex, hexToBytes, isZeroUint8Array, randomBytes} from "../helpers"; import {bytesToHex, hexToBytes, isZeroUint8Array, randomBytes} from "../helpers/index.js";
import {SECRET_KEY_LENGTH} from "../constants"; import {SECRET_KEY_LENGTH} from "../constants.js";
import {SecretKey as ISecretKey} from "../interface"; import {SecretKey as ISecretKey} from "../types.js";
import {PublicKey} from "./publicKey"; import {PublicKey} from "./publicKey.js";
import {Signature} from "./signature"; import {Signature} from "./signature.js";
import {ZeroSecretKeyError} from "../errors"; import {ZeroSecretKeyError} from "../errors.js";
export class SecretKey implements ISecretKey { export class SecretKey implements ISecretKey {
readonly value: blst.SecretKey; readonly value: blst.SecretKey;

View File

@ -1,8 +1,8 @@
import * as blst from "@chainsafe/blst"; import * as blst from "@chainsafe/blst";
import {bytesToHex, hexToBytes} from "../helpers"; import {bytesToHex, hexToBytes} from "../helpers/index.js";
import {PointFormat, Signature as ISignature} from "../interface"; import {PointFormat, Signature as ISignature} from "../types.js";
import {PublicKey} from "./publicKey"; import {PublicKey} from "./publicKey.js";
import {EmptyAggregateError, ZeroSignatureError} from "../errors"; import {EmptyAggregateError, ZeroSignatureError} from "../errors.js";
export class Signature extends blst.Signature implements ISignature { export class Signature extends blst.Signature implements ISignature {
constructor(value: ConstructorParameters<typeof blst.Signature>[0]) { constructor(value: ConstructorParameters<typeof blst.Signature>[0]) {

View File

@ -1,28 +0,0 @@
import {SecretKey} from "./secretKey";
import {PublicKey} from "./publicKey";
import {Signature} from "./signature";
import {IBls} from "../interface";
import {functionalInterfaceFactory} from "../functional";
export * from "../constants";
export {SecretKey, PublicKey, Signature};
export async function init(): Promise<void> {
// Native bindings require no init() call
}
export function destroy(): void {
// Native bindings require no destroy() call
}
export const bls: IBls = {
implementation: "blst-native",
SecretKey,
PublicKey,
Signature,
...functionalInterfaceFactory({SecretKey, PublicKey, Signature}),
init,
destroy,
};
export default bls;

View File

@ -1,9 +1,10 @@
import {IBls} from "./interface"; import {IBls} from "./types.js";
import {validateBytes} from "./helpers"; import {validateBytes} from "./helpers/index.js";
import {NotInitializedError} from "./errors"; import {NotInitializedError} from "./errors.js";
// Returned type is enforced at each implementation's index // Returned type is enforced at each implementation's index
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type // eslint-disable-next-line max-len
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type,@typescript-eslint/explicit-module-boundary-types
export function functionalInterfaceFactory({ export function functionalInterfaceFactory({
SecretKey, SecretKey,
PublicKey, PublicKey,

22
src/getImplementation.ts Normal file
View File

@ -0,0 +1,22 @@
import type {IBls, Implementation} from "./types.js";
// Thanks https://github.com/iliakan/detect-node/blob/master/index.esm.js
const isNode = Object.prototype.toString.call(typeof process !== "undefined" ? process : 0) === "[object process]";
export async function getImplementation(impl: Implementation = "herumi"): Promise<IBls> {
switch (impl) {
case "herumi": {
return (await import("./herumi/index.js")).bls;
}
case "blst-native":
// Lazy import native bindings to prevent automatically importing binding.node files
if (!isNode) {
throw Error("blst-native is only supported in NodeJS");
}
return (await import("./blst-native/index.js")).bls;
default:
throw new Error(`Unsupported implementation - ${impl}`);
}
}

View File

@ -1,2 +1,2 @@
export * from "./hex"; export * from "./hex.js";
export * from "./utils"; export * from "./utils.js";

View File

@ -1,6 +1,6 @@
/* eslint-disable require-atomic-updates */ /* eslint-disable require-atomic-updates */
import bls from "bls-eth-wasm"; import bls from "bls-eth-wasm";
import {NotInitializedError} from "../errors"; import {NotInitializedError} from "../errors.js";
type Bls = typeof bls; type Bls = typeof bls;
let blsGlobal: Bls | null = null; let blsGlobal: Bls | null = null;
@ -8,7 +8,6 @@ let blsGlobalPromise: Promise<void> | null = null;
// Patch to fix multiVerify() calls on a browser with polyfilled NodeJS crypto // Patch to fix multiVerify() calls on a browser with polyfilled NodeJS crypto
declare global { declare global {
// eslint-disable-next-line @typescript-eslint/interface-name-prefix
interface Window { interface Window {
msCrypto: typeof window["crypto"]; msCrypto: typeof window["crypto"];
} }

View File

@ -1,10 +1,13 @@
import {SecretKey} from "./secretKey"; import {SecretKey} from "./secretKey.js";
import {PublicKey} from "./publicKey"; import {PublicKey} from "./publicKey.js";
import {Signature} from "./signature"; import {Signature} from "./signature.js";
import {init, destroy} from "./context"; import {init, destroy} from "./context.js";
import {IBls} from "../interface"; import {IBls} from "../types.js";
import {functionalInterfaceFactory} from "../functional"; import {functionalInterfaceFactory} from "../functional.js";
export * from "../constants";
await init();
export * from "../constants.js";
export {SecretKey, PublicKey, Signature, init, destroy}; export {SecretKey, PublicKey, Signature, init, destroy};
@ -15,8 +18,6 @@ export const bls: IBls = {
Signature, Signature,
...functionalInterfaceFactory({SecretKey, PublicKey, Signature}), ...functionalInterfaceFactory({SecretKey, PublicKey, Signature}),
init,
destroy,
}; };
export default bls; export default bls;

View File

@ -1,9 +1,9 @@
import {PublicKeyType} from "bls-eth-wasm"; import type {PublicKeyType} from "bls-eth-wasm";
import {getContext} from "./context"; import {getContext} from "./context.js";
import {bytesToHex, hexToBytes, isZeroUint8Array} from "../helpers"; import {bytesToHex, hexToBytes, isZeroUint8Array} from "../helpers/index.js";
import {PointFormat, PublicKey as IPublicKey} from "../interface"; import {PointFormat, PublicKey as IPublicKey} from "../types.js";
import {EmptyAggregateError, InvalidLengthError, ZeroPublicKeyError} from "../errors"; import {EmptyAggregateError, InvalidLengthError, ZeroPublicKeyError} from "../errors.js";
import {PUBLIC_KEY_LENGTH_COMPRESSED, PUBLIC_KEY_LENGTH_UNCOMPRESSED} from "../constants"; import {PUBLIC_KEY_LENGTH_COMPRESSED, PUBLIC_KEY_LENGTH_UNCOMPRESSED} from "../constants.js";
export class PublicKey implements IPublicKey { export class PublicKey implements IPublicKey {
readonly value: PublicKeyType; readonly value: PublicKeyType;

View File

@ -1,12 +1,12 @@
import {SecretKeyType} from "bls-eth-wasm"; import type {SecretKeyType} from "bls-eth-wasm";
import {generateRandomSecretKey} from "@chainsafe/bls-keygen"; import {generateRandomSecretKey} from "@chainsafe/bls-keygen";
import {SECRET_KEY_LENGTH} from "../constants"; import {SECRET_KEY_LENGTH} from "../constants.js";
import {getContext} from "./context"; import {getContext} from "./context.js";
import {PublicKey} from "./publicKey"; import {PublicKey} from "./publicKey.js";
import {Signature} from "./signature"; import {Signature} from "./signature.js";
import {bytesToHex, hexToBytes} from "../helpers"; import {bytesToHex, hexToBytes} from "../helpers/index.js";
import {SecretKey as ISecretKey} from "../interface"; import {SecretKey as ISecretKey} from "../types.js";
import {InvalidLengthError, ZeroSecretKeyError} from "../errors"; import {InvalidLengthError, ZeroSecretKeyError} from "../errors.js";
export class SecretKey implements ISecretKey { export class SecretKey implements ISecretKey {
readonly value: SecretKeyType; readonly value: SecretKeyType;
@ -35,7 +35,7 @@ export class SecretKey implements ISecretKey {
} }
static fromKeygen(entropy?: Uint8Array): SecretKey { static fromKeygen(entropy?: Uint8Array): SecretKey {
const sk = generateRandomSecretKey(entropy && Buffer.from(entropy)); const sk = generateRandomSecretKey(entropy);
return this.fromBytes(sk); return this.fromBytes(sk);
} }

View File

@ -1,10 +1,10 @@
import {SignatureType, multiVerify} from "bls-eth-wasm"; import type {SignatureType} from "bls-eth-wasm";
import {getContext} from "./context"; import {getContext} from "./context.js";
import {PublicKey} from "./publicKey"; import {PublicKey} from "./publicKey.js";
import {bytesToHex, concatUint8Arrays, hexToBytes, isZeroUint8Array} from "../helpers"; import {bytesToHex, concatUint8Arrays, hexToBytes, isZeroUint8Array} from "../helpers/index.js";
import {PointFormat, Signature as ISignature, CoordType} from "../interface"; import {PointFormat, Signature as ISignature, CoordType} from "../types.js";
import {EmptyAggregateError, InvalidLengthError, InvalidOrderError} from "../errors"; import {EmptyAggregateError, InvalidLengthError, InvalidOrderError} from "../errors.js";
import {SIGNATURE_LENGTH_COMPRESSED, SIGNATURE_LENGTH_UNCOMPRESSED} from "../constants"; import {SIGNATURE_LENGTH_COMPRESSED, SIGNATURE_LENGTH_UNCOMPRESSED} from "../constants.js";
export class Signature implements ISignature { export class Signature implements ISignature {
readonly value: SignatureType; readonly value: SignatureType;
@ -53,7 +53,8 @@ export class Signature implements ISignature {
} }
static verifyMultipleSignatures(sets: {publicKey: PublicKey; message: Uint8Array; signature: Signature}[]): boolean { static verifyMultipleSignatures(sets: {publicKey: PublicKey; message: Uint8Array; signature: Signature}[]): boolean {
return multiVerify( const context = getContext();
return context.multiVerify(
sets.map((s) => s.publicKey.value), sets.map((s) => s.publicKey.value),
sets.map((s) => s.signature.value), sets.map((s) => s.signature.value),
sets.map((s) => s.message) sets.map((s) => s.message)

View File

@ -1,4 +1,4 @@
import {bls} from "./index"; import {bls} from "./index.js";
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
(function (window: any) { (function (window: any) {

View File

@ -1,47 +1,14 @@
import {IBls} from "./interface"; import type {IBls} from "./types.js";
import {bls as blsHerumi} from "./herumi"; import {getImplementation} from "./getImplementation.js";
export type Implementation = "herumi" | "blst-native"; // Thanks https://github.com/iliakan/detect-node/blob/master/index.esm.js
const isNode = Object.prototype.toString.call(typeof process !== "undefined" ? process : 0) === "[object process]";
export * from "./interface"; let bls: IBls;
try {
bls = await getImplementation(isNode ? "blst-native" : "herumi");
} catch (e) {
bls = await getImplementation("herumi");
}
// TODO: Use a Proxy for example to throw an error if it's not initialized yet
export const bls: IBls = {} as IBls;
export default bls; export default bls;
async function getImplementation(impl: Implementation = "herumi"): Promise<IBls> {
switch (impl) {
case "herumi":
await blsHerumi.init();
return blsHerumi;
case "blst-native":
// Lazy import native bindings to prevent automatically importing binding.node files
if (typeof require !== "function") {
throw Error("blst-native is only supported in NodeJS");
}
// eslint-disable-next-line @typescript-eslint/no-require-imports
return require("./blst").bls;
default:
throw new Error(`Unsupported implementation - ${impl}`);
}
}
export async function init(impl: Implementation): Promise<void> {
// Using Object.assign instead of just bls = getImplementation()
// because otherwise the default import breaks. The reference is lost
// and the imported object is still undefined after calling init()
const blsImpl = await getImplementation(impl);
Object.assign(bls, blsImpl);
Object.assign(exports, blsImpl);
}
// Proxy named exports, will get set by `Object.assign(exports, blsImpl)`
export declare let sign: IBls["sign"];
export declare let aggregateSignatures: IBls["aggregateSignatures"];
export declare let aggregatePublicKeys: IBls["aggregatePublicKeys"];
export declare let verify: IBls["verify"];
export declare let verifyAggregate: IBls["verifyAggregate"];
export declare let verifyMultiple: IBls["verifyMultiple"];
export declare let secretKeyToPublicKey: IBls["secretKeyToPublicKey"];

14
src/switchable.ts Normal file
View File

@ -0,0 +1,14 @@
import type {IBls, Implementation} from "./types.js";
import {getImplementation} from "./getImplementation.js";
// TODO: Use a Proxy for example to throw an error if it's not initialized yet
const bls: IBls = {} as IBls;
export default bls;
export async function init(impl: Implementation): Promise<void> {
// Using Object.assign instead of just bls = getImplementation()
// because otherwise the default import breaks. The reference is lost
// and the imported object is still undefined after calling init()
const blsImpl = await getImplementation(impl);
Object.assign(bls, blsImpl);
}

View File

@ -1,8 +1,8 @@
export interface IBls { export interface IBls {
implementation: Implementation; implementation: Implementation;
SecretKey: Omit<typeof SecretKey, "prototype">; SecretKey: typeof SecretKey;
PublicKey: Omit<typeof PublicKey, "prototype">; PublicKey: typeof PublicKey;
Signature: Omit<typeof Signature, "prototype">; Signature: typeof Signature;
sign(secretKey: Uint8Array, message: Uint8Array): Uint8Array; sign(secretKey: Uint8Array, message: Uint8Array): Uint8Array;
aggregatePublicKeys(publicKeys: Uint8Array[]): Uint8Array; aggregatePublicKeys(publicKeys: Uint8Array[]): Uint8Array;
@ -12,12 +12,11 @@ export interface IBls {
verifyMultiple(publicKeys: Uint8Array[], messages: Uint8Array[], signature: Uint8Array): boolean; verifyMultiple(publicKeys: Uint8Array[], messages: Uint8Array[], signature: Uint8Array): boolean;
verifyMultipleSignatures(sets: {publicKey: Uint8Array; message: Uint8Array; signature: Uint8Array}[]): boolean; verifyMultipleSignatures(sets: {publicKey: Uint8Array; message: Uint8Array; signature: Uint8Array}[]): boolean;
secretKeyToPublicKey(secretKey: Uint8Array): Uint8Array; secretKeyToPublicKey(secretKey: Uint8Array): Uint8Array;
init(): Promise<void>;
destroy(): void;
} }
export declare class SecretKey { export declare class SecretKey {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private constructor(...value: any);
static fromBytes(bytes: Uint8Array): SecretKey; static fromBytes(bytes: Uint8Array): SecretKey;
static fromHex(hex: string): SecretKey; static fromHex(hex: string): SecretKey;
static fromKeygen(entropy?: Uint8Array): SecretKey; static fromKeygen(entropy?: Uint8Array): SecretKey;
@ -28,6 +27,8 @@ export declare class SecretKey {
} }
export declare class PublicKey { export declare class PublicKey {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private constructor(...value: any);
/** @param type Only for impl `blst-native`. Defaults to `CoordType.jacobian` */ /** @param type Only for impl `blst-native`. Defaults to `CoordType.jacobian` */
static fromBytes(bytes: Uint8Array, type?: CoordType, validate?: boolean): PublicKey; static fromBytes(bytes: Uint8Array, type?: CoordType, validate?: boolean): PublicKey;
static fromHex(hex: string): PublicKey; static fromHex(hex: string): PublicKey;
@ -38,6 +39,8 @@ export declare class PublicKey {
} }
export declare class Signature { export declare class Signature {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private constructor(...value: any);
/** @param type Only for impl `blst-native`. Defaults to `CoordType.affine` /** @param type Only for impl `blst-native`. Defaults to `CoordType.affine`
* @param validate When using `herumi` implementation, signature validation is always on regardless of this flag. */ * @param validate When using `herumi` implementation, signature validation is always on regardless of this flag. */
static fromBytes(bytes: Uint8Array, type?: CoordType, validate?: boolean): Signature; static fromBytes(bytes: Uint8Array, type?: CoordType, validate?: boolean): Signature;

View File

@ -1,5 +1,5 @@
import {downloadTests} from "@chainsafe/lodestar-spec-test-util"; import {downloadTests} from "@chainsafe/lodestar-spec-test-util";
import {SPEC_TEST_VERSION, SPEC_TESTS_DIR, SPEC_TEST_TO_DOWNLOAD} from "./params"; import {SPEC_TEST_VERSION, SPEC_TESTS_DIR, SPEC_TEST_TO_DOWNLOAD} from "./params.js";
/* eslint-disable no-console */ /* eslint-disable no-console */

View File

@ -1,5 +1,8 @@
import path from "path"; import path from "path";
import {fileURLToPath} from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export const SPEC_TEST_VERSION = "v1.0.0"; export const SPEC_TEST_VERSION = "v1.0.0";
export const SPEC_TEST_TO_DOWNLOAD = ["general" as "general"]; export const SPEC_TEST_TO_DOWNLOAD = ["general" as const];
export const SPEC_TESTS_DIR = path.join(__dirname, "spec-tests"); export const SPEC_TESTS_DIR = path.join(__dirname, "spec-tests");

View File

@ -1,9 +1,9 @@
import path from "path"; import path from "path";
import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util";
import {bytesToHex, hexToBytes} from "../../src/helpers"; import {bytesToHex, hexToBytes} from "../../src/helpers/index.js";
import {SPEC_TESTS_DIR} from "../params"; import {SPEC_TESTS_DIR} from "../params.js";
import {describeForAllImplementations} from "../switch"; import {describeForAllImplementations} from "../switch.js";
import {EmptyAggregateError} from "../../src/errors"; import {EmptyAggregateError} from "../../src/errors.js";
interface IAggregateSigsTestCase { interface IAggregateSigsTestCase {
data: { data: {

View File

@ -1,8 +1,8 @@
import path from "path"; import path from "path";
import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util";
import {hexToBytes} from "../../src/helpers"; import {hexToBytes} from "../../src/helpers/index.js";
import {SPEC_TESTS_DIR} from "../params"; import {SPEC_TESTS_DIR} from "../params.js";
import {describeForAllImplementations} from "../switch"; import {describeForAllImplementations} from "../switch.js";
interface IAggregateSigsVerifyTestCase { interface IAggregateSigsVerifyTestCase {
data: { data: {

View File

@ -1,8 +1,8 @@
import path from "path"; import path from "path";
import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util";
import {hexToBytes} from "../../src/helpers"; import {hexToBytes} from "../../src/helpers/index.js";
import {SPEC_TESTS_DIR} from "../params"; import {SPEC_TESTS_DIR} from "../params.js";
import {describeForAllImplementations} from "../switch"; import {describeForAllImplementations} from "../switch.js";
import {CoordType} from "@chainsafe/blst"; import {CoordType} from "@chainsafe/blst";
interface IAggregateSigsVerifyTestCase { interface IAggregateSigsVerifyTestCase {

View File

@ -1,9 +1,9 @@
import path from "path"; import path from "path";
import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util";
import {bytesToHex, hexToBytes} from "../../src/helpers"; import {bytesToHex, hexToBytes} from "../../src/helpers/index.js";
import {SPEC_TESTS_DIR} from "../params"; import {SPEC_TESTS_DIR} from "../params.js";
import {describeForAllImplementations} from "../switch"; import {describeForAllImplementations} from "../switch.js";
import {ZeroSecretKeyError} from "../../src/errors"; import {ZeroSecretKeyError} from "../../src/errors.js";
interface ISignMessageTestCase { interface ISignMessageTestCase {
data: { data: {

View File

@ -1,8 +1,8 @@
import path from "path"; import path from "path";
import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util";
import {hexToBytes} from "../../src/helpers"; import {hexToBytes} from "../../src/helpers/index.js";
import {SPEC_TESTS_DIR} from "../params"; import {SPEC_TESTS_DIR} from "../params.js";
import {describeForAllImplementations} from "../switch"; import {describeForAllImplementations} from "../switch.js";
interface IVerifyTestCase { interface IVerifyTestCase {
data: { data: {

View File

@ -1,6 +1,6 @@
import blst from "../src/blst"; import blst from "../src/blst-native/index.js";
import herumi from "../src/herumi"; import herumi from "../src/herumi/index.js";
import {IBls} from "../src/interface"; import {IBls} from "../src/types.js";
export type Implementation = "blst" | "herumi"; export type Implementation = "blst" | "herumi";
@ -19,7 +19,6 @@ export async function runForAllImplementations(
): Promise<void> { ): Promise<void> {
for (const implementation of ["blst", "herumi"] as Implementation[]) { for (const implementation of ["blst", "herumi"] as Implementation[]) {
const bls = getBls(implementation); const bls = getBls(implementation);
await bls.init();
callback(bls, implementation); callback(bls, implementation);
} }
} }
@ -27,10 +26,6 @@ export async function runForAllImplementations(
export function describeForAllImplementations(callback: (bls: IBls) => void): void { export function describeForAllImplementations(callback: (bls: IBls) => void): void {
runForAllImplementations((bls, implementation) => { runForAllImplementations((bls, implementation) => {
describe(implementation, function () { describe(implementation, function () {
before(async () => {
await bls.init();
});
try { try {
callback(bls); callback(bls);
} catch (e) { } catch (e) {

View File

@ -1,18 +1,12 @@
import herumi from "../../src/herumi"; import {runSecretKeyTests} from "../unit/secretKey.test.js";
import {runSecretKeyTests} from "../unit/secretKey.test"; import {runPublicKeyTests} from "../unit/publicKey.test.js";
import {runPublicKeyTests} from "../unit/publicKey.test"; import {runIndexTests} from "../unit/index.test.js";
import {runIndexTests} from "../unit/index.test";
// This file is intended to be compiled and run by Karma // This file is intended to be compiled and run by Karma
// Do not import the node.bindings or it will break with: // Do not import the node.bindings or it will break with:
// Error: BLST bindings loader should only run in a NodeJS context: process.platform // Error: BLST bindings loader should only run in a NodeJS context: process.platform
describe("herumi", () => { describe("herumi", async () => {
before(async () => { const herumi = (await import("../../src/herumi/index.js")).default;
// For consistency with describeForAllImplementations
// eslint-disable-next-line import/no-named-as-default-member
await herumi.init();
});
runSecretKeyTests(herumi); runSecretKeyTests(herumi);
runPublicKeyTests(herumi); runPublicKeyTests(herumi);
runIndexTests(herumi); runIndexTests(herumi);

View File

@ -1,6 +1,6 @@
import {expect} from "chai"; import {expect} from "chai";
import {concatUint8Arrays, isZeroUint8Array} from "../../../src/helpers/utils"; import {concatUint8Arrays, isZeroUint8Array} from "../../../src/helpers/utils.js";
import {hexToBytesNode} from "../../util"; import {hexToBytesNode} from "../../util.js";
describe("helpers / bytes", () => { describe("helpers / bytes", () => {
describe("isZeroUint8Array", () => { describe("isZeroUint8Array", () => {

View File

@ -1,6 +1,6 @@
import {expect} from "chai"; import {expect} from "chai";
import {hexToBytes, bytesToHex} from "../../../src/helpers/hex"; import {hexToBytes, bytesToHex} from "../../../src/helpers/hex.js";
import {hexToBytesNode} from "../../util"; import {hexToBytesNode} from "../../util.js";
describe("helpers / hex", () => { describe("helpers / hex", () => {
const testCases: {id: string; hex: string}[] = [ const testCases: {id: string; hex: string}[] = [

View File

@ -1,10 +1,14 @@
import {expect} from "chai"; import {expect} from "chai";
import {SecretKey, PublicKey, Signature, init, bls} from "../../src"; import type {SecretKey, PublicKey, Signature, IBls} from "../../src/types.js";
describe("types named exports", async () => {
let bls: IBls;
before(async () => {
bls = (await import("../../src/index.js")).default;
});
describe("index named exports", () => {
it("Classes and methods should be defined", async () => { it("Classes and methods should be defined", async () => {
await init("herumi");
/** /**
* Sample helper to test argument typing * Sample helper to test argument typing
*/ */
@ -12,7 +16,7 @@ describe("index named exports", () => {
return sig.verify(pk, msg); return sig.verify(pk, msg);
} }
const sk = SecretKey.fromKeygen(); const sk = bls.SecretKey.fromKeygen();
const msg = new Uint8Array(32); const msg = new Uint8Array(32);
const sig = sk.sign(msg); const sig = sk.sign(msg);
const pk = sk.toPublicKey(); const pk = sk.toPublicKey();

View File

@ -1,8 +1,9 @@
import {expect} from "chai"; import {expect} from "chai";
import {IBls, PointFormat} from "../../src/interface"; import {Buffer} from "buffer";
import {getN, randomMessage, hexToBytesNode} from "../util"; import {IBls, PointFormat} from "../../src/types.js";
import {hexToBytes} from "../../src/helpers"; import {getN, randomMessage} from "../util.js";
import {maliciousVerifyMultipleSignaturesData} from "../data/malicious-signature-test-data"; import {hexToBytes} from "../../src/helpers/index.js";
import {maliciousVerifyMultipleSignaturesData} from "../data/malicious-signature-test-data.js";
export function runIndexTests(bls: IBls): void { export function runIndexTests(bls: IBls): void {
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
@ -178,13 +179,13 @@ export function runIndexTests(bls: IBls): void {
"0x0a1a1c26055a329817a5759d877a2795f9499b97d6056edde0eea39512f24e8bc874b4471f0501127abb1ea0d9f68ac111392125a1c3750363c2c97d9650fb78696e6428db8ff9efaf0471cbfd20324916ab545746db83756d335e92f9e8c8b8"; "0x0a1a1c26055a329817a5759d877a2795f9499b97d6056edde0eea39512f24e8bc874b4471f0501127abb1ea0d9f68ac111392125a1c3750363c2c97d9650fb78696e6428db8ff9efaf0471cbfd20324916ab545746db83756d335e92f9e8c8b8";
it("Should serialize comp pubkey", () => { it("Should serialize comp pubkey", () => {
const sk = bls.SecretKey.fromBytes(hexToBytesNode(skHex)); const sk = bls.SecretKey.fromBytes(hexToBytes(skHex));
const pkHexComp = sk.toPublicKey().toHex(PointFormat.compressed); const pkHexComp = sk.toPublicKey().toHex(PointFormat.compressed);
expect(pkHexComp).to.equal(pkHexCompExpected, "Wrong pkHexComp"); expect(pkHexComp).to.equal(pkHexCompExpected, "Wrong pkHexComp");
}); });
it("Should serialize uncomp pubkey", () => { it("Should serialize uncomp pubkey", () => {
const sk = bls.SecretKey.fromBytes(hexToBytesNode(skHex)); const sk = bls.SecretKey.fromBytes(hexToBytes(skHex));
const pkHexUncomp = sk.toPublicKey().toHex(PointFormat.uncompressed); const pkHexUncomp = sk.toPublicKey().toHex(PointFormat.uncompressed);
expect(pkHexUncomp).to.equal(pkHexUncompExpected, "Wrong pkHexUncomp"); expect(pkHexUncomp).to.equal(pkHexUncompExpected, "Wrong pkHexUncomp");
}); });

View File

@ -1,5 +1,5 @@
import {expect} from "chai"; import {expect} from "chai";
import {chunkify} from "./utils"; import {chunkify} from "./utils.js";
describe("chunkify", () => { describe("chunkify", () => {
const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15]; const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15];

View File

@ -1,11 +1,16 @@
import {spawn, Pool, Worker, Thread} from "threads"; import {spawn, Pool, Worker, Thread} from "@chainsafe/threads";
import {Implementation, PointFormat, PublicKey, Signature} from "../../../../src"; import {Implementation, PointFormat, PublicKey, Signature} from "../../../../src/types.js";
import {WorkerApi} from "./worker"; import {WorkerApi} from "./worker.js";
type ThreadType = { type ThreadType = {
[K in keyof WorkerApi]: (...args: Parameters<WorkerApi[K]>) => Promise<ReturnType<WorkerApi[K]>>; [K in keyof WorkerApi]: (...args: Parameters<WorkerApi[K]>) => Promise<ReturnType<WorkerApi[K]>>;
}; };
import path from "path";
import {fileURLToPath} from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export class BlsMultiThreadNaive { export class BlsMultiThreadNaive {
impl: Implementation; impl: Implementation;
pool: Pool<Thread & ThreadType>; pool: Pool<Thread & ThreadType>;
@ -17,7 +22,18 @@ export class BlsMultiThreadNaive {
// THe worker is not able to deserialize from uncompressed // THe worker is not able to deserialize from uncompressed
// `Error: err _wrapDeserialize` // `Error: err _wrapDeserialize`
this.format = impl === "blst-native" ? PointFormat.uncompressed : PointFormat.compressed; this.format = impl === "blst-native" ? PointFormat.uncompressed : PointFormat.compressed;
this.pool = Pool(() => (spawn(new Worker("./worker")) as any) as Promise<Thread & ThreadType>, workerCount); this.pool = Pool(
() =>
(spawn(
// There is still an annoyance dealing with ESM imports here:
// threads.js attempts to require.resolve any files passed to Worker, and
// the esm module resolver requires the .js extension, even though the .js file does not actually exist.
// The solution for now:
// Pass in the script path as an absolute path and suppress threads.js default behavior when importing
new Worker(path.join(__dirname, "./worker.js"), {suppressResolveScript: true, suppressTranspileTS: true})
) as any) as Promise<Thread & ThreadType>,
workerCount
);
} }
async destroy(): Promise<void> { async destroy(): Promise<void> {

View File

@ -1,6 +1,7 @@
import {expect} from "chai"; import {expect} from "chai";
import {IBls, PublicKey, Signature} from "../../../../src"; import {IBls} from "../../../../src/types.js";
import {BlsMultiThreadNaive} from "./index"; import type {PublicKey, Signature} from "../../../../src/types.js";
import {BlsMultiThreadNaive} from "./index.js";
export function runMultithreadTests(bls: IBls): void { export function runMultithreadTests(bls: IBls): void {
describe("bls pool naive", function () { describe("bls pool naive", function () {

View File

@ -1,5 +1,6 @@
import {expose} from "threads/worker"; import {expose} from "@chainsafe/threads/worker";
import {bls, init, CoordType, Implementation} from "../../../../src"; import {CoordType, Implementation} from "../../../../src/types.js";
import bls, {init} from "../../../../src/switchable.js";
export type WorkerApi = typeof workerApi; export type WorkerApi = typeof workerApi;

View File

@ -1,5 +1,5 @@
import {expect} from "chai"; import {expect} from "chai";
import {IBls} from "../../src/interface"; import {IBls} from "../../src/types.js";
export function runPublicKeyTests(bls: IBls): void { export function runPublicKeyTests(bls: IBls): void {
describe("PublicKey", () => { describe("PublicKey", () => {

View File

@ -2,7 +2,7 @@ import {runSecretKeyTests} from "./secretKey.test";
import {runPublicKeyTests} from "./publicKey.test"; import {runPublicKeyTests} from "./publicKey.test";
import {runIndexTests} from "./index.test"; import {runIndexTests} from "./index.test";
import {runMultithreadTests} from "./multithread/naive/naive.test"; import {runMultithreadTests} from "./multithread/naive/naive.test";
import {describeForAllImplementations} from "../switch"; import {describeForAllImplementations} from "../switch.js";
// Import test's bls lib lazily to prevent breaking test with Karma // Import test's bls lib lazily to prevent breaking test with Karma
describeForAllImplementations((bls) => { describeForAllImplementations((bls) => {

View File

@ -1,5 +1,5 @@
import {expect} from "chai"; import {expect} from "chai";
import {IBls} from "../../src/interface"; import {IBls} from "../../src/types.js";
export function runSecretKeyTests(bls: IBls): void { export function runSecretKeyTests(bls: IBls): void {
describe("SecretKey", () => { describe("SecretKey", () => {

View File

@ -1,4 +1,4 @@
import {randomBytes} from "../src/helpers"; import {randomBytes} from "../src/helpers/index.js";
export function randomMessage(): Uint8Array { export function randomMessage(): Uint8Array {
return randomBytes(32); return randomBytes(32);

View File

@ -3,7 +3,8 @@
"compilerOptions": { "compilerOptions": {
"outDir": "lib", "outDir": "lib",
"target": "es2019", "target": "es2019",
"module": "commonjs", "module": "esnext",
"moduleResolution": "Node",
"pretty": true, "pretty": true,
"lib": ["esnext.bigint", "DOM"], "lib": ["esnext.bigint", "DOM"],
"typeRoots": ["./node_modules/@types"], "typeRoots": ["./node_modules/@types"],

29
webpack.config.cjs Normal file
View File

@ -0,0 +1,29 @@
const ResolveTypeScriptPlugin = require("resolve-typescript-plugin");
module.exports = {
entry: "./src/index.ts",
mode: "production",
output: {
filename: "dist/bundle.js",
},
module: {
rules: [{test: /\.(ts)$/, use: {loader: "ts-loader", options: {transpileOnly: true}}}],
},
optimization: {
// Disable minification for better debugging on Karma tests
minimize: false,
//splitChunks: false, runtimeChunk: false,
},
devtool: "source-map",
resolve: {
plugins: [new ResolveTypeScriptPlugin()],
fallback: {
crypto: false,
fs: false,
path: false,
},
},
experiments: {
topLevelAwait: true,
},
};

View File

@ -1,21 +0,0 @@
module.exports = {
entry: "./src/index.ts",
mode: "production",
node: {
fs: "empty",
},
output: {
filename: "dist/bundle.js",
},
resolve: {
extensions: [".ts", ".js"],
},
module: {
rules: [{test: /\.ts$/, use: {loader: "ts-loader", options: {transpileOnly: true}}}],
},
optimization: {
// Disable minification for better debugging on Karma tests
minimize: false,
},
devtool: "source-map",
};

3760
yarn.lock

File diff suppressed because it is too large Load Diff