abstract herumi

This commit is contained in:
Marin Petrunić 2020-11-23 14:35:20 +01:00
parent 34f285e779
commit 0d273a7d36
No known key found for this signature in database
GPG Key ID: 834D07135E110DA5
10 changed files with 219 additions and 72 deletions

View File

@ -1,34 +1,38 @@
/* eslint-disable require-atomic-updates */ import herumi from "bls-eth-wasm";
import bls from "bls-eth-wasm"; import { herumiToIBls } from "./herumi";
import { IBls } from "./interface";
type Bls = typeof bls; export type Backing = "herumi" | "blst-native" | "blst-wasm";
let blsGlobal: Bls | null = null;
let blsGlobalPromise: Promise<Bls> | null = null;
export async function setupBls(): Promise<Bls> { let backing: Backing|undefined = undefined;
if (!blsGlobal) { let context: IBls|undefined = undefined;
await bls.init();
blsGlobal = bls; //to maintain api compatible, add all backing context to return type
export async function init(backing: Backing = "herumi"): Promise<IBls> {
if (!context) {
switch(backing) {
case "herumi": {
context = await herumiToIBls();
backing = backing;
} break;
default: throw new Error(`Unsupported backing - ${backing}`)
}
} }
return blsGlobal; await context.init();
} return context;
// Cache a promise for Bls instead of Bls to make sure it is initialized only once
export async function init(): Promise<Bls> {
if (!blsGlobalPromise) {
blsGlobalPromise = setupBls();
}
return blsGlobalPromise;
} }
export function destroy(): void { export function destroy(): void {
blsGlobal = null; if(context) {
blsGlobalPromise = null; context.destroy();
}
context = undefined;
backing = undefined;
} }
export function getContext(): Bls { export function getContext(): IBls {
if (!blsGlobal) { if (!context) {
throw new Error("BLS not initialized"); throw new Error("BLS not initialized");
} }
return blsGlobal; return context;
} }

33
src/herumi/adapter.ts Normal file
View File

@ -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<IBls> {
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;
}

34
src/herumi/context.ts Normal file
View File

@ -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<Bls> | null = null;
export async function setupBls(): Promise<Bls> {
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<Bls> {
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;
}

1
src/herumi/index.ts Normal file
View File

@ -0,0 +1 @@
export {herumiToIBls} from "./adapter";

View File

@ -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 {PUBLIC_KEY_LENGTH} from "./constants";
import assert from "assert"; 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}; export {Keypair, PrivateKey, PublicKey, Signature};

90
src/interface.ts Normal file
View File

@ -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<IPrivateKeyValue>;
PublicKey: InstanceType<IPublicKeyValue>;
Signature: InstanceType<ISignatureValue>;
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<this>;
destroy(): void;
}

View File

