Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 95 additions & 1 deletion packages/beacon-node/test/perf/bls/bls.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import crypto from "node:crypto";
import {bench, describe} from "@chainsafe/benchmark";
import {blsBatch} from "@chainsafe/lodestar-z/bls-batch";
import {
PublicKey,
SecretKey,
Expand All @@ -9,7 +10,8 @@ import {
verify,
verifyMultipleAggregateSignatures,
} from "@chainsafe/lodestar-z/blst";
import {linspace} from "../../../src/util/numpy.js";
import {pubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
import {linspace} from "../../../src/util/numpy.ts";

describe("BLS ops", () => {
type Keypair = {publicKey: PublicKey; secretKey: SecretKey};
Expand Down Expand Up @@ -127,3 +129,95 @@ describe("BLS ops", () => {
});
}
});

describe("BLS napi batching bindings", () => {
const maxKeys = 256;
blsBatch.init(40_000);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The number 40_000 used in blsBatch.init appears to be a magic number. It would improve readability and maintainability to define this value as a named constant, clearly indicating its purpose (e.g., MAX_BATCH_SIZE or INITIAL_CAPACITY).

Suggested change
blsBatch.init(40_000);
const maxKeys = 256;
const BLS_BATCH_INITIAL_CAPACITY = 40_000;
blsBatch.init(BLS_BATCH_INITIAL_CAPACITY);


const secretKeys: SecretKey[] = [];
for (let i = 0; i < maxKeys; i++) {
const bytes = new Uint8Array(32);
const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
dataView.setUint32(0, i + 1, true);
const sk = SecretKey.fromKeygen(bytes);
secretKeys.push(sk);
pubkeyCache.set(i, sk.toPublicKey().toBytes());
}

type IndexedSet = {index: number; message: Uint8Array; signature: Uint8Array};
type SingleSet = {publicKey: Uint8Array; message: Uint8Array; signature: Uint8Array};

function getIndexedSet(i: number): IndexedSet {
const message = Buffer.alloc(32, i + 1);
return {index: i, message, signature: secretKeys[i].sign(message).toBytes()};
}

function getSingleSet(i: number): SingleSet {
const message = Buffer.alloc(32, i + 1);
return {
publicKey: secretKeys[i].toPublicKey().toBytes(),
message,
signature: secretKeys[i].sign(message).toBytes(),
};
}

const seedMessage = crypto.randomBytes(32);
function getSameMessageSet(i: number): {index: number; signature: Uint8Array} {
return {index: i, signature: secretKeys[i].sign(seedMessage).toBytes()};
}

for (const count of [3, 8, 32, 64, 128]) {
bench({
id: `blsBatch.verify(indexed) ${count}`,
beforeEach: () => linspace(0, count - 1).map((i) => getIndexedSet(i)),
fn: (sets) => {
const isValid = blsBatch.verify(blsBatch.indexed, sets);
if (!isValid) throw Error("Invalid");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error message "Invalid" is very generic. If a verification fails in a benchmark, a more descriptive error message would be beneficial for debugging. Consider including details like the id of the benchmark or the count of items being verified.

        const isValid = blsBatch.verify(blsBatch.indexed, sets);
        if (!isValid) throw Error(`Invalid verification for blsBatch.verify(indexed) with count ${count}`);

},
});
}

for (const count of [3, 8, 32, 64, 128]) {
bench({
id: `blsBatch.verify(single) ${count}`,
beforeEach: () => linspace(0, count - 1).map((i) => getSingleSet(i)),
fn: (sets) => {
const isValid = blsBatch.verify(blsBatch.single, sets);
if (!isValid) throw Error("Invalid");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error message "Invalid" is very generic. If a verification fails in a benchmark, a more descriptive error message would be beneficial for debugging. Consider including details like the id of the benchmark or the count of items being verified.

        const isValid = blsBatch.verify(blsBatch.single, sets);
        if (!isValid) throw Error(`Invalid verification for blsBatch.verify(single) with count ${count}`);

},
});
}

for (const count of [3, 8, 32, 64, 128]) {
bench({
id: `blsBatch.asyncVerify(indexed) ${count}`,
beforeEach: () => linspace(0, count - 1).map((i) => getIndexedSet(i)),
fn: async (sets) => {
const isValid = await blsBatch.asyncVerify(blsBatch.indexed, sets);
if (!isValid) throw Error("Invalid");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error message "Invalid" is very generic. If a verification fails in a benchmark, a more descriptive error message would be beneficial for debugging. Consider including details like the id of the benchmark or the count of items being verified.

        const isValid = await blsBatch.asyncVerify(blsBatch.indexed, sets);
        if (!isValid) throw Error(`Invalid verification for blsBatch.asyncVerify(indexed) with count ${count}`);

},
});
}

for (const count of [3, 8, 32, 64, 128]) {
bench({
id: `blsBatch.asyncVerify(single) ${count}`,
beforeEach: () => linspace(0, count - 1).map((i) => getSingleSet(i)),
fn: (sets) => {
const isValid = blsBatch.asyncVerify(blsBatch.single, sets);
if (!isValid) throw Error("Invalid");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error message "Invalid" is very generic. If a verification fails in a benchmark, a more descriptive error message would be beneficial for debugging. Consider including details like the id of the benchmark or the count of items being verified.

        const isValid = blsBatch.asyncVerify(blsBatch.single, sets);
        if (!isValid) throw Error(`Invalid verification for blsBatch.asyncVerify(single) with count ${count}`);

},
});
}

for (const count of [3, 8, 32, 64, 128]) {
bench({
id: `blsBatch.asyncVerifySameMessage ${count}`,
beforeEach: () => linspace(0, count - 1).map((i) => getSameMessageSet(i)),
fn: async (sets) => {
const isValid = await blsBatch.asyncVerifySameMessage(sets, seedMessage);
if (!isValid) throw Error("Invalid");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error message "Invalid" is very generic. If a verification fails in a benchmark, a more descriptive error message would be beneficial for debugging. Consider including details like the id of the benchmark or the count of items being verified.

        const isValid = await blsBatch.asyncVerifySameMessage(sets, seedMessage);
        if (!isValid) throw Error(`Invalid verification for blsBatch.asyncVerifySameMessage with count ${count}`);

},
});
}
});
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {afterEach, beforeEach, describe, expect, it, vi} from "vitest";
import {PublicKey, SecretKey} from "@chainsafe/lodestar-z/blst";
import {getEmptyLogger} from "@lodestar/logger/empty";
import {ForkName} from "@lodestar/params";
import {SignatureSetType, getPubkeyCache} from "@lodestar/state-transition";
import {ssz} from "@lodestar/types";
import {getEmptyLogger} from "@lodestar/logger/empty";
import {BlsVerifier} from "../../../../../src/chain/bls/blsVerifier.js";
import {AttestationError, AttestationErrorCode, GossipAction} from "../../../../../src/chain/errors/index.js";
import {IBeaconChain} from "../../../../../src/chain/index.js";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {BitArray, toHexString} from "@chainsafe/ssz";
import {ExecutionStatus, IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
import {getEmptyLogger} from "@lodestar/logger/empty";
import {DOMAIN_BEACON_ATTESTER} from "@lodestar/params";
import {
DataAvailabilityStatus,
Expand All @@ -12,7 +13,6 @@ import {
generateTestCachedBeaconStateOnlyValidators,
getSecretKeyFromIndexCached,
} from "../../../../state-transition/test/perf/util.js";
import {getEmptyLogger} from "@lodestar/logger/empty";
import {BlsVerifier} from "../../../src/chain/bls/index.js";
import {IBeaconChain} from "../../../src/chain/index.js";
import {defaultChainOptions} from "../../../src/chain/options.js";
Expand Down
Loading