diff --git a/README.md b/README.md index 370eb5f..b4209eb 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,10 @@ To use native bindings you must install peer dependency `@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 -import {init, SecretKey, secretKeyToPublicKey, sign, verify} from "@chainsafe/bls"; +import {SecretKey, secretKeyToPublicKey, sign, verify} from "@chainsafe/bls"; (async () => { await init("herumi"); @@ -45,7 +45,7 @@ import {init, SecretKey, secretKeyToPublicKey, sign, verify} from "@chainsafe/bl ### Browser -If you are in the browser, import from `/herumi` to import directly the WASM version +If you are in the browser, import from `/herumi` to explicitly import the WASM version ```ts import bls from "@chainsafe/bls/herumi"; @@ -53,7 +53,7 @@ import bls from "@chainsafe/bls/herumi"; ### Native bindings only -If you are in NodeJS, import from `/blst-native` 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 yarn add @chainsafe/bls @chainsafe/blst @@ -63,23 +63,27 @@ yarn add @chainsafe/bls @chainsafe/blst 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 - -```bash -yarn add @chainsafe/bls @chainsafe/blst -``` +If you need to get a bls implementation at runtime, import from `/getImplementation`. ```ts -import {init} from "@chainsafe/bls"; +import {getImplementation} from "@chainsafe/bls/getImplementation"; -try { - await init("blst-native"); -} catch (e) { - await init("herumi"); - console.warn("Using WASM"); -} +const bls = await getImplementation("herumi"); +``` + +### 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"); +// 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. diff --git a/package.json b/package.json index 3abf4d2..f6bd136 100644 --- a/package.json +++ b/package.json @@ -7,14 +7,14 @@ ".": { "import": "./lib/index.js" }, - "./register": { - "import": "./register.js" - }, "./types": { "import": "./lib/types.js" }, - "./default": { - "import": "./lib/default.js" + "./getImplementation": { + "import": "./lib/getImplementation.js" + }, + "./switchable": { + "import": "./lib/switchable.js" }, "./blst-native": { "import": "./lib/blst-native/index.js" diff --git a/register.js b/register.js deleted file mode 100644 index 62f2a9d..0000000 --- a/register.js +++ /dev/null @@ -1,12 +0,0 @@ -// ----------------------------------------- -// 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 -import("./lib/index.js").then(({init}) => init("blst-native")).catch((e) => { - // eslint-disable-next-line no-console - console.error(e); - process.exit(1); -}); diff --git a/src/blst-native/index.ts b/src/blst-native/index.ts index 04665db..75e2ce3 100644 --- a/src/blst-native/index.ts +++ b/src/blst-native/index.ts @@ -7,13 +7,6 @@ export * from "../constants.js"; export {SecretKey, PublicKey, Signature}; -export async function init(): Promise { - // Native bindings require no init() call -} -export function destroy(): void { - // Native bindings require no destroy() call -} - export const bls: IBls = { implementation: "blst-native", SecretKey, @@ -21,8 +14,6 @@ export const bls: IBls = { Signature, ...functionalInterfaceFactory({SecretKey, PublicKey, Signature}), - init, - destroy, }; export default bls; diff --git a/src/default.ts b/src/default.ts deleted file mode 100644 index 3179687..0000000 --- a/src/default.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type {IBls} from "./interface.js"; -import {getImplementation} from "./getImplementation.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]"; - -let bls: IBls; -try { - bls = await getImplementation(isNode ? "blst-native" : "herumi"); -} catch (e) { - bls = await getImplementation("herumi"); -} - -export default bls; diff --git a/src/getImplementation.ts b/src/getImplementation.ts index bd80dd2..18b0f9d 100644 --- a/src/getImplementation.ts +++ b/src/getImplementation.ts @@ -6,9 +6,7 @@ const isNode = Object.prototype.toString.call(typeof process !== "undefined" ? p export async function getImplementation(impl: Implementation = "herumi"): Promise { switch (impl) { case "herumi": { - const blsHerumi = (await import("./herumi/index.js")).bls; - await blsHerumi.init(); - return blsHerumi; + return (await import("./herumi/index.js")).bls; } case "blst-native": diff --git a/src/herumi/index.ts b/src/herumi/index.ts index e352361..277aa1f 100644 --- a/src/herumi/index.ts +++ b/src/herumi/index.ts @@ -4,6 +4,9 @@ import {Signature} from "./signature.js"; import {init, destroy} from "./context.js"; import {IBls} from "../interface.js"; import {functionalInterfaceFactory} from "../functional.js"; + +await init(); + export * from "../constants.js"; export {SecretKey, PublicKey, Signature, init, destroy}; @@ -15,8 +18,6 @@ export const bls: IBls = { Signature, ...functionalInterfaceFactory({SecretKey, PublicKey, Signature}), - init, - destroy, }; export default bls; diff --git a/src/index.ts b/src/index.ts index 5cb3084..3179687 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,16 +1,14 @@ -import type {IBls, Implementation} from "./interface.js"; +import type {IBls} from "./interface.js"; import {getImplementation} from "./getImplementation.js"; -export {IBls, Implementation, CoordType, PointFormat} from "./interface.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]"; -// 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 async function init(impl: Implementation): Promise { - // 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); +let bls: IBls; +try { + bls = await getImplementation(isNode ? "blst-native" : "herumi"); +} catch (e) { + bls = await getImplementation("herumi"); } + +export default bls; diff --git a/src/interface.ts b/src/interface.ts index 6a3a66a..ca9e20e 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -12,9 +12,6 @@ export interface IBls { verifyMultiple(publicKeys: Uint8Array[], messages: Uint8Array[], signature: Uint8Array): boolean; verifyMultipleSignatures(sets: {publicKey: Uint8Array; message: Uint8Array; signature: Uint8Array}[]): boolean; secretKeyToPublicKey(secretKey: Uint8Array): Uint8Array; - - init(): Promise; - destroy(): void; } export declare class SecretKey { diff --git a/src/switchable.ts b/src/switchable.ts new file mode 100644 index 0000000..c329acc --- /dev/null +++ b/src/switchable.ts @@ -0,0 +1,16 @@ +import type {IBls, Implementation} from "./interface.js"; +import {getImplementation} from "./getImplementation.js"; + +export * from "./interface.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 { + // 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); +} diff --git a/tsconfig.json b/tsconfig.json index d6ea2b8..c7a51eb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "outDir": "lib", "target": "es2019", - "module": "es2022", + "module": "esnext", "moduleResolution": "Node", "pretty": true, "lib": ["esnext.bigint", "DOM"],