Skip to content

Commit

Permalink
Merge pull request #167 from ChainSafe/mkeil/napi-rebuild-2
Browse files Browse the repository at this point in the history
feat: implement napi build
  • Loading branch information
matthewkeil authored Apr 11, 2024
2 parents 46ecf80 + bcaec4d commit 81150f2
Show file tree
Hide file tree
Showing 17 changed files with 736 additions and 310 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- name: Unit tests
run: yarn test:unit
- name: Download spec tests
run: yarn download-test-cases
run: yarn download-spec-tests
- name: Spec tests
run: yarn test:spec
- name: Web tests
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@chainsafe/bls",
"version": "7.1.3",
"version": "8.0.0",
"description": "Implementation of bls signature verification for ethereum 2.0",
"engines": {
"node": ">=18"
Expand Down Expand Up @@ -73,7 +73,7 @@
"test:coverage": "nyc --cache-dir .nyc_output/.cache -r lcov -e .ts mocha 'test/unit/**/*.test.ts' && nyc report",
"test:spec": "mocha 'test/spec/**/*.test.ts'",
"test": "yarn run test:unit && yarn run test:spec",
"download-test-cases": "node --loader ts-node/esm test/downloadSpecTests.ts",
"download-spec-tests": "node --loader ts-node/esm test/downloadSpecTests.ts",
"coverage": "codecov -F bls",
"benchmark": "node --loader ts-node/esm benchmark/index.ts",
"benchmark:all": "cd benchmark && yarn install && yarn benchmark:all"
Expand All @@ -83,7 +83,7 @@
"bls-eth-wasm": "^1.1.1"
},
"devDependencies": {
"@chainsafe/blst": "^0.2.4",
"@chainsafe/blst": "^1.0.0",
"@chainsafe/eslint-plugin-node": "^11.2.3",
"@chainsafe/threads": "^1.9.0",
"@lodestar/spec-test-util": "1.13.0",
Expand All @@ -106,6 +106,7 @@
"karma-spec-reporter": "^0.0.32",
"karma-webpack": "^5.0.0",
"mocha": "^10.0.0",
"null-loader": "^4.0.1",
"nyc": "^15.0.0",
"prettier": "^2.1.2",
"resolve-typescript-plugin": "^1.2.0",
Expand All @@ -120,7 +121,7 @@
"v8-profiler-next": "1.10.0"
},
"peerDependencies": {
"@chainsafe/blst": "^0.2.4"
"@chainsafe/blst": "^1.0.0"
},
"peerDependenciesMeta": {
"@chainsafe/blst": {
Expand Down
7 changes: 4 additions & 3 deletions src/blst-native/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import {SecretKey} from "./secretKey.js";
import {PublicKey} from "./publicKey.js";
import {Signature} from "./signature.js";
import {IBls} from "../types.js";
import {IBls, Implementation} from "../types.js";
import {functionalInterfaceFactory} from "../functional.js";
export * from "../constants.js";

export {SecretKey, PublicKey, Signature};

const implementation: Implementation = "blst-native";
export const bls: IBls = {
implementation: "blst-native",
implementation,
SecretKey,
PublicKey,
Signature,

...functionalInterfaceFactory({SecretKey, PublicKey, Signature}),
...functionalInterfaceFactory({implementation, SecretKey, PublicKey, Signature}),
};

export default bls;
36 changes: 23 additions & 13 deletions src/blst-native/publicKey.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import * as blst from "@chainsafe/blst";
import blst from "@chainsafe/blst";
import {EmptyAggregateError} from "../errors.js";
import {bytesToHex, hexToBytes} from "../helpers/index.js";
import {PointFormat, PublicKey as IPublicKey} from "../types.js";
import {CoordType, PointFormat, PublicKey as IPublicKey} from "../types.js";

export class PublicKey extends blst.PublicKey implements IPublicKey {
constructor(value: ConstructorParameters<typeof blst.PublicKey>[0]) {
super(value);
}
export class PublicKey implements IPublicKey {
private constructor(private readonly value: blst.PublicKey) {}

/** @param type Defaults to `CoordType.jacobian` */
static fromBytes(bytes: Uint8Array, type?: blst.CoordType, validate?: boolean): PublicKey {
const pk = blst.PublicKey.fromBytes(bytes, type);
static fromBytes(bytes: Uint8Array, type?: CoordType, validate = true): PublicKey {
// need to hack the CoordType so @chainsafe/blst is not a required dep
const pk = blst.PublicKey.deserialize(bytes, (type as unknown) as blst.CoordType);
if (validate) pk.keyValidate();
return new PublicKey(pk.value);
return new PublicKey(pk);
}

static fromHex(hex: string): PublicKey {
Expand All @@ -24,19 +23,30 @@ export class PublicKey extends blst.PublicKey implements IPublicKey {
throw new EmptyAggregateError();
}

const pk = blst.aggregatePubkeys(publicKeys);
return new PublicKey(pk.value);
const pk = blst.aggregatePublicKeys(publicKeys.map(({value}) => value));
return new PublicKey(pk);
}

/**
* Implemented for SecretKey to be able to call .toPublicKey()
*/
private static friendBuild(key: blst.PublicKey): PublicKey {
return new PublicKey(key);
}

toBytes(format?: PointFormat): Uint8Array {
if (format === PointFormat.uncompressed) {
return this.value.serialize();
return this.value.serialize(false);
} else {
return this.value.compress();
return this.value.serialize(true);
}
}

toHex(format?: PointFormat): string {
return bytesToHex(this.toBytes(format));
}

multiplyBy(bytes: Uint8Array): PublicKey {
return new PublicKey(this.value.multiplyBy(bytes));
}
}
17 changes: 8 additions & 9 deletions src/blst-native/secretKey.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as blst from "@chainsafe/blst";
import blst from "@chainsafe/blst";
import crypto from "crypto";
import {bytesToHex, hexToBytes, isZeroUint8Array} from "../helpers/index.js";
import {SECRET_KEY_LENGTH} from "../constants.js";
Expand All @@ -8,18 +8,15 @@ import {Signature} from "./signature.js";
import {ZeroSecretKeyError} from "../errors.js";

export class SecretKey implements ISecretKey {
readonly value: blst.SecretKey;
constructor(value: blst.SecretKey) {
this.value = value;
}
constructor(private readonly value: blst.SecretKey) {}

static fromBytes(bytes: Uint8Array): SecretKey {
// draft-irtf-cfrg-bls-signature-04 does not allow SK == 0
if (isZeroUint8Array(bytes)) {
throw new ZeroSecretKeyError();
}

const sk = blst.SecretKey.fromBytes(bytes);
const sk = blst.SecretKey.deserialize(bytes);
return new SecretKey(sk);
}

Expand All @@ -33,16 +30,18 @@ export class SecretKey implements ISecretKey {
}

sign(message: Uint8Array): Signature {
return new Signature(this.value.sign(message).value);
// @ts-expect-error Need to hack private constructor with static method
return Signature.friendBuild(this.value.sign(message));
}

toPublicKey(): PublicKey {
const pk = this.value.toPublicKey();
return new PublicKey(pk.value);
// @ts-expect-error Need to hack private constructor with static method
return PublicKey.friendBuild(pk);
}

toBytes(): Uint8Array {
return this.value.toBytes();
return this.value.serialize();
}

toHex(): string {
Expand Down
61 changes: 41 additions & 20 deletions src/blst-native/signature.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import * as blst from "@chainsafe/blst";
import blst from "@chainsafe/blst";
import {bytesToHex, hexToBytes} from "../helpers/index.js";
import {PointFormat, Signature as ISignature} from "../types.js";
import {CoordType, PointFormat, Signature as ISignature} from "../types.js";
import {PublicKey} from "./publicKey.js";
import {EmptyAggregateError, ZeroSignatureError} from "../errors.js";

export class Signature extends blst.Signature implements ISignature {
constructor(value: ConstructorParameters<typeof blst.Signature>[0]) {
super(value);
}
export class Signature implements ISignature {
private constructor(private readonly value: blst.Signature) {}

/** @param type Defaults to `CoordType.affine` */
static fromBytes(bytes: Uint8Array, type?: blst.CoordType, validate = true): Signature {
const sig = blst.Signature.fromBytes(bytes, type);
static fromBytes(bytes: Uint8Array, type?: CoordType, validate = true): Signature {
// need to hack the CoordType so @chainsafe/blst is not a required dep
const sig = blst.Signature.deserialize(bytes, (type as unknown) as blst.CoordType);
if (validate) sig.sigValidate();
return new Signature(sig.value);
return new Signature(sig);
}

static fromHex(hex: string): Signature {
Expand All @@ -25,53 +24,75 @@ export class Signature extends blst.Signature implements ISignature {
throw new EmptyAggregateError();
}

const agg = blst.aggregateSignatures(signatures);
return new Signature(agg.value);
const agg = blst.aggregateSignatures(signatures.map(({value}) => value));
return new Signature(agg);
}

static verifyMultipleSignatures(sets: {publicKey: PublicKey; message: Uint8Array; signature: Signature}[]): boolean {
return blst.verifyMultipleAggregateSignatures(
sets.map((s) => ({msg: s.message, pk: s.publicKey, sig: s.signature}))
// @ts-expect-error Need to hack type to get access to the private `value`
sets.map((s) => ({message: s.message, publicKey: s.publicKey.value, signature: s.signature.value}))
);
}

/**
* Implemented for SecretKey to be able to call .sign()
*/
private static friendBuild(sig: blst.Signature): Signature {
return new Signature(sig);
}

verify(publicKey: PublicKey, message: Uint8Array): boolean {
// Individual infinity signatures are NOT okay. Aggregated signatures MAY be infinity
if (this.value.is_inf()) {
if (this.value.isInfinity()) {
throw new ZeroSignatureError();
}

return blst.verify(message, publicKey, this);
// @ts-expect-error Need to hack type to get access to the private `value`
return blst.verify(message, publicKey.value, this.value);
}

verifyAggregate(publicKeys: PublicKey[], message: Uint8Array): boolean {
return blst.fastAggregateVerify(message, publicKeys, this);
return blst.fastAggregateVerify(
message,
// @ts-expect-error Need to hack type to get access to the private `value`
publicKeys.map((pk) => pk.value),
this.value
);
}

verifyMultiple(publicKeys: PublicKey[], messages: Uint8Array[]): boolean {
return blst.aggregateVerify(messages, publicKeys, this);
return this.aggregateVerify(
messages,
// @ts-expect-error Need to hack type to get access to the private `value`
publicKeys.map((pk) => pk.value)
);
}

toBytes(format?: PointFormat): Uint8Array {
if (format === PointFormat.uncompressed) {
return this.value.serialize();
return this.value.serialize(false);
} else {
return this.value.compress();
return this.value.serialize(true);
}
}

toHex(format?: PointFormat): string {
return bytesToHex(this.toBytes(format));
}

multiplyBy(bytes: Uint8Array): Signature {
return new Signature(this.value.multiplyBy(bytes));
}

private aggregateVerify(msgs: Uint8Array[], pks: blst.PublicKey[]): boolean {
// 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.
// for Eth2.0 specs tests
if (this.value.is_inf() && pks.length === 1 && pks[0].value.is_inf()) {
if (this.value.isInfinity() && pks.length === 1 && pks[0].isInfinity()) {
return true;
}

return blst.aggregateVerify(msgs, pks, this);
return blst.aggregateVerify(msgs, pks, this.value);
}
}
Loading

0 comments on commit 81150f2

Please sign in to comment.