@ -1,15 +1,14 @@
import {SECRET_KEY_LENGTH} from "./constants"; import {SECRET_KEY_LENGTH} from "./constants";
import assert from "assert"; import assert from "assert";
import {SecretKeyType} from "bls-eth-wasm";
import {generateRandomSecretKey} from "@chainsafe/bls-keygen"; import {generateRandomSecretKey} from "@chainsafe/bls-keygen";
import {getContext} from "./context"; import { IPrivateKeyValue } from "./interface";
import {PublicKey} from "./publicKey"; import { getContext } from "./context";
import {Signature} from "./signature"; import { PublicKey, Signature } from ".";
export class PrivateKey { export class PrivateKey {
private value: SecretKeyType; private value: IPrivateKeyValue;
protected constructor(value: SecretKeyType) { protected constructor(value: IPrivateKeyValue) {
this.value = value; this.value = value;
} }
@ -25,7 +24,9 @@ export class PrivateKey {
value = value.replace("0x", ""); value = value.replace("0x", "");
assert(value.length === SECRET_KEY_LENGTH * 2, "secret key must have 32 bytes"); assert(value.length === SECRET_KEY_LENGTH * 2, "secret key must have 32 bytes");
const context = getContext(); 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 { public static fromInt(num: number): PrivateKey {
@ -40,7 +41,7 @@ export class PrivateKey {
return this.fromBytes(randomKey); return this.fromBytes(randomKey);
} }
public getValue(): SecretKeyType { public getValue(): IPrivateKeyValue {
return this.value; return this.value;
} }

View File

@ -1,15 +1,15 @@
import {PrivateKey} from "./privateKey"; import {PrivateKey} from "./privateKey";
import {PublicKeyType} from "bls-eth-wasm";
import {getContext} from "./context";
import {PUBLIC_KEY_LENGTH} from "./constants"; import {PUBLIC_KEY_LENGTH} from "./constants";
import assert from "assert"; import assert from "assert";
import {Signature} from "./signature"; import {Signature} from "./signature";
import {EMPTY_PUBLIC_KEY} from "./helpers/utils"; import {EMPTY_PUBLIC_KEY} from "./helpers/utils";
import { IPublicKeyValue } from './interface';
import { getContext } from "./context";
export class PublicKey { export class PublicKey {
private value: PublicKeyType; private value: IPublicKeyValue;
protected constructor(value: PublicKeyType) { protected constructor(value: IPublicKeyValue) {
this.value = value; this.value = value;
} }
@ -30,10 +30,12 @@ export class PublicKey {
value = value.replace("0x", ""); value = value.replace("0x", "");
assert(value.length === PUBLIC_KEY_LENGTH * 2); assert(value.length === PUBLIC_KEY_LENGTH * 2);
const context = getContext(); 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); return new PublicKey(value);
} }
@ -55,7 +57,7 @@ export class PublicKey {
return `0x${this.toBytesCompressed().toString("hex")}`; return `0x${this.toBytesCompressed().toString("hex")}`;
} }
public getValue(): PublicKeyType { public getValue(): IPublicKeyValue {
return this.value; return this.value;
} }
} }

View File

@ -1,14 +1,14 @@
import assert from "assert"; import assert from "assert";
import {FP_POINT_LENGTH} from "./constants"; import { FP_POINT_LENGTH } from "./constants";
import {SignatureType} from "bls-eth-wasm"; import { getContext } from "./context";
import {getContext} from "./context"; import { EMPTY_SIGNATURE } from "./helpers/utils";
import {PublicKey} from "./publicKey"; import { ISignatureValue } from './interface';
import {EMPTY_SIGNATURE} from "./helpers/utils"; import { PublicKey } from "./publicKey";
export class Signature { export class Signature {
private value: SignatureType; private value: ISignatureValue;
protected constructor(value: SignatureType) { protected constructor(value: ISignatureValue) {
this.value = value; this.value = value;
assert(this.value.isValidOrder()); assert(this.value.isValidOrder());
} }
@ -23,7 +23,7 @@ export class Signature {
return new Signature(signature); return new Signature(signature);
} }
public static fromValue(signature: SignatureType): Signature { public static fromValue(signature: ISignatureValue): Signature {
return new Signature(signature); return new Signature(signature);
} }
@ -40,7 +40,7 @@ export class Signature {
return new Signature(agg); return new Signature(agg);
} }
public getValue(): SignatureType { public getValue(): ISignatureValue {
return this.value; return this.value;
} }

View File

@ -37,12 +37,6 @@ declare module "bls-eth-wasm" {
getPublicKey(): PublicKeyType; getPublicKey(): PublicKeyType;
sign(m: string | Uint8Array): SignatureType; sign(m: string | Uint8Array): SignatureType;
/**
*
* @param m must have 40 bytes
*/
signHashWithDomain(m: Uint8Array): SignatureType;
} }
export class PublicKeyType extends Common { export class PublicKeyType extends Common {
@ -55,12 +49,6 @@ declare module "bls-eth-wasm" {
serializeUncompressed (): Uint8Array; serializeUncompressed (): Uint8Array;
deserializeUncompressedHexStr (s:string): void; deserializeUncompressedHexStr (s:string): void;
serializeUncompressedToHexStr(): string; serializeUncompressedToHexStr(): string;
/**
*
* @param signature
* @param m must have 40 bytes
*/
verifyHashWithDomain(signature: SignatureType, m: Uint8Array): boolean;
} }
export class SignatureType extends Common { export class SignatureType extends Common {
@ -74,12 +62,6 @@ declare module "bls-eth-wasm" {
aggregate(others: SignatureType[]): boolean; aggregate(others: SignatureType[]): boolean;
aggregateVerifyNoCheck(publicKeys: PublicKeyType[], messages: Uint8Array): boolean; aggregateVerifyNoCheck(publicKeys: PublicKeyType[], messages: Uint8Array): boolean;
fastAggregateVerify(publicKeys: PublicKeyType[], message: 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 deserializeHexStrToPublicKey(s: string): PublicKeyType;
export function deserializeHexStrToSignature(s: string): SignatureType; export function deserializeHexStrToSignature(s: string): SignatureType;
export const SecretKey: typeof SecretKeyType; export const SecretKey: InstanceType<typeof SecretKeyType>;
export const PublicKey: typeof PublicKeyType; export const PublicKey: InstanceType<typeof PublicKeyType>;
export const Signature: typeof SignatureType; export const Signature: InstanceType<typeof SignatureType>;
} }