From 57694c2e5401b1213020017f272a7d68896f35be Mon Sep 17 00:00:00 2001 From: dapplion Date: Thu, 19 Nov 2020 13:22:41 +0000 Subject: [PATCH] Co-exist implementations --- package.json | 2 +- src/blst/index.ts | 146 ++++++++++++++++++ src/{ => blst}/keypair.ts | 0 src/{ => blst}/privateKey.ts | 8 +- src/{ => blst}/publicKey.ts | 14 +- src/blst/signature.ts | 63 ++++++++ src/constants.ts | 2 + src/helpers/utils.ts | 6 +- src/{ => herumi}/context.ts | 2 +- src/herumi/index.ts | 146 ++++++++++++++++++ src/herumi/keypair.ts | 29 ++++ src/herumi/privateKey.ts | 52 +++++++ src/herumi/publicKey.ts | 44 ++++++ src/herumi/signature.ts | 69 +++++++++ src/{ => herumi}/web.ts | 0 src/index.ts | 147 +------------------ src/signature.ts | 53 ------- test/benchmark/index.ts | 12 +- test/params.ts | 2 + test/spec-blst/aggregate_sigs.test.ts | 29 ---- test/spec-blst/aggregate_sigs_verify.test.ts | 39 ----- test/spec-blst/fast_aggregate_verify.test.ts | 42 ------ test/spec-blst/sign.test.ts | 34 ----- test/spec-blst/verify.test.ts | 39 ----- test/spec/aggregate_sigs.test.ts | 49 +++---- test/spec/aggregate_sigs_verify.test.ts | 43 ++---- test/spec/fast_aggregate_verify.test.ts | 44 ++---- test/spec/sign.test.ts | 38 +++-- test/spec/verify.test.ts | 37 ++--- test/switch.ts | 30 ++++ test/unit/context.test.ts | 19 --- test/unit/index.test.ts | 33 ++--- test/unit/keypair.test.ts | 5 +- test/unit/privateKey.test.ts | 6 +- test/unit/publicKey.test.ts | 5 +- yarn.lock | 13 +- 36 files changed, 707 insertions(+), 595 deletions(-) create mode 100644 src/blst/index.ts rename src/{ => blst}/keypair.ts (100%) rename src/{ => blst}/privateKey.ts (76%) rename src/{ => blst}/publicKey.ts (68%) create mode 100644 src/blst/signature.ts rename src/{ => herumi}/context.ts (93%) create mode 100644 src/herumi/index.ts create mode 100644 src/herumi/keypair.ts create mode 100644 src/herumi/privateKey.ts create mode 100644 src/herumi/publicKey.ts create mode 100644 src/herumi/signature.ts rename src/{ => herumi}/web.ts (100%) delete mode 100644 src/signature.ts create mode 100644 test/params.ts delete mode 100644 test/spec-blst/aggregate_sigs.test.ts delete mode 100644 test/spec-blst/aggregate_sigs_verify.test.ts delete mode 100644 test/spec-blst/fast_aggregate_verify.test.ts delete mode 100644 test/spec-blst/sign.test.ts delete mode 100644 test/spec-blst/verify.test.ts create mode 100644 test/switch.ts delete mode 100644 test/unit/context.test.ts diff --git a/package.json b/package.json index 996b109..56666ad 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ }, "dependencies": { "@chainsafe/bls-keygen": "^0.2.0", - "@chainsafe/blst-ts": "file:../blst-ts", + "@chainsafe/blst": "^0.1.0", "@chainsafe/eth2-bls-wasm": "^0.5.0", "assert": "^1.4.1" }, diff --git a/src/blst/index.ts b/src/blst/index.ts new file mode 100644 index 0000000..380f1d7 --- /dev/null +++ b/src/blst/index.ts @@ -0,0 +1,146 @@ +import assert from "assert"; +import {Keypair} from "./keypair"; +import {PrivateKey} from "./privateKey"; +import {PublicKey} from "./publicKey"; +import {Signature} from "./signature"; +import {toBuffer} from "../helpers/utils"; +export * from "../constants"; + +export {Keypair, PrivateKey, PublicKey, Signature}; + +export async function initBLS() { + // Native bindings require no init() call +} +export function destroy() { + // Native bindings require no destroy() call +} + +/** + * Generates new secret and public key + */ +export function generateKeyPair(): Keypair { + return Keypair.generate(); +} + +/** + * Generates public key from given secret. + * @param {BLSSecretKey} secretKey + */ +export function generatePublicKey(secretKey: Uint8Array): Buffer { + assert(secretKey, "secretKey is null or undefined"); + const keypair = new Keypair(PrivateKey.fromBytes(toBuffer(secretKey))); + return keypair.publicKey.toBytes(); +} + +/** + * Signs given message using secret key. + * @param secretKey + * @param messageHash + */ +export function sign(secretKey: Uint8Array, messageHash: Uint8Array): Buffer { + assert(secretKey, "secretKey is null or undefined"); + assert(messageHash, "messageHash is null or undefined"); + const privateKey = PrivateKey.fromBytes(toBuffer(secretKey)); + return privateKey.signMessage(toBuffer(messageHash)).toBytes(); +} + +/** + * Compines all given signature into one. + * @param signatures + */ +export function aggregateSignatures(signatures: Uint8Array[]): Buffer { + const agg = Signature.aggregate(signatures.map((p) => Signature.fromBytes(p))); + return agg.toBytes(); +} + +/** + * Combines all given public keys into single one + * @param publicKeys + */ +export function aggregatePubkeys(publicKeys: Uint8Array[]): Buffer { + const agg = PublicKey.aggregate(publicKeys.map((p) => PublicKey.fromBytes(p))); + return agg.toBytes(); +} + +/** + * Verifies if signature is message signed with given public key. + * @param publicKey + * @param messageHash + * @param signature + */ +export function verify(publicKey: Uint8Array, messageHash: Uint8Array, signature: Uint8Array): boolean { + assert(publicKey, "publicKey is null or undefined"); + assert(messageHash, "messageHash is null or undefined"); + assert(signature, "signature is null or undefined"); + try { + return PublicKey.fromBytes(publicKey).verifyMessage( + Signature.fromBytes(toBuffer(signature)), + toBuffer(messageHash) + ); + } catch (e) { + return false; + } +} + +/** + * Verifies if aggregated signature is same message signed with given public keys. + * @param publicKeys + * @param messageHash + * @param signature + */ +export function verifyAggregate(publicKeys: Uint8Array[], messageHash: Uint8Array, signature: Uint8Array): boolean { + assert(publicKeys, "publicKey is null or undefined"); + assert(messageHash, "messageHash is null or undefined"); + assert(signature, "signature is null or undefined"); + try { + return Signature.fromBytes(signature).verifyAggregate( + publicKeys.map((pubkey) => PublicKey.fromBytes(pubkey)), + messageHash + ); + } catch (e) { + return false; + } +} + +/** + * Verifies if signature is list of message signed with corresponding public key. + * @param publicKeys + * @param messageHashes + * @param signature + * @param fast Check if all messages are different + */ +export function verifyMultiple(publicKeys: Uint8Array[], messageHashes: Uint8Array[], signature: Uint8Array): boolean { + assert(publicKeys, "publicKey is null or undefined"); + assert(messageHashes, "messageHash is null or undefined"); + assert(signature, "signature is null or undefined"); + + if (publicKeys.length === 0 || publicKeys.length != messageHashes.length) { + return false; + } + try { + return Signature.fromBytes(toBuffer(signature)).verifyMultiple( + publicKeys.map((key) => PublicKey.fromBytes(toBuffer(key))), + messageHashes.map((m) => toBuffer(m)) + ); + } catch (e) { + return false; + } +} + +export default { + generateKeyPair, + generatePublicKey, + sign, + aggregateSignatures, + aggregatePubkeys, + verify, + verifyAggregate, + verifyMultiple, + + Keypair, + PrivateKey, + PublicKey, + Signature, + initBLS, + destroy, +}; diff --git a/src/keypair.ts b/src/blst/keypair.ts similarity index 100% rename from src/keypair.ts rename to src/blst/keypair.ts diff --git a/src/privateKey.ts b/src/blst/privateKey.ts similarity index 76% rename from src/privateKey.ts rename to src/blst/privateKey.ts index 21d513c..c375cf6 100644 --- a/src/privateKey.ts +++ b/src/blst/privateKey.ts @@ -1,6 +1,5 @@ -import * as blst from "@chainsafe/blst-ts"; -import {blst as blstBindings} from "@chainsafe/blst-ts/dist/bindings"; -import {bytesToHex, getRandomBytes, hexToBytes} from "./helpers/utils"; +import * as blst from "@chainsafe/blst"; +import {bytesToHex, getRandomBytes, hexToBytes} from "../helpers/utils"; import {PublicKey} from "./publicKey"; import {Signature} from "./signature"; @@ -30,8 +29,7 @@ export class PrivateKey { } toPublicKey(): PublicKey { - const p1 = new blstBindings.P1(this.value.value); - const jacobian = new blst.AggregatePublicKey(p1); + const jacobian = this.value.toAggregatePublicKey(); const affine = jacobian.toPublicKey(); return new PublicKey(affine, jacobian); } diff --git a/src/publicKey.ts b/src/blst/publicKey.ts similarity index 68% rename from src/publicKey.ts rename to src/blst/publicKey.ts index 203e4bb..67f5281 100644 --- a/src/publicKey.ts +++ b/src/blst/publicKey.ts @@ -1,7 +1,6 @@ -import * as blst from "@chainsafe/blst-ts"; -import {blst as blstBindings} from "@chainsafe/blst-ts/dist/bindings"; +import * as blst from "@chainsafe/blst"; +import {bytesToHex, hexToBytes} from "../helpers/utils"; import {Signature} from "./signature"; -import {bytesToHex, hexToBytes} from "./helpers/utils"; export class PublicKey { readonly affine: blst.PublicKey; @@ -23,18 +22,13 @@ export class PublicKey { } static aggregate(pubkeys: PublicKey[]): PublicKey { - const p1Arr = pubkeys.map((pk) => pk.jacobian.value); - const aggP1 = p1Arr.reduce((_agg, pk) => { - return blstBindings.P1.add(_agg, pk); - }); - - const jacobian = new blst.AggregatePublicKey(aggP1); + const jacobian = blst.aggregatePubkeys(pubkeys.map((pk) => pk.jacobian)); const affine = jacobian.toPublicKey(); return new PublicKey(affine, jacobian); } verifyMessage(signature: Signature, message: Uint8Array): boolean { - return blst.verify(message, this.affine, signature.value); + return signature.verify(this, message); } toBytes(): Buffer { diff --git a/src/blst/signature.ts b/src/blst/signature.ts new file mode 100644 index 0000000..508fedf --- /dev/null +++ b/src/blst/signature.ts @@ -0,0 +1,63 @@ +import * as blst from "@chainsafe/blst"; +import {bytesToHex, hexToBytes} from "../helpers/utils"; +import {PublicKey} from "./publicKey"; + +export class Signature { + readonly affine: blst.Signature; + + constructor(value: blst.Signature) { + this.affine = value; + } + + static fromBytes(bytes: Uint8Array): Signature { + return new Signature(blst.Signature.fromBytes(bytes)); + } + + static fromHex(hex: string): Signature { + return this.fromBytes(hexToBytes(hex)); + } + + static fromValue(signature: blst.Signature): Signature { + return new Signature(signature); + } + + static aggregate(signatures: Signature[]): Signature { + const agg = blst.AggregateSignature.fromSignatures(signatures.map((sig) => sig.affine)); + return new Signature(agg.toSignature()); + } + + verify(publicKey: PublicKey, message: Uint8Array): boolean { + return this.aggregateVerify([message], [publicKey.affine]); + } + + verifyAggregate(publicKeys: PublicKey[], message: Uint8Array): boolean { + const agg = PublicKey.aggregate(publicKeys); + return this.aggregateVerify([message], [agg.affine]); + } + + verifyMultiple(publicKeys: PublicKey[], messages: Uint8Array[]): boolean { + return this.aggregateVerify( + messages, + publicKeys.map((pk) => pk.affine) + ); + } + + private aggregateVerify(msgs: Uint8Array[], pks: blst.PublicKey[]): boolean { + // If this set is simply an infinity signature and infinity pubkey then skip verification. + // This has the effect of always declaring that this sig/pubkey combination is valid. + // for Eth2.0 specs tests + if (this.affine.value.is_inf() && pks.length === 1 && pks[0].value.is_inf()) { + return true; + } + + return blst.aggregateVerify(msgs, pks, this.affine); + } + + toBytes(): Buffer { + return Buffer.from(this.affine.toBytes()); + } + + toHex(): string { + return bytesToHex(this.toBytes()); + } +} diff --git a/src/constants.ts b/src/constants.ts index 2243a76..cf34046 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -3,3 +3,5 @@ export const SIGNATURE_LENGTH = 96; export const FP_POINT_LENGTH = 48; export const PUBLIC_KEY_LENGTH = FP_POINT_LENGTH; export const G2_HASH_PADDING = 16; +export const EMPTY_PUBLIC_KEY = Buffer.alloc(PUBLIC_KEY_LENGTH); +export const EMPTY_SIGNATURE = Buffer.alloc(SIGNATURE_LENGTH); diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts index 2e829ed..50a34d9 100644 --- a/src/helpers/utils.ts +++ b/src/helpers/utils.ts @@ -1,6 +1,5 @@ import assert from "assert"; import crypto from "crypto"; -import {PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH} from "../constants"; /** * Pads byte array with zeroes on left side up to desired length. @@ -27,5 +26,6 @@ export function getRandomBytes(size: number): Uint8Array { return Uint8Array.from(crypto.randomBytes(size)); } -export const EMPTY_PUBLIC_KEY = Buffer.alloc(PUBLIC_KEY_LENGTH); -export const EMPTY_SIGNATURE = Buffer.alloc(SIGNATURE_LENGTH); +export function toBuffer(input: Uint8Array): Buffer { + return Buffer.from(input.buffer, input.byteOffset, input.length); +} diff --git a/src/context.ts b/src/herumi/context.ts similarity index 93% rename from src/context.ts rename to src/herumi/context.ts index aceb809..65992a4 100644 --- a/src/context.ts +++ b/src/herumi/context.ts @@ -14,7 +14,7 @@ export async function setupBls(): Promise { } // Cache a promise for Bls instead of Bls to make sure it is initialized only once -export async function init(): Promise { +export async function initBLS(): Promise { if (!blsGlobalPromise) { blsGlobalPromise = setupBls(); } diff --git a/src/herumi/index.ts b/src/herumi/index.ts new file mode 100644 index 0000000..0d80cc7 --- /dev/null +++ b/src/herumi/index.ts @@ -0,0 +1,146 @@ +import {Keypair} from "./keypair"; +import {PrivateKey} from "./privateKey"; +import {PublicKey} from "./publicKey"; +import {Signature} from "./signature"; +import {initBLS, destroy} from "./context"; +import {PUBLIC_KEY_LENGTH} from "../constants"; +import assert from "assert"; +import {toBuffer} from "../helpers/utils"; + +export {Keypair, PrivateKey, PublicKey, Signature, initBLS, destroy}; + +/** + * Generates new secret and public key + */ +export function generateKeyPair(): Keypair { + return Keypair.generate(); +} + +/** + * Generates public key from given secret. + * @param {BLSSecretKey} secretKey + */ +export function generatePublicKey(secretKey: Uint8Array): Buffer { + assert(secretKey, "secretKey is null or undefined"); + const keypair = new Keypair(PrivateKey.fromBytes(toBuffer(secretKey))); + return keypair.publicKey.toBytes(); +} + +/** + * Signs given message using secret key. + * @param secretKey + * @param messageHash + */ +export function sign(secretKey: Uint8Array, messageHash: Uint8Array): Buffer { + assert(secretKey, "secretKey is null or undefined"); + assert(messageHash, "messageHash is null or undefined"); + const privateKey = PrivateKey.fromBytes(toBuffer(secretKey)); + return privateKey.signMessage(toBuffer(messageHash)).toBytes(); +} + +/** + * Compines all given signature into one. + * @param signatures + */ +export function aggregateSignatures(signatures: Uint8Array[]): Buffer { + assert(signatures && signatures.length > 0, "EMPTY_AGGREGATE_ARRAY"); + const agg = Signature.aggregate(signatures.map((signature): Signature => Signature.fromBytes(signature))); + return agg.toBytes(); +} + +/** + * Combines all given public keys into single one + * @param publicKeys + */ +export function aggregatePubkeys(publicKeys: Uint8Array[]): Buffer { + assert(publicKeys, "publicKeys is null or undefined"); + if (publicKeys.length === 0) { + return Buffer.alloc(PUBLIC_KEY_LENGTH); + } + return publicKeys + .map((p) => PublicKey.fromBytes(toBuffer(p))) + .reduce((agg, pubKey) => agg.add(pubKey)) + .toBytes(); +} + +/** + * Verifies if signature is message signed with given public key. + * @param publicKey + * @param messageHash + * @param signature + */ +export function verify(publicKey: Uint8Array, messageHash: Uint8Array, signature: Uint8Array): boolean { + assert(publicKey, "publicKey is null or undefined"); + assert(messageHash, "messageHash is null or undefined"); + assert(signature, "signature is null or undefined"); + try { + return PublicKey.fromBytes(publicKey).verifyMessage( + Signature.fromBytes(toBuffer(signature)), + toBuffer(messageHash) + ); + } catch (e) { + return false; + } +} + +/** + * Verifies if aggregated signature is same message signed with given public keys. + * @param publicKeys + * @param messageHash + * @param signature + */ +export function verifyAggregate(publicKeys: Uint8Array[], messageHash: Uint8Array, signature: Uint8Array): boolean { + assert(publicKeys, "publicKey is null or undefined"); + assert(messageHash, "messageHash is null or undefined"); + assert(signature, "signature is null or undefined"); + try { + return Signature.fromBytes(signature).verifyAggregate( + publicKeys.map((pubkey) => PublicKey.fromBytes(pubkey)), + messageHash + ); + } catch (e) { + return false; + } +} + +/** + * Verifies if signature is list of message signed with corresponding public key. + * @param publicKeys + * @param messageHashes + * @param signature + */ +export function verifyMultiple(publicKeys: Uint8Array[], messageHashes: Uint8Array[], signature: Uint8Array): boolean { + assert(publicKeys, "publicKey is null or undefined"); + assert(messageHashes, "messageHash is null or undefined"); + assert(signature, "signature is null or undefined"); + + if (publicKeys.length === 0 || publicKeys.length != messageHashes.length) { + return false; + } + try { + return Signature.fromBytes(toBuffer(signature)).verifyMultiple( + publicKeys.map((key) => PublicKey.fromBytes(toBuffer(key))), + messageHashes.map((m) => toBuffer(m)) + ); + } catch (e) { + return false; + } +} + +export default { + generateKeyPair, + generatePublicKey, + sign, + aggregateSignatures, + aggregatePubkeys, + verify, + verifyAggregate, + verifyMultiple, + + Keypair, + PrivateKey, + PublicKey, + Signature, + initBLS, + destroy, +}; diff --git a/src/herumi/keypair.ts b/src/herumi/keypair.ts new file mode 100644 index 0000000..2e34c54 --- /dev/null +++ b/src/herumi/keypair.ts @@ -0,0 +1,29 @@ +import {PublicKey} from "./publicKey"; +import {PrivateKey} from "./privateKey"; + +export class Keypair { + private readonly _publicKey: PublicKey; + + private readonly _privateKey: PrivateKey; + + public constructor(privateKey: PrivateKey, publicKey?: PublicKey) { + this._privateKey = privateKey; + if (!publicKey) { + this._publicKey = privateKey.toPublicKey(); + } else { + this._publicKey = publicKey; + } + } + + public get publicKey(): PublicKey { + return this._publicKey; + } + + public get privateKey(): PrivateKey { + return this._privateKey; + } + + public static generate(): Keypair { + return new Keypair(PrivateKey.fromKeygen()); + } +} diff --git a/src/herumi/privateKey.ts b/src/herumi/privateKey.ts new file mode 100644 index 0000000..43a0668 --- /dev/null +++ b/src/herumi/privateKey.ts @@ -0,0 +1,52 @@ +import assert from "assert"; +import {SecretKeyType} from "@chainsafe/eth2-bls-wasm"; +import {generateRandomSecretKey} from "@chainsafe/bls-keygen"; +import {SECRET_KEY_LENGTH} from "../constants"; +import {getContext} from "./context"; +import {PublicKey} from "./publicKey"; +import {Signature} from "./signature"; +import {bytesToHex, hexToBytes} from "../helpers/utils"; + +export class PrivateKey { + readonly value: SecretKeyType; + + constructor(value: SecretKeyType) { + this.value = value; + } + + static fromBytes(bytes: Uint8Array): PrivateKey { + assert(bytes.length === SECRET_KEY_LENGTH, "Private key should have 32 bytes"); + const context = getContext(); + const secretKey = new context.SecretKey(); + secretKey.deserialize(Buffer.from(bytes)); + return new PrivateKey(secretKey); + } + + static fromHex(hex: string): PrivateKey { + return this.fromBytes(hexToBytes(hex)); + } + + static fromKeygen(entropy?: Uint8Array): PrivateKey { + return this.fromBytes(generateRandomSecretKey(Buffer.from(entropy))); + } + + getValue(): SecretKeyType { + return this.value; + } + + signMessage(message: Uint8Array): Signature { + return new Signature(this.value.sign(message)); + } + + toPublicKey(): PublicKey { + return new PublicKey(this.value.getPublicKey()); + } + + toBytes(): Buffer { + return Buffer.from(this.value.serialize()); + } + + toHex(): string { + return bytesToHex(this.toBytes()); + } +} diff --git a/src/herumi/publicKey.ts b/src/herumi/publicKey.ts new file mode 100644 index 0000000..140fd0f --- /dev/null +++ b/src/herumi/publicKey.ts @@ -0,0 +1,44 @@ +import {PublicKeyType} from "@chainsafe/eth2-bls-wasm"; +import {getContext} from "./context"; +import {EMPTY_PUBLIC_KEY} from "../constants"; +import {Signature} from "./signature"; +import {bytesToHex, hexToBytes} from "../helpers/utils"; + +export class PublicKey { + readonly value: PublicKeyType; + + constructor(value: PublicKeyType) { + this.value = value; + } + + static fromBytes(bytes: Uint8Array): PublicKey { + const context = getContext(); + const publicKey = new context.PublicKey(); + if (!EMPTY_PUBLIC_KEY.equals(bytes)) { + publicKey.deserialize(bytes); + } + return new PublicKey(publicKey); + } + + static fromHex(hex: string): PublicKey { + return this.fromBytes(hexToBytes(hex)); + } + + add(other: PublicKey): PublicKey { + const agg = new PublicKey(this.value.clone()); + agg.value.add(other.value); + return agg; + } + + verifyMessage(signature: Signature, messageHash: Uint8Array): boolean { + return this.value.verify(signature.value, messageHash); + } + + toBytes(): Buffer { + return Buffer.from(this.value.serialize()); + } + + toHex(): string { + return bytesToHex(this.toBytes()); + } +} diff --git a/src/herumi/signature.ts b/src/herumi/signature.ts new file mode 100644 index 0000000..dd804c2 --- /dev/null +++ b/src/herumi/signature.ts @@ -0,0 +1,69 @@ +import assert from "assert"; +import {SIGNATURE_LENGTH, EMPTY_SIGNATURE} from "../constants"; +import {SignatureType} from "@chainsafe/eth2-bls-wasm"; +import {getContext} from "./context"; +import {PublicKey} from "./publicKey"; +import {bytesToHex, hexToBytes} from "../helpers/utils"; + +export class Signature { + readonly value: SignatureType; + + constructor(value: SignatureType) { + this.value = value; + assert(this.value.isValidOrder()); + } + + static fromBytes(bytes: Uint8Array): Signature { + assert(bytes.length === SIGNATURE_LENGTH, `Signature must have ${SIGNATURE_LENGTH} bytes`); + const context = getContext(); + const signature = new context.Signature(); + if (!EMPTY_SIGNATURE.equals(bytes)) { + signature.deserialize(bytes); + } + return new Signature(signature); + } + + static fromHex(hex: string): Signature { + return this.fromBytes(hexToBytes(hex)); + } + + static aggregate(signatures: Signature[]): Signature { + const context = getContext(); + const signature = new context.Signature(); + signature.aggregate(signatures.map((sig) => sig.value)); + return new Signature(signature); + } + + add(other: Signature): Signature { + const agg = this.value.clone(); + agg.add(other.value); + return new Signature(agg); + } + + verify(publicKey: PublicKey, message: Uint8Array): boolean { + return publicKey.value.verify(this.value, message); + } + + verifyAggregate(publicKeys: PublicKey[], message: Uint8Array): boolean { + return this.value.fastAggregateVerify( + publicKeys.map((key) => key.value), + message + ); + } + + verifyMultiple(publicKeys: PublicKey[], messages: Uint8Array[]): boolean { + const msgs = Buffer.concat(messages); + return this.value.aggregateVerifyNoCheck( + publicKeys.map((key) => key.value), + msgs + ); + } + + toBytes(): Buffer { + return Buffer.from(this.value.serialize()); + } + + toHex(): string { + return bytesToHex(this.toBytes()); + } +} diff --git a/src/web.ts b/src/herumi/web.ts similarity index 100% rename from src/web.ts rename to src/herumi/web.ts diff --git a/src/index.ts b/src/index.ts index 7dcedbf..f03d4ab 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,144 +1,3 @@ -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"; - -export {Keypair, PrivateKey, PublicKey, Signature}; - -export {init as initBLS} from "./context"; - -function toBuffer(input: Uint8Array): Buffer { - return Buffer.from(input.buffer, input.byteOffset, input.length); -} - -/** - * Generates new secret and public key - */ -export function generateKeyPair(): Keypair { - return Keypair.generate(); -} - -/** - * Generates public key from given secret. - * @param {BLSSecretKey} secretKey - */ -export function generatePublicKey(secretKey: Uint8Array): Buffer { - assert(secretKey, "secretKey is null or undefined"); - const keypair = new Keypair(PrivateKey.fromBytes(toBuffer(secretKey))); - return keypair.publicKey.toBytes(); -} - -/** - * Signs given message using secret key. - * @param secretKey - * @param messageHash - */ -export function sign(secretKey: Uint8Array, messageHash: Uint8Array): Buffer { - assert(secretKey, "secretKey is null or undefined"); - assert(messageHash, "messageHash is null or undefined"); - const privateKey = PrivateKey.fromBytes(toBuffer(secretKey)); - return privateKey.signMessage(toBuffer(messageHash)).toBytes(); -} - -/** - * Compines all given signature into one. - * @param signatures - */ -export function aggregateSignatures(signatures: Uint8Array[]): Buffer { - assert(signatures && signatures.length > 0, "signatures is null or undefined or empty array"); - // return Signature.aggregate( - // signatures.map( - // (signature): Signature => { - // return Signature.fromBytes(signature); - // } - // ) - // ).toBytes(); - return Buffer.alloc(0); -} - -/** - * Combines all given public keys into single one - * @param publicKeys - */ -export function aggregatePubkeys(publicKeys: Uint8Array[]): Buffer { - const agg = PublicKey.aggregate(publicKeys.map((p) => PublicKey.fromBytes(p))); - return agg.toBytes(); -} - -/** - * Verifies if signature is message signed with given public key. - * @param publicKey - * @param messageHash - * @param signature - */ -export function verify(publicKey: Uint8Array, messageHash: Uint8Array, signature: Uint8Array): boolean { - assert(publicKey, "publicKey is null or undefined"); - assert(messageHash, "messageHash is null or undefined"); - assert(signature, "signature is null or undefined"); - try { - return PublicKey.fromBytes(publicKey).verifyMessage( - Signature.fromBytes(toBuffer(signature)), - toBuffer(messageHash) - ); - } catch (e) { - return false; - } -} - -/** - * Verifies if aggregated signature is same message signed with given public keys. - * @param publicKeys - * @param messageHash - * @param signature - */ -export function verifyAggregate(publicKeys: Uint8Array[], messageHash: Uint8Array, signature: Uint8Array): boolean { - assert(publicKeys, "publicKey is null or undefined"); - assert(messageHash, "messageHash is null or undefined"); - assert(signature, "signature is null or undefined"); - try { - return Signature.fromBytes(signature).verifyAggregate( - publicKeys.map((pubkey) => PublicKey.fromBytes(pubkey)), - messageHash - ); - } catch (e) { - return false; - } -} - -/** - * Verifies if signature is list of message signed with corresponding public key. - * @param publicKeys - * @param messageHashes - * @param signature - * @param fast Check if all messages are different - */ -export function verifyMultiple(publicKeys: Uint8Array[], messageHashes: Uint8Array[], signature: Uint8Array): boolean { - assert(publicKeys, "publicKey is null or undefined"); - assert(messageHashes, "messageHash is null or undefined"); - assert(signature, "signature is null or undefined"); - - if (publicKeys.length === 0 || publicKeys.length != messageHashes.length) { - return false; - } - try { - return Signature.fromBytes(toBuffer(signature)).verifyMultiple( - publicKeys.map((key) => PublicKey.fromBytes(toBuffer(key))), - messageHashes.map((m) => toBuffer(m)) - ); - } catch (e) { - return false; - } -} - -export default { - generateKeyPair, - generatePublicKey, - sign, - aggregateSignatures, - aggregatePubkeys, - verify, - verifyAggregate, - verifyMultiple, -}; +import blst from "./blst"; +export default blst; +export * from "./blst"; diff --git a/src/signature.ts b/src/signature.ts deleted file mode 100644 index 60ae9f0..0000000 --- a/src/signature.ts +++ /dev/null @@ -1,53 +0,0 @@ -import * as blst from "@chainsafe/blst-ts"; -import {blst as blstBindings} from "@chainsafe/blst-ts/dist/bindings"; -import {bytesToHex, hexToBytes} from "./helpers/utils"; -import {PublicKey} from "./publicKey"; - -export class Signature { - readonly value: blst.Signature; - - constructor(value: blst.Signature) { - this.value = value; - } - - static fromBytes(bytes: Uint8Array): Signature { - return new Signature(blst.Signature.fromBytes(bytes)); - } - - static fromHex(hex: string): Signature { - return this.fromBytes(hexToBytes(hex)); - } - - static fromValue(signature: blst.Signature): Signature { - return new Signature(signature); - } - - static aggregate(signatures: Signature[]): Signature { - const agg = blst.AggregateSignature.fromSignatures(signatures.map((sig) => sig.value)); - return new Signature(agg.toSignature()); - } - - verifyAggregate(publicKeys: PublicKey[], message: Uint8Array): boolean { - return blst.fastAggregateVerify( - message, - publicKeys.map((pk) => pk.affine), - this.value - ); - } - - verifyMultiple(publicKeys: PublicKey[], messages: Uint8Array[]): boolean { - return blst.aggregateVerify( - messages, - publicKeys.map((pk) => pk.affine), - this.value - ); - } - - toBytes(): Buffer { - return Buffer.from(this.value.toBytes()); - } - - toHex(): string { - return bytesToHex(this.toBytes()); - } -} diff --git a/test/benchmark/index.ts b/test/benchmark/index.ts index bcda706..b726098 100644 --- a/test/benchmark/index.ts +++ b/test/benchmark/index.ts @@ -1,6 +1,5 @@ import crypto from "crypto"; -import * as blst from "@chainsafe/blst-ts"; -import {blst as blstBindings} from "@chainsafe/blst-ts/dist/bindings"; +import * as blst from "@chainsafe/blst"; import * as herumi from "../../src"; import {runBenchmark} from "./runner"; @@ -49,7 +48,7 @@ import {runBenchmark} from "./runner"; // Fast aggregate - runBenchmark<{pks: blst.PublicKey[]; msg: Uint8Array; sig: blst.Signature}, boolean>({ + runBenchmark<{pks: blst.AggregatePublicKey[]; msg: Uint8Array; sig: blst.Signature}, boolean>({ id: "BLST fastAggregateVerify", prepareTest: () => { @@ -57,7 +56,7 @@ import {runBenchmark} from "./runner"; const dataArr = range(aggCount).map(() => { const sk = blst.SecretKey.fromKeygen(crypto.randomBytes(32)); - const pk = sk.toPublicKey(); + const pk = sk.toAggregatePublicKey(); const sig = sk.sign(msg); return {pk, sig}; }); @@ -129,10 +128,7 @@ import {runBenchmark} from "./runner"; }; }, testRunner: (pks) => { - const p1Arr = pks.map((pk) => pk.value); - p1Arr.reduce((agg, pk) => { - return blstBindings.P1.add(agg, pk); - }); + blst.aggregatePubkeys(pks); }, }); diff --git a/test/params.ts b/test/params.ts new file mode 100644 index 0000000..513cc91 --- /dev/null +++ b/test/params.ts @@ -0,0 +1,2 @@ +import path from "path"; +export const SPEC_TESTS_DIR = path.join(__dirname, "../node_modules/@chainsafe/eth2-spec-tests/tests"); diff --git a/test/spec-blst/aggregate_sigs.test.ts b/test/spec-blst/aggregate_sigs.test.ts deleted file mode 100644 index 80a4eed..0000000 --- a/test/spec-blst/aggregate_sigs.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import path from "path"; -import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; -import {AggregateSignature} from "@chainsafe/blst-ts"; -import {fromHexString, toHexString} from "../util"; - -interface IAggregateSigsTestCase { - data: { - input: string[]; - output: string; - }; -} - -describeDirectorySpecTest( - "BLS - aggregate sigs", - path.join(__dirname, "../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/aggregate/small"), - (testCase) => { - const signaturesHex = testCase.data.input; - const signaturesBytes = signaturesHex.map(fromHexString); - const aggSig = AggregateSignature.fromSignaturesBytes(signaturesBytes); - const aggSigHex = aggSig.toSignature().toBytes(); - return toHexString(aggSigHex); - }, - { - inputTypes: { - data: InputType.YAML, - }, - getExpected: (testCase) => testCase.data.output, - } -); diff --git a/test/spec-blst/aggregate_sigs_verify.test.ts b/test/spec-blst/aggregate_sigs_verify.test.ts deleted file mode 100644 index de4cb74..0000000 --- a/test/spec-blst/aggregate_sigs_verify.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import path from "path"; -import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; -import {aggregateVerify, Signature, PublicKey} from "@chainsafe/blst-ts"; -import {fromHexString} from "../util"; - -interface IAggregateSigsVerifyTestCase { - data: { - input: { - pubkeys: string[]; - messages: string[]; - signature: string; - }; - output: boolean; - }; -} - -describeDirectorySpecTest( - "BLS - aggregate sigs verify", - path.join(__dirname, "../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/aggregate_verify/small"), - (testCase) => { - const {pubkeys, messages, signature} = testCase.data.input; - - try { - const msgs = messages.map(fromHexString); - const pks = pubkeys.map((pubkey) => PublicKey.fromBytes(fromHexString(pubkey))); - const sig = Signature.fromBytes(fromHexString(signature)); - - return aggregateVerify(msgs, pks, sig); - } catch (e) { - return false; - } - }, - { - inputTypes: { - data: InputType.YAML, - }, - getExpected: (testCase) => testCase.data.output, - } -); diff --git a/test/spec-blst/fast_aggregate_verify.test.ts b/test/spec-blst/fast_aggregate_verify.test.ts deleted file mode 100644 index 2982a73..0000000 --- a/test/spec-blst/fast_aggregate_verify.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import path from "path"; -import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; -import {fastAggregateVerify, Signature, PublicKey} from "@chainsafe/blst-ts"; -import {fromHexString} from "../util"; - -interface IAggregateSigsVerifyTestCase { - data: { - input: { - pubkeys: string[]; - message: string; - signature: string; - }; - output: boolean; - }; -} - -describeDirectorySpecTest( - "BLS - aggregate sigs verify", - path.join( - __dirname, - "../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/fast_aggregate_verify/small" - ), - (testCase) => { - const {pubkeys, message, signature} = testCase.data.input; - - try { - const msg = fromHexString(message); - const pks = pubkeys.map((pubkey) => PublicKey.fromBytes(fromHexString(pubkey))); - const sig = Signature.fromBytes(fromHexString(signature)); - - return fastAggregateVerify(msg, pks, sig); - } catch (e) { - return false; - } - }, - { - inputTypes: { - data: InputType.YAML, - }, - getExpected: (testCase) => testCase.data.output, - } -); diff --git a/test/spec-blst/sign.test.ts b/test/spec-blst/sign.test.ts deleted file mode 100644 index 8108ad8..0000000 --- a/test/spec-blst/sign.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import path from "path"; -import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; -import {SecretKey} from "@chainsafe/blst-ts"; -import {fromHexString, toHexString} from "../util"; - -interface ISignMessageTestCase { - data: { - input: { - privkey: string; - message: string; - }; - output: string; - }; -} - -describeDirectorySpecTest( - "BLS - sign", - path.join(__dirname, "../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/sign/small"), - (testCase) => { - const {privkey, message} = testCase.data.input; - - const sk = SecretKey.fromBytes(fromHexString(privkey)); - const msg = fromHexString(message); - const sig = sk.sign(msg); - - return toHexString(sig.toBytes()); - }, - { - inputTypes: { - data: InputType.YAML, - }, - getExpected: (testCase) => testCase.data.output, - } -); diff --git a/test/spec-blst/verify.test.ts b/test/spec-blst/verify.test.ts deleted file mode 100644 index fd914ac..0000000 --- a/test/spec-blst/verify.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import path from "path"; -import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; -import {verify, Signature, PublicKey} from "@chainsafe/blst-ts"; -import {fromHexString} from "../util"; - -interface IVerifyTestCase { - data: { - input: { - pubkey: string; - message: string; - signature: string; - }; - output: boolean; - }; -} - -describeDirectorySpecTest( - "BLS - verify", - path.join(__dirname, "../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/verify/small"), - (testCase) => { - const {pubkey, message, signature} = testCase.data.input; - - try { - const msg = fromHexString(message); - const pk = PublicKey.fromBytes(fromHexString(pubkey)); - const sig = Signature.fromBytes(fromHexString(signature)); - - return verify(msg, pk, sig); - } catch (e) { - return false; - } - }, - { - inputTypes: { - data: InputType.YAML, - }, - getExpected: (testCase) => testCase.data.output, - } -); diff --git a/test/spec/aggregate_sigs.test.ts b/test/spec/aggregate_sigs.test.ts index 70f69da..2baa1e7 100644 --- a/test/spec/aggregate_sigs.test.ts +++ b/test/spec/aggregate_sigs.test.ts @@ -1,6 +1,8 @@ import path from "path"; -import bls, {initBLS} from "../../src"; import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; +import {bytesToHex, hexToBytes} from "../../src/helpers/utils"; +import {SPEC_TESTS_DIR} from "../params"; +import {forEachImplementation} from "../switch"; interface IAggregateSigsTestCase { data: { @@ -9,32 +11,23 @@ interface IAggregateSigsTestCase { }; } -before(async function f() { - await initBLS(); -}); - -describeDirectorySpecTest( - "BLS - aggregate sigs", - path.join(__dirname, "../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/aggregate/small"), - (testCase) => { - try { - const result = bls.aggregateSignatures( - testCase.data.input.map((pubKey) => { - return Buffer.from(pubKey.replace("0x", ""), "hex"); - }) - ); - return `0x${result.toString("hex")}`; - } catch (e) { - if (e.message === "signatures is null or undefined or empty array") { - return null; +forEachImplementation((bls, implementation) => { + describeDirectorySpecTest( + `${implementation} - bls/aggregate/small`, + path.join(SPEC_TESTS_DIR, "general/phase0/bls/aggregate/small"), + (testCase) => { + try { + const signatures = testCase.data.input; + const agg = bls.aggregateSignatures(signatures.map(hexToBytes)); + return bytesToHex(agg); + } catch (e) { + if (e.message === "EMPTY_AGGREGATE_ARRAY") return null; + throw e; } - throw e; - } - }, - { - inputTypes: { - data: InputType.YAML, }, - getExpected: (testCase) => testCase.data.output, - } -); + { + inputTypes: {data: InputType.YAML}, + getExpected: (testCase) => testCase.data.output, + } + ); +}); diff --git a/test/spec/aggregate_sigs_verify.test.ts b/test/spec/aggregate_sigs_verify.test.ts index 8079830..fd56bbb 100644 --- a/test/spec/aggregate_sigs_verify.test.ts +++ b/test/spec/aggregate_sigs_verify.test.ts @@ -1,6 +1,8 @@ import path from "path"; -import bls, {initBLS} from "../../src"; import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; +import {hexToBytes} from "../../src/helpers/utils"; +import {SPEC_TESTS_DIR} from "../params"; +import {forEachImplementation} from "../switch"; interface IAggregateSigsVerifyTestCase { data: { @@ -13,30 +15,17 @@ interface IAggregateSigsVerifyTestCase { }; } -before(async function f() { - try { - await initBLS(); - } catch (e) { - console.log(e); - } -}); - -describeDirectorySpecTest( - "BLS - aggregate sigs verify", - path.join(__dirname, "../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/aggregate_verify/small"), - (testCase) => { - const pubkeys = testCase.data.input.pubkeys.map((pubkey) => { - return Buffer.from(pubkey.replace("0x", ""), "hex"); - }); - const messages = testCase.data.input.messages.map((msg) => { - return Buffer.from(msg.replace("0x", ""), "hex"); - }); - return bls.verifyMultiple(pubkeys, messages, Buffer.from(testCase.data.input.signature.replace("0x", ""), "hex")); - }, - { - inputTypes: { - data: InputType.YAML, +forEachImplementation((bls, implementation) => { + describeDirectorySpecTest( + `${implementation} - bls/aggregate_verify/small`, + path.join(SPEC_TESTS_DIR, "general/phase0/bls/aggregate_verify/small"), + (testCase) => { + const {pubkeys, messages, signature} = testCase.data.input; + return bls.verifyMultiple(pubkeys.map(hexToBytes), messages.map(hexToBytes), hexToBytes(signature)); }, - getExpected: (testCase) => testCase.data.output, - } -); + { + inputTypes: {data: InputType.YAML}, + getExpected: (testCase) => testCase.data.output, + } + ); +}); diff --git a/test/spec/fast_aggregate_verify.test.ts b/test/spec/fast_aggregate_verify.test.ts index d4f8322..fe38c06 100644 --- a/test/spec/fast_aggregate_verify.test.ts +++ b/test/spec/fast_aggregate_verify.test.ts @@ -1,6 +1,8 @@ import path from "path"; -import bls, {initBLS} from "../../src"; import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; +import {hexToBytes} from "../../src/helpers/utils"; +import {SPEC_TESTS_DIR} from "../params"; +import {forEachImplementation} from "../switch"; interface IAggregateSigsVerifyTestCase { data: { @@ -13,31 +15,17 @@ interface IAggregateSigsVerifyTestCase { }; } -before(async function f() { - try { - await initBLS(); - } catch (e) { - console.log(e); - } -}); - -describeDirectorySpecTest( - "BLS - aggregate sigs verify", - path.join( - __dirname, - "../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/fast_aggregate_verify/small" - ), - (testCase) => { - return bls.verifyAggregate( - testCase.data.input.pubkeys.map((key) => Buffer.from(key.replace("0x", ""), "hex")), - Buffer.from(testCase.data.input.message.replace("0x", ""), "hex"), - Buffer.from(testCase.data.input.signature.replace("0x", ""), "hex") - ); - }, - { - inputTypes: { - data: InputType.YAML, +forEachImplementation((bls, implementation) => { + describeDirectorySpecTest( + `${implementation} - bls/fast_aggregate_verify/small`, + path.join(SPEC_TESTS_DIR, "general/phase0/bls/fast_aggregate_verify/small"), + (testCase) => { + const {pubkeys, message, signature} = testCase.data.input; + return bls.verifyAggregate(pubkeys.map(hexToBytes), hexToBytes(message), hexToBytes(signature)); }, - getExpected: (testCase) => testCase.data.output, - } -); + { + inputTypes: {data: InputType.YAML}, + getExpected: (testCase) => testCase.data.output, + } + ); +}); diff --git a/test/spec/sign.test.ts b/test/spec/sign.test.ts index 84d8e83..63856ba 100644 --- a/test/spec/sign.test.ts +++ b/test/spec/sign.test.ts @@ -1,6 +1,8 @@ import path from "path"; -import bls, {initBLS} from "../../src"; import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; +import {bytesToHex, hexToBytes} from "../../src/helpers/utils"; +import {SPEC_TESTS_DIR} from "../params"; +import {forEachImplementation} from "../switch"; interface ISignMessageTestCase { data: { @@ -12,24 +14,18 @@ interface ISignMessageTestCase { }; } -before(async function f() { - await initBLS(); -}); - -describeDirectorySpecTest( - "BLS - sign", - path.join(__dirname, "../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/sign/small"), - (testCase) => { - const signature = bls.sign( - Buffer.from(testCase.data.input.privkey.replace("0x", ""), "hex"), - Buffer.from(testCase.data.input.message.replace("0x", ""), "hex") - ); - return `0x${signature.toString("hex")}`; - }, - { - inputTypes: { - data: InputType.YAML, +forEachImplementation((bls, implementation) => { + describeDirectorySpecTest( + `${implementation} - bls/sign/small`, + path.join(SPEC_TESTS_DIR, "general/phase0/bls/sign/small"), + (testCase) => { + const {privkey, message} = testCase.data.input; + const signature = bls.sign(hexToBytes(privkey), hexToBytes(message)); + return bytesToHex(signature); }, - getExpected: (testCase) => testCase.data.output, - } -); + { + inputTypes: {data: InputType.YAML}, + getExpected: (testCase) => testCase.data.output, + } + ); +}); diff --git a/test/spec/verify.test.ts b/test/spec/verify.test.ts index 5f98fc8..d4adba2 100644 --- a/test/spec/verify.test.ts +++ b/test/spec/verify.test.ts @@ -1,6 +1,8 @@ import path from "path"; -import bls, {initBLS} from "../../src"; import {describeDirectorySpecTest, InputType} from "@chainsafe/lodestar-spec-test-util"; +import {hexToBytes} from "../../src/helpers/utils"; +import {SPEC_TESTS_DIR} from "../params"; +import {forEachImplementation} from "../switch"; interface IVerifyTestCase { data: { @@ -13,24 +15,17 @@ interface IVerifyTestCase { }; } -before(async function f() { - await initBLS(); -}); - -describeDirectorySpecTest( - "BLS - verify", - path.join(__dirname, "../../node_modules/@chainsafe/eth2-spec-tests/tests/general/phase0/bls/verify/small"), - (testCase) => { - return bls.verify( - Buffer.from(testCase.data.input.pubkey.replace("0x", ""), "hex"), - Buffer.from(testCase.data.input.message.replace("0x", ""), "hex"), - Buffer.from(testCase.data.input.signature.replace("0x", ""), "hex") - ); - }, - { - inputTypes: { - data: InputType.YAML, +forEachImplementation((bls, implementation) => { + describeDirectorySpecTest( + `${implementation} - bls/verify/small`, + path.join(SPEC_TESTS_DIR, "general/phase0/bls/verify/small"), + (testCase) => { + const {pubkey, message, signature} = testCase.data.input; + return bls.verify(hexToBytes(pubkey), hexToBytes(message), hexToBytes(signature)); }, - getExpected: (testCase) => testCase.data.output, - } -); + { + inputTypes: {data: InputType.YAML}, + getExpected: (testCase) => testCase.data.output, + } + ); +}); diff --git a/test/switch.ts b/test/switch.ts new file mode 100644 index 0000000..9b82587 --- /dev/null +++ b/test/switch.ts @@ -0,0 +1,30 @@ +import blst from "../src/blst"; +import herumi from "../src/herumi"; + +export type Implementation = "blst" | "herumi"; +export const implementations: Implementation[] = ["blst", "herumi"]; + +export function getBls(implementation: Implementation) { + switch (implementation) { + case "blst": + return blst; + case "herumi": + return herumi; + } +} + +export function forEachImplementation( + callback: (bls: ReturnType, implementation: Implementation) => void +): void { + for (const implementation of implementations) { + const bls = getBls(implementation); + + if (implementation === "herumi") { + before(async () => { + await bls.initBLS(); + }); + } + + callback(bls, implementation); + } +} diff --git a/test/unit/context.test.ts b/test/unit/context.test.ts deleted file mode 100644 index 1942c54..0000000 --- a/test/unit/context.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {init, getContext, destroy} from "../../src/context"; -import {expect} from "chai"; - -describe("bls wasm constext", function () { - afterEach(() => { - destroy(); - }); - - it("initializes and works", async function () { - await init(); - expect(getContext().getCurveOrder()).to.be.equal( - "52435875175126190479447740508185965837690552500527637822603658699938581184513" - ); - }); - - it("throws if context not initialized", async function () { - expect(() => getContext().getCurveOrder()).to.throw(); - }); -}); diff --git a/test/unit/index.test.ts b/test/unit/index.test.ts index 5298689..3aef062 100644 --- a/test/unit/index.test.ts +++ b/test/unit/index.test.ts @@ -1,23 +1,12 @@ -import bls, {aggregatePubkeys, aggregateSignatures, initBLS, Keypair, verify, verifyMultiple} from "../../src"; +import {aggregatePubkeys, aggregateSignatures, initBLS, Keypair, verify, verifyMultiple} from "../../src"; import SHA256 from "@chainsafe/as-sha256"; import {expect} from "chai"; -import {destroy} from "../../src/context"; describe("test bls", function () { before(async function () { await initBLS(); }); - after(function () { - destroy(); - }); - - describe("aggregate pubkey", function () { - it("should aggregate empty array", function () { - expect(bls.aggregatePubkeys([])).to.not.throw; - }); - }); - describe("verify", function () { it("should verify signature", () => { const keypair = Keypair.generate(); @@ -148,22 +137,18 @@ describe("test bls", function () { const signature3 = keypair3.privateKey.signMessage(message2); const signature4 = keypair4.privateKey.signMessage(message2); - const aggregatePubKey12 = bls.aggregatePubkeys([keypair1.publicKey.toBytes(), keypair2.publicKey.toBytes()]); + const aggregatePubKey12 = aggregatePubkeys([keypair1.publicKey.toBytes(), keypair2.publicKey.toBytes()]); - const aggregatePubKey34 = bls.aggregatePubkeys([keypair3.publicKey.toBytes(), keypair4.publicKey.toBytes()]); + const aggregatePubKey34 = aggregatePubkeys([keypair3.publicKey.toBytes(), keypair4.publicKey.toBytes()]); - const aggregateSignature = bls.aggregateSignatures([ + const aggregateSignature = aggregateSignatures([ signature1.toBytes(), signature2.toBytes(), signature3.toBytes(), signature4.toBytes(), ]); - const result = bls.verifyMultiple( - [aggregatePubKey12, aggregatePubKey34], - [message2, message1], - aggregateSignature - ); + const result = verifyMultiple([aggregatePubKey12, aggregatePubKey34], [message2, message1], aggregateSignature); expect(result).to.be.false; }); @@ -182,16 +167,16 @@ describe("test bls", function () { const signature3 = keypair3.privateKey.signMessage(message2); const signature4 = keypair4.privateKey.signMessage(message2); - const aggregatePubKey12 = bls.aggregatePubkeys([keypair1.publicKey.toBytes(), keypair2.publicKey.toBytes()]); + const aggregatePubKey12 = aggregatePubkeys([keypair1.publicKey.toBytes(), keypair2.publicKey.toBytes()]); - const aggregateSignature = bls.aggregateSignatures([ + const aggregateSignature = aggregateSignatures([ signature1.toBytes(), signature2.toBytes(), signature3.toBytes(), signature4.toBytes(), ]); - const result = bls.verifyMultiple([aggregatePubKey12], [message2, message1], aggregateSignature); + const result = verifyMultiple([aggregatePubKey12], [message2, message1], aggregateSignature); expect(result).to.be.false; }); @@ -202,7 +187,7 @@ describe("test bls", function () { const message1 = Buffer.from(SHA256.digest(Buffer.from("Test1"))); const message2 = Buffer.from(SHA256.digest(Buffer.from("Test2"))); - const result = bls.verifyMultiple([], [message2, message1], signature); + const result = verifyMultiple([], [message2, message1], signature); expect(result).to.be.false; }); diff --git a/test/unit/keypair.test.ts b/test/unit/keypair.test.ts index 984a684..ab4a9ef 100644 --- a/test/unit/keypair.test.ts +++ b/test/unit/keypair.test.ts @@ -1,10 +1,9 @@ -import {PrivateKey, PublicKey, Keypair} from "../../src"; +import {PrivateKey, PublicKey, Keypair, destroy, initBLS} from "../../src"; import {expect} from "chai"; -import {destroy, init} from "../../src/context"; describe("keypair", function () { before(async function () { - await init(); + await initBLS(); }); after(function () { diff --git a/test/unit/privateKey.test.ts b/test/unit/privateKey.test.ts index c02b479..81b131a 100644 --- a/test/unit/privateKey.test.ts +++ b/test/unit/privateKey.test.ts @@ -1,11 +1,9 @@ -import {PrivateKey} from "../../src"; +import {PrivateKey, initBLS, destroy, SECRET_KEY_LENGTH} from "../../src"; import {expect} from "chai"; -import {SECRET_KEY_LENGTH} from "../../src/constants"; -import {destroy, init} from "../../src/context"; describe("privateKey", function () { before(async function () { - await init(); + await initBLS(); }); after(function () { diff --git a/test/unit/publicKey.test.ts b/test/unit/publicKey.test.ts index 28091bf..17b3a50 100644 --- a/test/unit/publicKey.test.ts +++ b/test/unit/publicKey.test.ts @@ -1,10 +1,9 @@ -import {destroy, init} from "../../src/context"; -import {PublicKey, PrivateKey} from "../../src"; +import {PublicKey, PrivateKey, initBLS, destroy} from "../../src"; import {expect} from "chai"; describe("public key", function () { before(async function f() { - await init(); + await initBLS(); }); after(function () { diff --git a/yarn.lock b/yarn.lock index cc97422..b2a7e71 100644 --- a/yarn.lock +++ b/yarn.lock @@ -820,10 +820,10 @@ bip39 "^3.0.2" buffer "^5.4.3" -"@chainsafe/blst-ts@file:../blst-ts": - version "0.1.1" - dependencies: - node-fetch "^2.6.1" +"@chainsafe/blst@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@chainsafe/blst/-/blst-0.1.0.tgz#6aa0ea5e91a0f7ae2d4358fb432eb9a558a4de31" + integrity sha512-sJXrADkWNA06xM5udoPr7cdNgzSBAjHaN1XbwLkbJ637eVSsEzip+P7Uz0ajfUu2pVsqReoEOBnpFJpUOHAEtg== "@chainsafe/eth2-bls-wasm@^0.5.0": version "0.5.0" @@ -4417,11 +4417,6 @@ node-environment-flags@1.0.5: object.getownpropertydescriptors "^2.0.3" semver "^5.7.0" -node-fetch@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - node-libs-browser@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425"