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"); + }); }); 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;