From fec6881cdb5d75c2fc15a150a90187253a162abf Mon Sep 17 00:00:00 2001 From: oXtxNt9U <120286271+oXtxNt9U@users.noreply.github.com> Date: Thu, 12 Mar 2026 14:01:39 +0900 Subject: [PATCH 1/2] make validatorSetUnpack more strict --- packages/utils/source/validator-set-pack.ts | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/utils/source/validator-set-pack.ts b/packages/utils/source/validator-set-pack.ts index 68cebc71f0..118193e341 100644 --- a/packages/utils/source/validator-set-pack.ts +++ b/packages/utils/source/validator-set-pack.ts @@ -10,11 +10,26 @@ export const validatorSetPack = (validatorSet: boolean[]): bigint => { }; export const validatorSetUnpack = (packed: bigint, numberOfValidators: number): boolean[] => { - const validatorSet: boolean[] = new Array(numberOfValidators); + if (!Number.isInteger(numberOfValidators) || numberOfValidators < 0) { + throw new RangeError("`numberOfValidators` must be a non-negative integer"); + } + + if (packed < 0n) { + throw new RangeError("`packed` must be non-negative"); + } + + if (packed >> BigInt(numberOfValidators) !== 0n) { + throw new RangeError("`packed` contains set bits beyond `numberOfValidators`"); + } + + const validatorSet: boolean[] = Array.from({ length: numberOfValidators }); + + let mask = 1n; for (let index = 0; index < numberOfValidators; index++) { - const mask = 2n ** BigInt(index); - const isSet = (packed & mask) > 0; + const isSet = (packed & mask) !== 0n; validatorSet[index] = isSet; + + mask <<= 1n; } return validatorSet; From 9834e432d6afb8aede06c25f2d8edaefd66fe000 Mon Sep 17 00:00:00 2001 From: oXtxNt9U <120286271+oXtxNt9U@users.noreply.github.com> Date: Thu, 12 Mar 2026 14:23:54 +0900 Subject: [PATCH 2/2] tests --- .../utils/source/validator-set-pack.test.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/utils/source/validator-set-pack.test.ts b/packages/utils/source/validator-set-pack.test.ts index c3c4d7f2f8..1a9c69b5c6 100644 --- a/packages/utils/source/validator-set-pack.test.ts +++ b/packages/utils/source/validator-set-pack.test.ts @@ -10,4 +10,37 @@ describe("validatorSet", async ({ assert, it }) => { it("should unpack it", () => { assert.equal(validatorSetUnpack(3n, 4), [true, true, false, false]); }); + + it("should pack and unpack", () => { + const validatorSet = [true, true, false, false]; + const packed = validatorSetPack(validatorSet); + const unpacked = validatorSetUnpack(packed, validatorSet.length); + + assert.equal(unpacked, validatorSet); + }); + + it("should pack and unpack empty", () => { + const validatorSet: boolean[] = []; + const packed = validatorSetPack(validatorSet); + const unpacked = validatorSetUnpack(packed, validatorSet.length); + + assert.equal(unpacked, validatorSet); + }); + + it("should reject invalid packed validator set", () => { + const validatorSet = [true, true, true, true]; + const packed = validatorSetPack(validatorSet); + + assert.throws(() => validatorSetUnpack(0n, -1), "`numberOfValidators` must be a non-negative integer"); + assert.throws(() => validatorSetUnpack(0n, -10), "`numberOfValidators` must be a non-negative integer"); + assert.throws(() => validatorSetUnpack(0n, 1.1), "`numberOfValidators` must be a non-negative integer"); + assert.throws(() => validatorSetUnpack(0n, -0.1), "`numberOfValidators` must be a non-negative integer"); + assert.throws(() => validatorSetUnpack(0n, 0.2), "`numberOfValidators` must be a non-negative integer"); + assert.throws( + () => validatorSetUnpack(0n, "4" as unknown as number), + "`numberOfValidators` must be a non-negative integer", + ); + assert.throws(() => validatorSetUnpack(packed, 3), "`packed` contains set bits beyond `numberOfValidators`"); + assert.throws(() => validatorSetUnpack(-1n, 3), "`packed` must be non-negative"); + }); });