diff --git a/src/context.ts b/src/context.ts index 38e0c18..03bdd12 100644 --- a/src/context.ts +++ b/src/context.ts @@ -1,34 +1,38 @@ -/* eslint-disable require-atomic-updates */ -import bls from "bls-eth-wasm"; +import herumi from "bls-eth-wasm"; +import { herumiToIBls } from "./herumi"; +import { IBls } from "./interface"; -type Bls = typeof bls; -let blsGlobal: Bls | null = null; -let blsGlobalPromise: Promise | null = null; +export type Backing = "herumi" | "blst-native" | "blst-wasm"; -export async function setupBls(): Promise { - if (!blsGlobal) { - await bls.init(); - blsGlobal = bls; +let backing: Backing|undefined = undefined; +let context: IBls|undefined = undefined; + +//to maintain api compatible, add all backing context to return type +export async function init(backing: Backing = "herumi"): Promise { + if (!context) { + switch(backing) { + case "herumi": { + context = await herumiToIBls(); + backing = backing; + } break; + default: throw new Error(`Unsupported backing - ${backing}`) + } } - return blsGlobal; -} - -// Cache a promise for Bls instead of Bls to make sure it is initialized only once -export async function init(): Promise { - if (!blsGlobalPromise) { - blsGlobalPromise = setupBls(); - } - return blsGlobalPromise; + await context.init(); + return context; } export function destroy(): void { - blsGlobal = null; - blsGlobalPromise = null; + if(context) { + context.destroy(); + } + context = undefined; + backing = undefined; } -export function getContext(): Bls { - if (!blsGlobal) { +export function getContext(): IBls { + if (!context) { throw new Error("BLS not initialized"); } - return blsGlobal; + return context; } diff --git a/src/herumi/adapter.ts b/src/herumi/adapter.ts new file mode 100644 index 0000000..0837e04 --- /dev/null +++ b/src/herumi/adapter.ts @@ -0,0 +1,33 @@ +import herumi from "bls-eth-wasm"; +import { init as initHerumi, destroy, getContext } from "./context"; +import { IBls, IPrivateKeyValue, IPublicKeyValue, ISignatureValue } from '../interface'; + +export async function herumiToIBls(): Promise { + await initHerumi(); + const context: IBls = { + SecretKey: getContext().SecretKey as IPrivateKeyValue, + PublicKey: getContext().PublicKey as IPublicKeyValue, + Signature: getContext().Signature as ISignatureValue, + + toHex: herumi.toHex, + toHexStr: herumi.toHexStr, + fromHexStr: herumi.fromHexStr, + getCurveOrder: herumi.getCurveOrder, + getFieldOrder: herumi.getFieldOrder, + verifySignatureOrder: herumi.verifySignatureOrder, + verifyPublicKeyOrder: herumi.verifyPublicKeyOrder, + areAllMsgDifferent: herumi.areAllMsgDifferent, + shouldVerifyBlsSignatureOrder: herumi.shouldVerifyBlsSignatureOrder, + shouldVerifyBlsPublicKeyOrder: herumi.shouldVerifyBlsPublicKeyOrder, + deserializeHexStrToSecretKey: herumi.deserializeHexStrToSecretKey as IBls["deserializeHexStrToSecretKey"], + deserializeHexStrToPublicKey: herumi.deserializeHexStrToPublicKey as IBls["deserializeHexStrToPublicKey"], + deserializeHexStrToSignature: herumi.deserializeHexStrToSignature as IBls["deserializeHexStrToSignature"], + init: async () => { + return context; + }, + destroy: () => { + destroy(); + } + } + return context; +} \ No newline at end of file diff --git a/src/herumi/context.ts b/src/herumi/context.ts new file mode 100644 index 0000000..38e0c18 --- /dev/null +++ b/src/herumi/context.ts @@ -0,0 +1,34 @@ +/* eslint-disable require-atomic-updates */ +import bls from "bls-eth-wasm"; + +type Bls = typeof bls; +let blsGlobal: Bls | null = null; +let blsGlobalPromise: Promise | null = null; + +export async function setupBls(): Promise { + if (!blsGlobal) { + await bls.init(); + blsGlobal = bls; + } + return blsGlobal; +} + +// Cache a promise for Bls instead of Bls to make sure it is initialized only once +export async function init(): Promise { + if (!blsGlobalPromise) { + blsGlobalPromise = setupBls(); + } + return blsGlobalPromise; +} + +export function destroy(): void { + blsGlobal = null; + blsGlobalPromise = null; +} + +export function getContext(): Bls { + if (!blsGlobal) { + throw new Error("BLS not initialized"); + } + return blsGlobal; +} diff --git a/src/herumi/index.ts b/src/herumi/index.ts new file mode 100644 index 0000000..ee2ec62 --- /dev/null +++ b/src/herumi/index.ts @@ -0,0 +1 @@ +export {herumiToIBls} from "./adapter"; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index b398620..2db838e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,9 @@ -import {Keypair} from "./keypair"; -import {PrivateKey} from "./privateKey"; -import {PublicKey} from "./publicKey"; -import {Signature} from "./signature"; import {PUBLIC_KEY_LENGTH} from "./constants"; import assert from "assert"; +import { Keypair } from "./keypair"; +import { PrivateKey } from "./privateKey"; +import { PublicKey } from "./publicKey"; +import { Signature } from "./signature"; export {Keypair, PrivateKey, PublicKey, Signature}; diff --git a/src/interface.ts b/src/interface.ts new file mode 100644 index 0000000..54905b9 --- /dev/null +++ b/src/interface.ts @@ -0,0 +1,90 @@ +interface Common { + + new(): this; + + deserializeHexStr(s: string): void; + + serializeToHexStr(): string; + + isEqual(rhs: this): boolean + + deserialize(v: Uint8Array): void; + + serialize(): Uint8Array; + + add(rhs: this): void; + + dump(msg?: string): string; + + clear(): void; + + clone(): this; +} + +export interface IPrivateKeyValue extends Common { + + setInt(x: number): void; + + setHashOf(a: Uint8Array): void; + + setLittleEndian(a: Uint8Array): void; + + setByCSPRNG(): void; + + getPublicKey(): IPublicKeyValue; + + sign(m: string | Uint8Array): ISignatureValue; + +} + +export interface IPublicKeyValue extends Common { + + verify(signature: ISignatureValue, m: Uint8Array | string): boolean; + isValidOrder(): boolean; + deserializeUncompressed (s: Uint8Array): void; + serializeUncompressed (): Uint8Array; + deserializeUncompressedHexStr (s:string): void; + serializeUncompressedToHexStr(): string; +} + +export interface ISignatureValue extends Common { + + deserializeUncompressed (s: Uint8Array): void; + serializeUncompressed (): Uint8Array; + deserializeUncompressedHexStr (s:string): void; + serializeUncompressedToHexStr(): string; + isValidOrder(): boolean; + aggregate(others: ISignatureValue[]): boolean; + aggregateVerifyNoCheck(publicKeys: IPublicKeyValue[], messages: Uint8Array): boolean; + fastAggregateVerify(publicKeys: IPublicKeyValue[], message: Uint8Array): boolean; +} + +export interface IBls { + //property names are like that for api compatibility + SecretKey: InstanceType; + PublicKey: InstanceType; + Signature: InstanceType; + + toHex(a: Uint8Array, start: number, length: number): string; + toHexStr(a: Uint8Array): string; + fromHexStr(s: string): Uint8Array; + getCurveOrder(): string; + getFieldOrder(): string; + verifySignatureOrder(doVerify: boolean): void; + verifyPublicKeyOrder(doVerify: boolean): void; + + /** + * + * @param msgs single array with concatenated messages + * @param msgSize defaults to MSG_SIZE + */ + areAllMsgDifferent(msgs: Uint8Array, msgSize?: number): boolean; + shouldVerifyBlsSignatureOrder(b: string): void; + shouldVerifyBlsPublicKeyOrder(b: string): void; + deserializeHexStrToSecretKey(s: string): IPrivateKeyValue; + deserializeHexStrToPublicKey(s: string): IPublicKeyValue; + deserializeHexStrToSignature(s: string): ISignatureValue; + + init(): Promise; + destroy(): void; +} \ No newline at end of file diff --git a/src/privateKey.ts b/src/privateKey.ts index dbf3160..76149e2 100644 --- a/src/privateKey.ts +++ b/src/privateKey.ts @@ -1,15 +1,14 @@ import {SECRET_KEY_LENGTH} from "./constants"; import assert from "assert"; -import {SecretKeyType} from "bls-eth-wasm"; import {generateRandomSecretKey} from "@chainsafe/bls-keygen"; -import {getContext} from "./context"; -import {PublicKey} from "./publicKey"; -import {Signature} from "./signature"; +import { IPrivateKeyValue } from "./interface"; +import { getContext } from "./context"; +import { PublicKey, Signature } from "."; export class PrivateKey { - private value: SecretKeyType; + private value: IPrivateKeyValue; - protected constructor(value: SecretKeyType) { + protected constructor(value: IPrivateKeyValue) { this.value = value; } @@ -25,7 +24,9 @@ export class PrivateKey { value = value.replace("0x", ""); assert(value.length === SECRET_KEY_LENGTH * 2, "secret key must have 32 bytes"); const context = getContext(); - return new PrivateKey(context.deserializeHexStrToSecretKey(value)); + const secretKeyValue = new context.SecretKey(); + secretKeyValue.deserializeHexStr(value); + return new PrivateKey(secretKeyValue); } public static fromInt(num: number): PrivateKey { @@ -40,7 +41,7 @@ export class PrivateKey { return this.fromBytes(randomKey); } - public getValue(): SecretKeyType { + public getValue(): IPrivateKeyValue { return this.value; } diff --git a/src/publicKey.ts b/src/publicKey.ts index c7537c7..802fb45 100644 --- a/src/publicKey.ts +++ b/src/publicKey.ts @@ -1,15 +1,15 @@ import {PrivateKey} from "./privateKey"; -import {PublicKeyType} from "bls-eth-wasm"; -import {getContext} from "./context"; import {PUBLIC_KEY_LENGTH} from "./constants"; import assert from "assert"; import {Signature} from "./signature"; import {EMPTY_PUBLIC_KEY} from "./helpers/utils"; +import { IPublicKeyValue } from './interface'; +import { getContext } from "./context"; export class PublicKey { - private value: PublicKeyType; + private value: IPublicKeyValue; - protected constructor(value: PublicKeyType) { + protected constructor(value: IPublicKeyValue) { this.value = value; } @@ -30,10 +30,12 @@ export class PublicKey { value = value.replace("0x", ""); assert(value.length === PUBLIC_KEY_LENGTH * 2); const context = getContext(); - return new PublicKey(context.deserializeHexStrToPublicKey(value)); + const pubkeyValue = new context.PublicKey(); + pubkeyValue.deserializeHexStr(value) + return new PublicKey(pubkeyValue); } - public static fromPublicKeyType(value: PublicKeyType): PublicKey { + public static fromPublicKeyType(value: IPublicKeyValue): PublicKey { return new PublicKey(value); } @@ -55,7 +57,7 @@ export class PublicKey { return `0x${this.toBytesCompressed().toString("hex")}`; } - public getValue(): PublicKeyType { + public getValue(): IPublicKeyValue { return this.value; } } diff --git a/src/signature.ts b/src/signature.ts index 5da9104..0ada6b0 100644 --- a/src/signature.ts +++ b/src/signature.ts @@ -1,14 +1,14 @@ import assert from "assert"; -import {FP_POINT_LENGTH} from "./constants"; -import {SignatureType} from "bls-eth-wasm"; -import {getContext} from "./context"; -import {PublicKey} from "./publicKey"; -import {EMPTY_SIGNATURE} from "./helpers/utils"; +import { FP_POINT_LENGTH } from "./constants"; +import { getContext } from "./context"; +import { EMPTY_SIGNATURE } from "./helpers/utils"; +import { ISignatureValue } from './interface'; +import { PublicKey } from "./publicKey"; export class Signature { - private value: SignatureType; + private value: ISignatureValue; - protected constructor(value: SignatureType) { + protected constructor(value: ISignatureValue) { this.value = value; assert(this.value.isValidOrder()); } @@ -23,7 +23,7 @@ export class Signature { return new Signature(signature); } - public static fromValue(signature: SignatureType): Signature { + public static fromValue(signature: ISignatureValue): Signature { return new Signature(signature); } @@ -40,7 +40,7 @@ export class Signature { return new Signature(agg); } - public getValue(): SignatureType { + public getValue(): ISignatureValue { return this.value; } diff --git a/types/bls-eth-wasm/index.d.ts b/types/bls-eth-wasm/index.d.ts index a41515e..c861ef9 100644 --- a/types/bls-eth-wasm/index.d.ts +++ b/types/bls-eth-wasm/index.d.ts @@ -37,12 +37,6 @@ declare module "bls-eth-wasm" { getPublicKey(): PublicKeyType; sign(m: string | Uint8Array): SignatureType; - - /** - * - * @param m must have 40 bytes - */ - signHashWithDomain(m: Uint8Array): SignatureType; } export class PublicKeyType extends Common { @@ -55,12 +49,6 @@ declare module "bls-eth-wasm" { serializeUncompressed (): Uint8Array; deserializeUncompressedHexStr (s:string): void; serializeUncompressedToHexStr(): string; - /** - * - * @param signature - * @param m must have 40 bytes - */ - verifyHashWithDomain(signature: SignatureType, m: Uint8Array): boolean; } export class SignatureType extends Common { @@ -74,12 +62,6 @@ declare module "bls-eth-wasm" { aggregate(others: SignatureType[]): boolean; aggregateVerifyNoCheck(publicKeys: PublicKeyType[], messages: Uint8Array): boolean; fastAggregateVerify(publicKeys: PublicKeyType[], message: Uint8Array): boolean; - /** - * - * @param publicKeys - * @param messages each message must have 40bytes - */ - verifyAggregatedHashWithDomain(publicKeys: PublicKeyType[], messages: Uint8Array[]): boolean } @@ -105,7 +87,7 @@ declare module "bls-eth-wasm" { export function deserializeHexStrToPublicKey(s: string): PublicKeyType; export function deserializeHexStrToSignature(s: string): SignatureType; - export const SecretKey: typeof SecretKeyType; - export const PublicKey: typeof PublicKeyType; - export const Signature: typeof SignatureType; + export const SecretKey: InstanceType; + export const PublicKey: InstanceType; + export const Signature: InstanceType; } \ No newline at end of file