Merge pull request #85 from ChainSafe/dapplion/multi-threading
Enable multi-threading for all implementations
This commit is contained in:
commit
0f895b4795
|
@ -86,29 +86,25 @@ import {aggCount, runs} from "./params";
|
||||||
|
|
||||||
// Verify multiple signatures
|
// Verify multiple signatures
|
||||||
|
|
||||||
await runBenchmark<{pks: PublicKey[]; msgs: Uint8Array[]; sigs: Signature[]}, boolean>({
|
await runBenchmark({
|
||||||
id: `${implementation} verifyMultipleSignatures (${aggCount})`,
|
id: `${implementation} verifyMultipleSignatures (${aggCount})`,
|
||||||
|
|
||||||
prepareTest: () => {
|
prepareTest: () => {
|
||||||
const dataArr = range(aggCount).map(() => {
|
const sets = range(aggCount).map(() => {
|
||||||
const sk = bls.SecretKey.fromKeygen();
|
const sk = bls.SecretKey.fromKeygen();
|
||||||
const pk = sk.toPublicKey();
|
const pk = sk.toPublicKey();
|
||||||
const msg = randomMessage();
|
const msg = randomMessage();
|
||||||
const sig = sk.sign(msg);
|
const sig = sk.sign(msg);
|
||||||
return {pk, msg, sig};
|
return {publicKey: pk, message: msg, signature: sig};
|
||||||
});
|
});
|
||||||
|
|
||||||
const pks = dataArr.map((data) => data.pk);
|
|
||||||
const msgs = dataArr.map((data) => data.msg);
|
|
||||||
const sigs = dataArr.map((data) => data.sig);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
input: {pks, msgs, sigs},
|
input: sets,
|
||||||
resultCheck: (valid) => valid === true,
|
resultCheck: (valid) => valid === true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
testRunner: ({pks, msgs, sigs}) => {
|
testRunner: (sets) => {
|
||||||
return bls.Signature.verifyMultipleSignatures(pks, msgs, sigs);
|
return bls.Signature.verifyMultipleSignatures(sets);
|
||||||
},
|
},
|
||||||
runs,
|
runs,
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,21 +12,17 @@ import {range, randomMessage} from "../test/util";
|
||||||
const pk = sk.toPublicKey();
|
const pk = sk.toPublicKey();
|
||||||
const msg = randomMessage();
|
const msg = randomMessage();
|
||||||
const sig = sk.sign(msg);
|
const sig = sk.sign(msg);
|
||||||
return {pk, msg, sig};
|
return {publicKey: pk, message: msg, signature: sig};
|
||||||
});
|
});
|
||||||
|
|
||||||
const pks = dataArr.map((data) => data.pk);
|
|
||||||
const msgs = dataArr.map((data) => data.msg);
|
|
||||||
const sigs = dataArr.map((data) => data.sig);
|
|
||||||
|
|
||||||
const startMulti = process.hrtime.bigint();
|
const startMulti = process.hrtime.bigint();
|
||||||
bls.Signature.verifyMultipleSignatures(pks, msgs, sigs);
|
bls.Signature.verifyMultipleSignatures(dataArr);
|
||||||
const endMulti = process.hrtime.bigint();
|
const endMulti = process.hrtime.bigint();
|
||||||
const diffMulti = endMulti - startMulti;
|
const diffMulti = endMulti - startMulti;
|
||||||
|
|
||||||
const startSingle = process.hrtime.bigint();
|
const startSingle = process.hrtime.bigint();
|
||||||
for (const {pk, msg, sig} of dataArr) {
|
for (const {publicKey, message, signature} of dataArr) {
|
||||||
sig.verify(pk, msg);
|
signature.verify(publicKey, message);
|
||||||
}
|
}
|
||||||
const endSingle = process.hrtime.bigint();
|
const endSingle = process.hrtime.bigint();
|
||||||
const diffSingle = endSingle - startSingle;
|
const diffSingle = endSingle - startSingle;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
noble-bls12-381@^0.6.1:
|
noble-bls12-381@^0.7.2:
|
||||||
version "0.6.1"
|
version "0.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/noble-bls12-381/-/noble-bls12-381-0.6.1.tgz#b44bb5443b4b5c409723f19a8288155f0b3ad126"
|
resolved "https://registry.yarnpkg.com/noble-bls12-381/-/noble-bls12-381-0.7.2.tgz#9a9384891569ba32785d6e4ff8588b783487eae4"
|
||||||
integrity sha512-Dt0lq24ez75HqOqNIsxxbzfY7YOuwArtE3H6Clp1XbwnY4Ga1OjFbTaXq5aDBE3+ab1wLK11s0b3yR3+RiWWqw==
|
integrity sha512-Z5isbU6opuWPL3dxsGqO5BdOE8WP1XUM7HFIn/xeE5pATTnml/PEIy4MFQQrktHiitkuJdsCDtzEOnS9eIpC3Q==
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
"randombytes": "^2.1.0"
|
"randombytes": "^2.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@chainsafe/blst": "^0.1.6",
|
"@chainsafe/blst": "^0.2.0",
|
||||||
"@chainsafe/lodestar-spec-test-util": "^0.18.0",
|
"@chainsafe/lodestar-spec-test-util": "^0.18.0",
|
||||||
"@types/chai": "^4.2.9",
|
"@types/chai": "^4.2.9",
|
||||||
"@types/mocha": "^8.0.4",
|
"@types/mocha": "^8.0.4",
|
||||||
|
@ -67,6 +67,7 @@
|
||||||
"mocha": "^8.2.1",
|
"mocha": "^8.2.1",
|
||||||
"nyc": "^15.0.0",
|
"nyc": "^15.0.0",
|
||||||
"prettier": "^2.1.2",
|
"prettier": "^2.1.2",
|
||||||
|
"threads": "^1.6.3",
|
||||||
"ts-loader": "^6.2.1",
|
"ts-loader": "^6.2.1",
|
||||||
"ts-node": "^8.6.2",
|
"ts-node": "^8.6.2",
|
||||||
"typescript": "^3.7.5",
|
"typescript": "^3.7.5",
|
||||||
|
@ -78,6 +79,6 @@
|
||||||
"v8-profiler-next": "1.3.0"
|
"v8-profiler-next": "1.3.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@chainsafe/blst": "^0.1.6"
|
"@chainsafe/blst": "^0.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ export function destroy(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const bls: IBls = {
|
export const bls: IBls = {
|
||||||
|
implementation: "blst-native",
|
||||||
SecretKey,
|
SecretKey,
|
||||||
PublicKey,
|
PublicKey,
|
||||||
Signature,
|
Signature,
|
||||||
|
|
|
@ -1,25 +1,21 @@
|
||||||
import * as blst from "@chainsafe/blst";
|
import * as blst from "@chainsafe/blst";
|
||||||
import {EmptyAggregateError, ZeroPublicKeyError} from "../errors";
|
import {EmptyAggregateError, ZeroPublicKeyError} from "../errors";
|
||||||
import {bytesToHex, hexToBytes} from "../helpers";
|
import {bytesToHex, hexToBytes} from "../helpers";
|
||||||
import {PublicKey as IPublicKey} from "../interface";
|
import {PointFormat, PublicKey as IPublicKey} from "../interface";
|
||||||
|
|
||||||
export class PublicKey implements IPublicKey {
|
export class PublicKey extends blst.PublicKey implements IPublicKey {
|
||||||
readonly affine: blst.PublicKey;
|
constructor(value: ConstructorParameters<typeof blst.PublicKey>[0]) {
|
||||||
readonly jacobian: blst.AggregatePublicKey;
|
super(value);
|
||||||
|
|
||||||
constructor(affine: blst.PublicKey, jacobian: blst.AggregatePublicKey) {
|
|
||||||
this.affine = affine;
|
|
||||||
this.jacobian = jacobian;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromBytes(bytes: Uint8Array): PublicKey {
|
/** @param type Defaults to `CoordType.jacobian` */
|
||||||
const affine = blst.PublicKey.fromBytes(bytes);
|
static fromBytes(bytes: Uint8Array, type?: blst.CoordType): PublicKey {
|
||||||
if (affine.value.is_inf()) {
|
const pk = blst.PublicKey.fromBytes(bytes, type);
|
||||||
|
if (pk.value.is_inf()) {
|
||||||
throw new ZeroPublicKeyError();
|
throw new ZeroPublicKeyError();
|
||||||
}
|
}
|
||||||
|
|
||||||
const jacobian = blst.AggregatePublicKey.fromPublicKey(affine);
|
return new PublicKey(pk.value);
|
||||||
return new PublicKey(affine, jacobian);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromHex(hex: string): PublicKey {
|
static fromHex(hex: string): PublicKey {
|
||||||
|
@ -31,16 +27,19 @@ export class PublicKey implements IPublicKey {
|
||||||
throw new EmptyAggregateError();
|
throw new EmptyAggregateError();
|
||||||
}
|
}
|
||||||
|
|
||||||
const jacobian = blst.aggregatePubkeys(publicKeys.map((pk) => pk.jacobian));
|
const pk = blst.aggregatePubkeys(publicKeys);
|
||||||
const affine = jacobian.toPublicKey();
|
return new PublicKey(pk.value);
|
||||||
return new PublicKey(affine, jacobian);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toBytes(): Uint8Array {
|
toBytes(format?: PointFormat): Uint8Array {
|
||||||
return this.affine.toBytes();
|
if (format === PointFormat.uncompressed) {
|
||||||
|
return this.value.serialize();
|
||||||
|
} else {
|
||||||
|
return this.value.compress();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toHex(): string {
|
toHex(format?: PointFormat): string {
|
||||||
return bytesToHex(this.toBytes());
|
return bytesToHex(this.toBytes(format));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import {ZeroSecretKeyError} from "../errors";
|
||||||
|
|
||||||
export class SecretKey implements ISecretKey {
|
export class SecretKey implements ISecretKey {
|
||||||
readonly value: blst.SecretKey;
|
readonly value: blst.SecretKey;
|
||||||
|
|
||||||
constructor(value: blst.SecretKey) {
|
constructor(value: blst.SecretKey) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
@ -33,13 +32,12 @@ export class SecretKey implements ISecretKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
sign(message: Uint8Array): Signature {
|
sign(message: Uint8Array): Signature {
|
||||||
return new Signature(this.value.sign(message));
|
return new Signature(this.value.sign(message).value);
|
||||||
}
|
}
|
||||||
|
|
||||||
toPublicKey(): PublicKey {
|
toPublicKey(): PublicKey {
|
||||||
const jacobian = this.value.toAggregatePublicKey();
|
const pk = this.value.toPublicKey();
|
||||||
const affine = jacobian.toPublicKey();
|
return new PublicKey(pk.value);
|
||||||
return new PublicKey(affine, jacobian);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toBytes(): Uint8Array {
|
toBytes(): Uint8Array {
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
import * as blst from "@chainsafe/blst";
|
import * as blst from "@chainsafe/blst";
|
||||||
import {bytesToHex, hexToBytes} from "../helpers";
|
import {bytesToHex, hexToBytes} from "../helpers";
|
||||||
import {Signature as ISignature} from "../interface";
|
import {PointFormat, Signature as ISignature} from "../interface";
|
||||||
import {PublicKey} from "./publicKey";
|
import {PublicKey} from "./publicKey";
|
||||||
import {EmptyAggregateError, ZeroSignatureError} from "../errors";
|
import {EmptyAggregateError, ZeroSignatureError} from "../errors";
|
||||||
|
|
||||||
export class Signature implements ISignature {
|
export class Signature extends blst.Signature implements ISignature {
|
||||||
readonly affine: blst.Signature;
|
constructor(value: ConstructorParameters<typeof blst.Signature>[0]) {
|
||||||
|
super(value);
|
||||||
constructor(value: blst.Signature) {
|
|
||||||
this.affine = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromBytes(bytes: Uint8Array): Signature {
|
/** @param type Defaults to `CoordType.affine` */
|
||||||
return new Signature(blst.Signature.fromBytes(bytes));
|
static fromBytes(bytes: Uint8Array, type?: blst.CoordType, validate?: boolean): Signature {
|
||||||
|
const sig = blst.Signature.fromBytes(bytes, type);
|
||||||
|
if (validate) sig.sigValidate();
|
||||||
|
return new Signature(sig.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromHex(hex: string): Signature {
|
static fromHex(hex: string): Signature {
|
||||||
|
@ -24,55 +25,53 @@ export class Signature implements ISignature {
|
||||||
throw new EmptyAggregateError();
|
throw new EmptyAggregateError();
|
||||||
}
|
}
|
||||||
|
|
||||||
const agg = blst.AggregateSignature.fromSignatures(signatures.map((sig) => sig.affine));
|
const agg = blst.aggregateSignatures(signatures);
|
||||||
return new Signature(agg.toSignature());
|
return new Signature(agg.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static verifyMultipleSignatures(publicKeys: PublicKey[], messages: Uint8Array[], signatures: Signature[]): boolean {
|
static verifyMultipleSignatures(sets: {publicKey: PublicKey; message: Uint8Array; signature: Signature}[]): boolean {
|
||||||
return blst.verifyMultipleAggregateSignatures(
|
return blst.verifyMultipleAggregateSignatures(
|
||||||
messages,
|
sets.map((s) => ({msg: s.message, pk: s.publicKey, sig: s.signature}))
|
||||||
publicKeys.map((publicKey) => publicKey.affine),
|
|
||||||
signatures.map((signature) => signature.affine)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(publicKey: PublicKey, message: Uint8Array): boolean {
|
verify(publicKey: PublicKey, message: Uint8Array): boolean {
|
||||||
// Individual infinity signatures are NOT okay. Aggregated signatures MAY be infinity
|
// Individual infinity signatures are NOT okay. Aggregated signatures MAY be infinity
|
||||||
if (this.affine.value.is_inf()) {
|
if (this.value.is_inf()) {
|
||||||
throw new ZeroSignatureError();
|
throw new ZeroSignatureError();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.aggregateVerify([message], [publicKey.affine]);
|
return blst.verify(message, publicKey, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyAggregate(publicKeys: PublicKey[], message: Uint8Array): boolean {
|
verifyAggregate(publicKeys: PublicKey[], message: Uint8Array): boolean {
|
||||||
const agg = PublicKey.aggregate(publicKeys);
|
return blst.fastAggregateVerify(message, publicKeys, this);
|
||||||
return this.aggregateVerify([message], [agg.affine]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyMultiple(publicKeys: PublicKey[], messages: Uint8Array[]): boolean {
|
verifyMultiple(publicKeys: PublicKey[], messages: Uint8Array[]): boolean {
|
||||||
return this.aggregateVerify(
|
return blst.aggregateVerify(messages, publicKeys, this);
|
||||||
messages,
|
|
||||||
publicKeys.map((pk) => pk.affine)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toBytes(): Uint8Array {
|
toBytes(format?: PointFormat): Uint8Array {
|
||||||
return this.affine.toBytes();
|
if (format === PointFormat.uncompressed) {
|
||||||
|
return this.value.serialize();
|
||||||
|
} else {
|
||||||
|
return this.value.compress();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toHex(): string {
|
toHex(format?: PointFormat): string {
|
||||||
return bytesToHex(this.toBytes());
|
return bytesToHex(this.toBytes(format));
|
||||||
}
|
}
|
||||||
|
|
||||||
private aggregateVerify(msgs: Uint8Array[], pks: blst.PublicKey[]): boolean {
|
private aggregateVerify(msgs: Uint8Array[], pks: blst.PublicKey[]): boolean {
|
||||||
// If this set is simply an infinity signature and infinity publicKey then skip verification.
|
// If this set is simply an infinity signature and infinity publicKey then skip verification.
|
||||||
// This has the effect of always declaring that this sig/publicKey combination is valid.
|
// This has the effect of always declaring that this sig/publicKey combination is valid.
|
||||||
// for Eth2.0 specs tests
|
// for Eth2.0 specs tests
|
||||||
if (this.affine.value.is_inf() && pks.length === 1 && pks[0].value.is_inf()) {
|
if (this.value.is_inf() && pks.length === 1 && pks[0].value.is_inf()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return blst.aggregateVerify(msgs, pks, this.affine);
|
return blst.aggregateVerify(msgs, pks, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export const SECRET_KEY_LENGTH = 32;
|
export const SECRET_KEY_LENGTH = 32;
|
||||||
export const SIGNATURE_LENGTH = 96;
|
export const PUBLIC_KEY_LENGTH_COMPRESSED = 48;
|
||||||
export const FP_POINT_LENGTH = 48;
|
export const PUBLIC_KEY_LENGTH_UNCOMPRESSED = 48 * 2;
|
||||||
export const PUBLIC_KEY_LENGTH = FP_POINT_LENGTH;
|
export const SIGNATURE_LENGTH_COMPRESSED = 96;
|
||||||
export const G2_HASH_PADDING = 16;
|
export const SIGNATURE_LENGTH_UNCOMPRESSED = 96 * 2;
|
||||||
|
|
|
@ -40,6 +40,6 @@ export class InvalidOrderError extends Error {
|
||||||
|
|
||||||
export class InvalidLengthError extends Error {
|
export class InvalidLengthError extends Error {
|
||||||
constructor(arg: string, length: number) {
|
constructor(arg: string, length: number) {
|
||||||
super(`INVALID_LENGTH: ${arg} must have ${length} bytes`);
|
super(`INVALID_LENGTH: ${arg} - ${length} bytes`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,22 +117,17 @@ export function functionalInterfaceFactory({
|
||||||
* https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
|
* https://ethresear.ch/t/fast-verification-of-multiple-bls-signatures/5407
|
||||||
*/
|
*/
|
||||||
function verifyMultipleSignatures(
|
function verifyMultipleSignatures(
|
||||||
publicKeys: Uint8Array[],
|
sets: {publicKey: Uint8Array; message: Uint8Array; signature: Uint8Array}[]
|
||||||
messages: Uint8Array[],
|
|
||||||
signatures: Uint8Array[]
|
|
||||||
): boolean {
|
): boolean {
|
||||||
validateBytes(publicKeys, "publicKey");
|
if (!sets) throw Error("sets is null or undefined");
|
||||||
validateBytes(messages, "message");
|
|
||||||
validateBytes(signatures, "signatures");
|
|
||||||
|
|
||||||
if (publicKeys.length === 0 || publicKeys.length !== messages.length || publicKeys.length !== signatures.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
return Signature.verifyMultipleSignatures(
|
return Signature.verifyMultipleSignatures(
|
||||||
publicKeys.map((publicKey) => PublicKey.fromBytes(publicKey)),
|
sets.map((s) => ({
|
||||||
messages.map((msg) => msg),
|
publicKey: PublicKey.fromBytes(s.publicKey),
|
||||||
signatures.map((signature) => Signature.fromBytes(signature))
|
message: s.message,
|
||||||
|
signature: Signature.fromBytes(s.signature),
|
||||||
|
}))
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof NotInitializedError) throw e;
|
if (e instanceof NotInitializedError) throw e;
|
||||||
|
|
|
@ -9,6 +9,7 @@ export * from "../constants";
|
||||||
export {SecretKey, PublicKey, Signature, init, destroy};
|
export {SecretKey, PublicKey, Signature, init, destroy};
|
||||||
|
|
||||||
export const bls: IBls = {
|
export const bls: IBls = {
|
||||||
|
implementation: "herumi",
|
||||||
SecretKey,
|
SecretKey,
|
||||||
PublicKey,
|
PublicKey,
|
||||||
Signature,
|
Signature,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import {PublicKeyType} from "bls-eth-wasm";
|
import {PublicKeyType} from "bls-eth-wasm";
|
||||||
import {getContext} from "./context";
|
import {getContext} from "./context";
|
||||||
import {PUBLIC_KEY_LENGTH} from "../constants";
|
|
||||||
import {bytesToHex, hexToBytes, isZeroUint8Array} from "../helpers";
|
import {bytesToHex, hexToBytes, isZeroUint8Array} from "../helpers";
|
||||||
import {PublicKey as IPublicKey} from "../interface";
|
import {PointFormat, PublicKey as IPublicKey} from "../interface";
|
||||||
import {EmptyAggregateError, InvalidLengthError, ZeroPublicKeyError} from "../errors";
|
import {EmptyAggregateError, InvalidLengthError, ZeroPublicKeyError} from "../errors";
|
||||||
|
import {PUBLIC_KEY_LENGTH_COMPRESSED, PUBLIC_KEY_LENGTH_UNCOMPRESSED} from "../constants";
|
||||||
|
|
||||||
export class PublicKey implements IPublicKey {
|
export class PublicKey implements IPublicKey {
|
||||||
readonly value: PublicKeyType;
|
readonly value: PublicKeyType;
|
||||||
|
@ -17,14 +17,16 @@ export class PublicKey implements IPublicKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromBytes(bytes: Uint8Array): PublicKey {
|
static fromBytes(bytes: Uint8Array): PublicKey {
|
||||||
if (bytes.length !== PUBLIC_KEY_LENGTH) {
|
|
||||||
throw new InvalidLengthError("PublicKey", PUBLIC_KEY_LENGTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
const context = getContext();
|
const context = getContext();
|
||||||
const publicKey = new context.PublicKey();
|
const publicKey = new context.PublicKey();
|
||||||
if (!isZeroUint8Array(bytes)) {
|
if (!isZeroUint8Array(bytes)) {
|
||||||
|
if (bytes.length === PUBLIC_KEY_LENGTH_COMPRESSED) {
|
||||||
publicKey.deserialize(bytes);
|
publicKey.deserialize(bytes);
|
||||||
|
} else if (bytes.length === PUBLIC_KEY_LENGTH_UNCOMPRESSED) {
|
||||||
|
publicKey.deserializeUncompressed(bytes);
|
||||||
|
} else {
|
||||||
|
throw new InvalidLengthError("PublicKey", bytes.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return new PublicKey(publicKey);
|
return new PublicKey(publicKey);
|
||||||
}
|
}
|
||||||
|
@ -45,11 +47,15 @@ export class PublicKey implements IPublicKey {
|
||||||
return agg;
|
return agg;
|
||||||
}
|
}
|
||||||
|
|
||||||
toBytes(): Uint8Array {
|
toBytes(format?: PointFormat): Uint8Array {
|
||||||
|
if (format === PointFormat.uncompressed) {
|
||||||
|
return this.value.serializeUncompressed();
|
||||||
|
} else {
|
||||||
return this.value.serialize();
|
return this.value.serialize();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toHex(): string {
|
toHex(format?: PointFormat): string {
|
||||||
return bytesToHex(this.toBytes());
|
return bytesToHex(this.toBytes(format));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import {SIGNATURE_LENGTH} from "../constants";
|
|
||||||
import {SignatureType, multiVerify} from "bls-eth-wasm";
|
import {SignatureType, multiVerify} from "bls-eth-wasm";
|
||||||
import {getContext} from "./context";
|
import {getContext} from "./context";
|
||||||
import {PublicKey} from "./publicKey";
|
import {PublicKey} from "./publicKey";
|
||||||
import {bytesToHex, concatUint8Arrays, hexToBytes, isZeroUint8Array} from "../helpers";
|
import {bytesToHex, concatUint8Arrays, hexToBytes, isZeroUint8Array} from "../helpers";
|
||||||
import {Signature as ISignature} from "../interface";
|
import {PointFormat, Signature as ISignature} from "../interface";
|
||||||
import {EmptyAggregateError, InvalidLengthError, InvalidOrderError} from "../errors";
|
import {EmptyAggregateError, InvalidLengthError, InvalidOrderError} from "../errors";
|
||||||
|
import {SIGNATURE_LENGTH_COMPRESSED, SIGNATURE_LENGTH_UNCOMPRESSED} from "../constants";
|
||||||
|
|
||||||
export class Signature implements ISignature {
|
export class Signature implements ISignature {
|
||||||
readonly value: SignatureType;
|
readonly value: SignatureType;
|
||||||
|
@ -18,13 +18,16 @@ export class Signature implements ISignature {
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromBytes(bytes: Uint8Array): Signature {
|
static fromBytes(bytes: Uint8Array): Signature {
|
||||||
if (bytes.length !== SIGNATURE_LENGTH) {
|
|
||||||
throw new InvalidLengthError("Signature", SIGNATURE_LENGTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
const context = getContext();
|
const context = getContext();
|
||||||
const signature = new context.Signature();
|
const signature = new context.Signature();
|
||||||
if (!isZeroUint8Array(bytes)) {
|
if (!isZeroUint8Array(bytes)) {
|
||||||
|
if (bytes.length === SIGNATURE_LENGTH_COMPRESSED) {
|
||||||
|
signature.deserialize(bytes);
|
||||||
|
} else if (bytes.length === SIGNATURE_LENGTH_UNCOMPRESSED) {
|
||||||
|
signature.deserializeUncompressed(bytes);
|
||||||
|
} else {
|
||||||
|
throw new InvalidLengthError("Signature", bytes.length);
|
||||||
|
}
|
||||||
signature.deserialize(bytes);
|
signature.deserialize(bytes);
|
||||||
}
|
}
|
||||||
return new Signature(signature);
|
return new Signature(signature);
|
||||||
|
@ -45,11 +48,11 @@ export class Signature implements ISignature {
|
||||||
return new Signature(signature);
|
return new Signature(signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
static verifyMultipleSignatures(publicKeys: PublicKey[], messages: Uint8Array[], signatures: Signature[]): boolean {
|
static verifyMultipleSignatures(sets: {publicKey: PublicKey; message: Uint8Array; signature: Signature}[]): boolean {
|
||||||
return multiVerify(
|
return multiVerify(
|
||||||
publicKeys.map((publicKey) => publicKey.value),
|
sets.map((s) => s.publicKey.value),
|
||||||
signatures.map((signature) => signature.value),
|
sets.map((s) => s.signature.value),
|
||||||
messages
|
sets.map((s) => s.message)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,11 +74,15 @@ export class Signature implements ISignature {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBytes(): Uint8Array {
|
toBytes(format?: PointFormat): Uint8Array {
|
||||||
|
if (format === PointFormat.uncompressed) {
|
||||||
|
return this.value.serializeUncompressed();
|
||||||
|
} else {
|
||||||
return this.value.serialize();
|
return this.value.serialize();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toHex(): string {
|
toHex(format?: PointFormat): string {
|
||||||
return bytesToHex(this.toBytes());
|
return bytesToHex(this.toBytes(format));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,8 @@
|
||||||
export interface IBls {
|
export interface IBls {
|
||||||
SecretKey: {
|
implementation: Implementation;
|
||||||
fromBytes(bytes: Uint8Array): SecretKey;
|
SecretKey: Omit<typeof SecretKey, "prototype">;
|
||||||
fromHex(hex: string): SecretKey;
|
PublicKey: Omit<typeof PublicKey, "prototype">;
|
||||||
fromKeygen(ikm?: Uint8Array): SecretKey;
|
Signature: Omit<typeof Signature, "prototype">;
|
||||||
};
|
|
||||||
PublicKey: {
|
|
||||||
fromBytes(bytes: Uint8Array): PublicKey;
|
|
||||||
fromHex(hex: string): PublicKey;
|
|
||||||
aggregate(publicKeys: PublicKey[]): PublicKey;
|
|
||||||
};
|
|
||||||
Signature: {
|
|
||||||
fromBytes(bytes: Uint8Array): Signature;
|
|
||||||
fromHex(hex: string): Signature;
|
|
||||||
aggregate(signatures: Signature[]): Signature;
|
|
||||||
verifyMultipleSignatures(publicKeys: PublicKey[], messages: Uint8Array[], signatures: Signature[]): boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
sign(secretKey: Uint8Array, message: Uint8Array): Uint8Array;
|
sign(secretKey: Uint8Array, message: Uint8Array): Uint8Array;
|
||||||
aggregatePublicKeys(publicKeys: Uint8Array[]): Uint8Array;
|
aggregatePublicKeys(publicKeys: Uint8Array[]): Uint8Array;
|
||||||
|
@ -22,7 +10,7 @@ export interface IBls {
|
||||||
verify(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array): boolean;
|
verify(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array): boolean;
|
||||||
verifyAggregate(publicKeys: Uint8Array[], message: Uint8Array, signature: Uint8Array): boolean;
|
verifyAggregate(publicKeys: Uint8Array[], message: Uint8Array, signature: Uint8Array): boolean;
|
||||||
verifyMultiple(publicKeys: Uint8Array[], messages: Uint8Array[], signature: Uint8Array): boolean;
|
verifyMultiple(publicKeys: Uint8Array[], messages: Uint8Array[], signature: Uint8Array): boolean;
|
||||||
verifyMultipleSignatures(publicKeys: Uint8Array[], messages: Uint8Array[], signatures: Uint8Array[]): boolean;
|
verifyMultipleSignatures(sets: {publicKey: Uint8Array; message: Uint8Array; signature: Uint8Array}[]): boolean;
|
||||||
secretKeyToPublicKey(secretKey: Uint8Array): Uint8Array;
|
secretKeyToPublicKey(secretKey: Uint8Array): Uint8Array;
|
||||||
|
|
||||||
init(): Promise<void>;
|
init(): Promise<void>;
|
||||||
|
@ -40,21 +28,37 @@ export declare class SecretKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class PublicKey {
|
export declare class PublicKey {
|
||||||
static fromBytes(bytes: Uint8Array): PublicKey;
|
/** @param type Only for impl `blst-native`. Defaults to `CoordType.jacobian` */
|
||||||
|
static fromBytes(bytes: Uint8Array, type?: CoordType): PublicKey;
|
||||||
static fromHex(hex: string): PublicKey;
|
static fromHex(hex: string): PublicKey;
|
||||||
static aggregate(publicKeys: PublicKey[]): PublicKey;
|
static aggregate(publicKeys: PublicKey[]): PublicKey;
|
||||||
toBytes(): Uint8Array;
|
/** @param format Defaults to `PointFormat.compressed` */
|
||||||
toHex(): string;
|
toBytes(format?: PointFormat): Uint8Array;
|
||||||
|
toHex(format?: PointFormat): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class Signature {
|
export declare class Signature {
|
||||||
static fromBytes(bytes: Uint8Array): Signature;
|
/** @param type Only for impl `blst-native`. Defaults to `CoordType.affine` */
|
||||||
|
static fromBytes(bytes: Uint8Array, type?: CoordType, validate?: boolean): Signature;
|
||||||
static fromHex(hex: string): Signature;
|
static fromHex(hex: string): Signature;
|
||||||
static aggregate(signatures: Signature[]): Signature;
|
static aggregate(signatures: Signature[]): Signature;
|
||||||
static verifyMultipleSignatures(publicKeys: PublicKey[], messages: Uint8Array[], signatures: Signature[]): boolean;
|
static verifyMultipleSignatures(sets: {publicKey: PublicKey; message: Uint8Array; signature: Signature}[]): boolean;
|
||||||
verify(publicKey: PublicKey, message: Uint8Array): boolean;
|
verify(publicKey: PublicKey, message: Uint8Array): boolean;
|
||||||
verifyAggregate(publicKeys: PublicKey[], message: Uint8Array): boolean;
|
verifyAggregate(publicKeys: PublicKey[], message: Uint8Array): boolean;
|
||||||
verifyMultiple(publicKeys: PublicKey[], messages: Uint8Array[]): boolean;
|
verifyMultiple(publicKeys: PublicKey[], messages: Uint8Array[]): boolean;
|
||||||
toBytes(): Uint8Array;
|
/** @param format Defaults to `PointFormat.compressed` */
|
||||||
toHex(): string;
|
toBytes(format?: PointFormat): Uint8Array;
|
||||||
|
toHex(format?: PointFormat): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Implementation = "herumi" | "blst-native";
|
||||||
|
|
||||||
|
export enum PointFormat {
|
||||||
|
compressed = "compressed",
|
||||||
|
uncompressed = "uncompressed",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CoordType {
|
||||||
|
affine,
|
||||||
|
jacobian,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {expect} from "chai";
|
import {expect} from "chai";
|
||||||
import {IBls} from "../../src/interface";
|
import {IBls, PointFormat} from "../../src/interface";
|
||||||
import {getN, randomMessage} from "../util";
|
import {getN, randomMessage, hexToBytesNode} from "../util";
|
||||||
import {hexToBytes} from "../../src/helpers";
|
import {hexToBytes} from "../../src/helpers";
|
||||||
import {maliciousVerifyMultipleSignaturesData} from "../data/malicious-signature-test-data";
|
import {maliciousVerifyMultipleSignaturesData} from "../data/malicious-signature-test-data";
|
||||||
|
|
||||||
|
@ -96,24 +96,23 @@ export function runIndexTests(bls: IBls): void {
|
||||||
describe("verifyMultipleSignatures", () => {
|
describe("verifyMultipleSignatures", () => {
|
||||||
it("Should verify multiple signatures", () => {
|
it("Should verify multiple signatures", () => {
|
||||||
const n = 4;
|
const n = 4;
|
||||||
const dataArr = getN(n, () => {
|
const sets = getN(n, () => {
|
||||||
const sk = bls.SecretKey.fromKeygen();
|
const sk = bls.SecretKey.fromKeygen();
|
||||||
const pk = sk.toPublicKey();
|
const publicKey = sk.toPublicKey();
|
||||||
const msg = randomMessage();
|
const message = randomMessage();
|
||||||
const sig = sk.sign(msg);
|
const signature = sk.sign(message);
|
||||||
return {pk, msg, sig};
|
return {publicKey, message, signature};
|
||||||
});
|
});
|
||||||
const pks = dataArr.map((data) => data.pk);
|
|
||||||
const msgs = dataArr.map((data) => data.msg);
|
|
||||||
const sigs = dataArr.map((data) => data.sig);
|
|
||||||
|
|
||||||
expect(bls.Signature.verifyMultipleSignatures(pks, msgs, sigs)).to.equal(true, "class interface failed");
|
expect(bls.Signature.verifyMultipleSignatures(sets)).to.equal(true, "class interface failed");
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
bls.verifyMultipleSignatures(
|
bls.verifyMultipleSignatures(
|
||||||
pks.map((pk) => pk.toBytes()),
|
sets.map((s) => ({
|
||||||
msgs,
|
publicKey: s.publicKey.toBytes(),
|
||||||
sigs.map((sig) => sig.toBytes())
|
message: s.message,
|
||||||
|
signature: s.signature.toBytes(),
|
||||||
|
}))
|
||||||
)
|
)
|
||||||
).to.equal(true, "functional (bytes serialized) interface failed");
|
).to.equal(true, "functional (bytes serialized) interface failed");
|
||||||
});
|
});
|
||||||
|
@ -138,11 +137,47 @@ export function runIndexTests(bls: IBls): void {
|
||||||
"Malicious signature should be validated with bls.verifyMultiple"
|
"Malicious signature should be validated with bls.verifyMultiple"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const maliciousSets = pks.map((_, i) => ({
|
||||||
|
publicKey: pks[i],
|
||||||
|
message: msgs[i],
|
||||||
|
signature: sigs[i],
|
||||||
|
}));
|
||||||
|
|
||||||
// This method is expected to catch the malicious signature and not verify
|
// This method is expected to catch the malicious signature and not verify
|
||||||
expect(bls.Signature.verifyMultipleSignatures(pks, msgs, sigs)).to.equal(
|
expect(bls.Signature.verifyMultipleSignatures(maliciousSets)).to.equal(
|
||||||
false,
|
false,
|
||||||
"Malicous signature should not validate with bls.verifyMultipleSignatures"
|
"Malicous signature should not validate with bls.verifyMultipleSignatures"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("serialize deserialize", () => {
|
||||||
|
/* eslint-disable max-len */
|
||||||
|
|
||||||
|
const skHex = "0x0101010101010101010101010101010101010101010101010101010101010101";
|
||||||
|
const pkHexCompExpected =
|
||||||
|
"0xaa1a1c26055a329817a5759d877a2795f9499b97d6056edde0eea39512f24e8bc874b4471f0501127abb1ea0d9f68ac1";
|
||||||
|
const pkHexUncompExpected =
|
||||||
|
"0x0a1a1c26055a329817a5759d877a2795f9499b97d6056edde0eea39512f24e8bc874b4471f0501127abb1ea0d9f68ac111392125a1c3750363c2c97d9650fb78696e6428db8ff9efaf0471cbfd20324916ab545746db83756d335e92f9e8c8b8";
|
||||||
|
|
||||||
|
it("Should serialize comp pubkey", () => {
|
||||||
|
const sk = bls.SecretKey.fromBytes(hexToBytesNode(skHex));
|
||||||
|
const pkHexComp = sk.toPublicKey().toHex(PointFormat.compressed);
|
||||||
|
expect(pkHexComp).to.equal(pkHexCompExpected, "Wrong pkHexComp");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should serialize uncomp pubkey", () => {
|
||||||
|
const sk = bls.SecretKey.fromBytes(hexToBytesNode(skHex));
|
||||||
|
const pkHexUncomp = sk.toPublicKey().toHex(PointFormat.uncompressed);
|
||||||
|
expect(pkHexUncomp).to.equal(pkHexUncompExpected, "Wrong pkHexUncomp");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should deserialize comp pubkey", () => {
|
||||||
|
bls.PublicKey.fromHex(pkHexCompExpected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should deserialize uncomp pubkey", () => {
|
||||||
|
bls.PublicKey.fromHex(pkHexUncompExpected);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import {expect} from "chai";
|
||||||
|
import {chunkify} from "./utils";
|
||||||
|
|
||||||
|
describe("chunkify", () => {
|
||||||
|
const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15];
|
||||||
|
|
||||||
|
const results = {
|
||||||
|
0: [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15]],
|
||||||
|
1: [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15]],
|
||||||
|
2: [
|
||||||
|
[0, 1, 2, 3, 4, 5, 6, 7],
|
||||||
|
[8, 9, 10, 12, 13, 14, 15],
|
||||||
|
],
|
||||||
|
3: [
|
||||||
|
[0, 1, 2, 3, 4],
|
||||||
|
[5, 6, 7, 8, 9],
|
||||||
|
[10, 12, 13, 14, 15],
|
||||||
|
],
|
||||||
|
4: [
|
||||||
|
[0, 1, 2, 3],
|
||||||
|
[4, 5, 6, 7],
|
||||||
|
[8, 9, 10, 12],
|
||||||
|
[13, 14, 15],
|
||||||
|
],
|
||||||
|
5: [
|
||||||
|
[0, 1, 2],
|
||||||
|
[3, 4, 5],
|
||||||
|
[6, 7, 8],
|
||||||
|
[9, 10, 12],
|
||||||
|
[13, 14, 15],
|
||||||
|
],
|
||||||
|
6: [
|
||||||
|
[0, 1, 2],
|
||||||
|
[3, 4, 5],
|
||||||
|
[6, 7, 8],
|
||||||
|
[9, 10, 12],
|
||||||
|
[13, 14, 15],
|
||||||
|
],
|
||||||
|
7: [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 12], [13, 14], [15]],
|
||||||
|
8: [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 12], [13, 14], [15]],
|
||||||
|
};
|
||||||
|
|
||||||
|
const testCases: {
|
||||||
|
id: string;
|
||||||
|
n: number;
|
||||||
|
arr: number[];
|
||||||
|
expectArr: number[][];
|
||||||
|
}[] = Object.entries(results).map(([i, expectArr]) => ({
|
||||||
|
id: i,
|
||||||
|
n: parseInt(i),
|
||||||
|
arr,
|
||||||
|
expectArr,
|
||||||
|
}));
|
||||||
|
|
||||||
|
for (const {id, arr, n, expectArr} of testCases) {
|
||||||
|
it(id, () => {
|
||||||
|
expect(chunkify(arr, n)).to.deep.equal(expectArr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,47 @@
|
||||||
|
import {spawn, Pool, Worker, Thread} from "threads";
|
||||||
|
import {Implementation, PointFormat, PublicKey, Signature} from "../../../../src";
|
||||||
|
import {WorkerApi} from "./worker";
|
||||||
|
|
||||||
|
type ThreadType = {
|
||||||
|
[K in keyof WorkerApi]: (...args: Parameters<WorkerApi[K]>) => Promise<ReturnType<WorkerApi[K]>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class BlsMultiThreadNaive {
|
||||||
|
impl: Implementation;
|
||||||
|
pool: Pool<Thread & ThreadType>;
|
||||||
|
format: PointFormat;
|
||||||
|
|
||||||
|
constructor(impl: Implementation, workerCount?: number) {
|
||||||
|
this.impl = impl;
|
||||||
|
// Use compressed for herumi for now.
|
||||||
|
// THe worker is not able to deserialize from uncompressed
|
||||||
|
// `Error: err _wrapDeserialize`
|
||||||
|
this.format = impl === "blst-native" ? PointFormat.uncompressed : PointFormat.compressed;
|
||||||
|
this.pool = Pool(() => (spawn(new Worker("./worker")) as any) as Promise<Thread & ThreadType>, workerCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
async destroy(): Promise<void> {
|
||||||
|
await this.pool.terminate(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async verify(pk: PublicKey, msg: Uint8Array, sig: Signature): Promise<boolean> {
|
||||||
|
return this.pool.queue((worker) =>
|
||||||
|
worker.verify(this.impl, pk.toBytes(PointFormat.uncompressed), msg, sig.toBytes(PointFormat.uncompressed))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyMultipleAggregateSignatures(
|
||||||
|
sets: {publicKey: PublicKey; message: Uint8Array; signature: Signature}[]
|
||||||
|
): Promise<boolean> {
|
||||||
|
return this.pool.queue((worker) =>
|
||||||
|
worker.verifyMultipleAggregateSignatures(
|
||||||
|
this.impl,
|
||||||
|
sets.map((s) => ({
|
||||||
|
publicKey: s.publicKey.toBytes(PointFormat.uncompressed),
|
||||||
|
message: s.message,
|
||||||
|
signature: s.signature.toBytes(PointFormat.uncompressed),
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
import {expect} from "chai";
|
||||||
|
import {IBls, PublicKey, Signature} from "../../../../src";
|
||||||
|
import {BlsMultiThreadNaive} from "./index";
|
||||||
|
|
||||||
|
export function runMultithreadTests(bls: IBls): void {
|
||||||
|
describe("bls pool naive", function () {
|
||||||
|
// Starting all threads may take a while due to ts-node compilation
|
||||||
|
this.timeout(20 * 1000);
|
||||||
|
|
||||||
|
const n = 16;
|
||||||
|
let pool: BlsMultiThreadNaive;
|
||||||
|
|
||||||
|
before("Create pool and warm-up wallets", async function () {
|
||||||
|
pool = new BlsMultiThreadNaive(bls.implementation);
|
||||||
|
});
|
||||||
|
|
||||||
|
after("Destroy pool", async function () {
|
||||||
|
await pool.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("1 msg, 1 pk", function () {
|
||||||
|
const msg = Buffer.from("sample-msg");
|
||||||
|
const sk = bls.SecretKey.fromKeygen(Buffer.alloc(32, 1));
|
||||||
|
const pk = sk.toPublicKey();
|
||||||
|
const sig = sk.sign(msg);
|
||||||
|
|
||||||
|
it("verify", async function () {
|
||||||
|
if (bls.implementation === "herumi") this.skip();
|
||||||
|
|
||||||
|
const validArr = await Promise.all(
|
||||||
|
Array.from({length: 32}, (i) => i).map(async () => pool.verify(pk, msg, sig))
|
||||||
|
);
|
||||||
|
for (const [i, valid] of validArr.entries()) {
|
||||||
|
expect(valid).to.equal(true, `Invalid ${i}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("N msgs, N pks", function () {
|
||||||
|
const sets: {publicKey: PublicKey; message: Uint8Array; signature: Signature}[] = [];
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
const message = Buffer.alloc(32, i);
|
||||||
|
const sk = bls.SecretKey.fromKeygen(Buffer.alloc(32, i));
|
||||||
|
sets.push({message, publicKey: sk.toPublicKey(), signature: sk.sign(message)});
|
||||||
|
}
|
||||||
|
|
||||||
|
it("verify", async function () {
|
||||||
|
if (bls.implementation === "herumi") this.skip();
|
||||||
|
|
||||||
|
const validArr = await Promise.all(sets.map((s) => pool.verify(s.publicKey, s.message, s.signature)));
|
||||||
|
for (const [i, valid] of validArr.entries()) {
|
||||||
|
expect(valid).to.equal(true, `Invalid ${i}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("verifyMultipleAggregateSignatures", async function () {
|
||||||
|
if (bls.implementation === "herumi") this.skip();
|
||||||
|
|
||||||
|
const valid = await pool.verifyMultipleAggregateSignatures(sets);
|
||||||
|
expect(valid).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
export function chunkify<T>(arr: T[], chunkCount: number): T[][] {
|
||||||
|
const chunkSize = Math.round(arr.length / chunkCount);
|
||||||
|
const arrArr: T[][] = [];
|
||||||
|
|
||||||
|
for (let i = 0, j = arr.length; i < j; i += chunkSize) {
|
||||||
|
arrArr.push(arr.slice(i, i + chunkSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
return arrArr;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import {expose} from "threads/worker";
|
||||||
|
import {bls, init, CoordType, Implementation} from "../../../../src";
|
||||||
|
|
||||||
|
export type WorkerApi = typeof workerApi;
|
||||||
|
|
||||||
|
const workerApi = {
|
||||||
|
async verify(impl: Implementation, publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array) {
|
||||||
|
await init(impl);
|
||||||
|
const pk = bls.PublicKey.fromBytes(publicKey, CoordType.affine);
|
||||||
|
const sig = bls.Signature.fromBytes(signature, CoordType.affine, true);
|
||||||
|
return sig.verify(pk, message);
|
||||||
|
},
|
||||||
|
async verifyMultipleAggregateSignatures(
|
||||||
|
impl: Implementation,
|
||||||
|
sets: {publicKey: Uint8Array; message: Uint8Array; signature: Uint8Array}[]
|
||||||
|
) {
|
||||||
|
await init(impl);
|
||||||
|
return bls.Signature.verifyMultipleSignatures(
|
||||||
|
sets.map((s) => ({
|
||||||
|
publicKey: bls.PublicKey.fromBytes(s.publicKey, CoordType.affine),
|
||||||
|
message: s.message,
|
||||||
|
signature: bls.Signature.fromBytes(s.signature, CoordType.affine, true),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expose(workerApi);
|
|
@ -1,6 +1,7 @@
|
||||||
import {runSecretKeyTests} from "./secretKey.test";
|
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 {describeForAllImplementations} from "../switch";
|
import {describeForAllImplementations} from "../switch";
|
||||||
|
|
||||||
// Import test's bls lib lazily to prevent breaking test with Karma
|
// Import test's bls lib lazily to prevent breaking test with Karma
|
||||||
|
@ -8,4 +9,5 @@ describeForAllImplementations((bls) => {
|
||||||
runSecretKeyTests(bls);
|
runSecretKeyTests(bls);
|
||||||
runPublicKeyTests(bls);
|
runPublicKeyTests(bls);
|
||||||
runIndexTests(bls);
|
runIndexTests(bls);
|
||||||
|
runMultithreadTests(bls);
|
||||||
});
|
});
|
||||||
|
|
51
yarn.lock
51
yarn.lock
|
@ -161,10 +161,10 @@
|
||||||
bls-eth-wasm "^0.4.4"
|
bls-eth-wasm "^0.4.4"
|
||||||
randombytes "^2.1.0"
|
randombytes "^2.1.0"
|
||||||
|
|
||||||
"@chainsafe/blst@^0.1.6":
|
"@chainsafe/blst@^0.2.0":
|
||||||
version "0.1.6"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@chainsafe/blst/-/blst-0.1.6.tgz#d933a1568d9e781cd13673d80ff1eaf40c955107"
|
resolved "https://registry.yarnpkg.com/@chainsafe/blst/-/blst-0.2.0.tgz#5e2d2707c2c0d56ff077a00179a5255eaca14099"
|
||||||
integrity sha512-iv1CASFce9T1QQB+pVznMXadEYZFw3x/YOgstjw14OoKGZSYvjupA7h7247u/ZHQguw7aO3jmVTEzHAwO8yQqw==
|
integrity sha512-eyyLm4C+Zhl18YwFa93J+xRSHj0NrBZodBO+z+aaREf71RnA7/EvOcAPVLpEW2CI7PsInhVne/ufb+A7gfHQrg==
|
||||||
dependencies:
|
dependencies:
|
||||||
node-fetch "^2.6.1"
|
node-fetch "^2.6.1"
|
||||||
node-gyp "^7.1.2"
|
node-gyp "^7.1.2"
|
||||||
|
@ -1171,7 +1171,7 @@ callsite@1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20"
|
resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20"
|
||||||
integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA=
|
integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA=
|
||||||
|
|
||||||
callsites@^3.0.0:
|
callsites@^3.0.0, callsites@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||||
|
@ -2133,6 +2133,11 @@ eslint@^6.8.0:
|
||||||
text-table "^0.2.0"
|
text-table "^0.2.0"
|
||||||
v8-compile-cache "^2.0.3"
|
v8-compile-cache "^2.0.3"
|
||||||
|
|
||||||
|
esm@^3.2.25:
|
||||||
|
version "3.2.25"
|
||||||
|
resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
|
||||||
|
integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
|
||||||
|
|
||||||
espree@^6.1.2:
|
espree@^6.1.2:
|
||||||
version "6.1.2"
|
version "6.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d"
|
resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d"
|
||||||
|
@ -3152,6 +3157,13 @@ is-number@^7.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||||
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
||||||
|
|
||||||
|
is-observable@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e"
|
||||||
|
integrity sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==
|
||||||
|
dependencies:
|
||||||
|
symbol-observable "^1.1.0"
|
||||||
|
|
||||||
is-plain-obj@^2.1.0:
|
is-plain-obj@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
|
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
|
||||||
|
@ -4216,6 +4228,11 @@ object.values@^1.1.0:
|
||||||
function-bind "^1.1.1"
|
function-bind "^1.1.1"
|
||||||
has "^1.0.3"
|
has "^1.0.3"
|
||||||
|
|
||||||
|
observable-fns@^0.5.1:
|
||||||
|
version "0.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/observable-fns/-/observable-fns-0.5.1.tgz#9b56478690dd0fa8603e3a7e7d2975d88bca0904"
|
||||||
|
integrity sha512-wf7g4Jpo1Wt2KIqZKLGeiuLOEMqpaOZ5gJn7DmSdqXgTdxRwSdBhWegQQpPteQ2gZvzCKqNNpwb853wcpA0j7A==
|
||||||
|
|
||||||
on-finished@~2.3.0:
|
on-finished@~2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
||||||
|
@ -5518,6 +5535,11 @@ supports-color@^7.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^4.0.0"
|
has-flag "^4.0.0"
|
||||||
|
|
||||||
|
symbol-observable@^1.1.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
||||||
|
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
|
||||||
|
|
||||||
table@^5.2.3:
|
table@^5.2.3:
|
||||||
version "5.4.6"
|
version "5.4.6"
|
||||||
resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
|
resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
|
||||||
|
@ -5588,6 +5610,18 @@ text-table@^0.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||||
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
|
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
|
||||||
|
|
||||||
|
threads@^1.6.3:
|
||||||
|
version "1.6.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/threads/-/threads-1.6.3.tgz#89324a93509403c90a169344023151ae1fe4986b"
|
||||||
|
integrity sha512-tKwFIWRgfAT85KGkrpDt2jWPO8IVH0sLNfB/pXad/VW9eUIY2Zlz+QyeizypXhPHv9IHfqRzvk2t3mPw+imhWw==
|
||||||
|
dependencies:
|
||||||
|
callsites "^3.1.0"
|
||||||
|
debug "^4.1.1"
|
||||||
|
is-observable "^1.1.0"
|
||||||
|
observable-fns "^0.5.1"
|
||||||
|
optionalDependencies:
|
||||||
|
tiny-worker ">= 2"
|
||||||
|
|
||||||
through2@^2.0.0:
|
through2@^2.0.0:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
|
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
|
||||||
|
@ -5608,6 +5642,13 @@ timers-browserify@^2.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
setimmediate "^1.0.4"
|
setimmediate "^1.0.4"
|
||||||
|
|
||||||
|
"tiny-worker@>= 2":
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tiny-worker/-/tiny-worker-2.3.0.tgz#715ae34304c757a9af573ae9a8e3967177e6011e"
|
||||||
|
integrity sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==
|
||||||
|
dependencies:
|
||||||
|
esm "^3.2.25"
|
||||||
|
|
||||||
tmp@0.0.33, tmp@0.0.x, tmp@^0.0.33:
|
tmp@0.0.33, tmp@0.0.x, tmp@^0.0.33:
|
||||||
version "0.0.33"
|
version "0.0.33"
|
||||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||||
|
|
Reference in New Issue