diff --git a/packages/crypto-config/source/configuration.test.ts b/packages/crypto-config/source/configuration.test.ts index 2e74520b4..b0a52c318 100644 --- a/packages/crypto-config/source/configuration.test.ts +++ b/packages/crypto-config/source/configuration.test.ts @@ -42,6 +42,21 @@ describe<{ ); }); + const withRoundValidators = (value: number) => ({ + ...cryptoJson, + milestones: cryptoJson.milestones.map((milestone, index) => + index === 1 ? { ...milestone, roundValidators: value } : milestone, + ), + }); + + it("should reject a milestone with roundValidators above the 64 wire-format cap", ({ configManager }) => { + assert.throws(() => configManager.setConfig(withRoundValidators(65))); + }); + + it("should accept a milestone with roundValidators at the 64 cap", ({ configManager }) => { + assert.not.throws(() => configManager.setConfig(withRoundValidators(64))); + }); + it("should throw on set before config is initialized", () => { const fresh = new Configuration(); diff --git a/packages/crypto-config/source/schemas.ts b/packages/crypto-config/source/schemas.ts index 9905d0e47..ceebb8161 100644 --- a/packages/crypto-config/source/schemas.ts +++ b/packages/crypto-config/source/schemas.ts @@ -77,7 +77,10 @@ const milestone: AnySchemaObject = { height: { minimum: 0, type: "integer" }, p2p: milestoneP2p, reward: { type: "string" }, - roundValidators: { minimum: 0, type: "integer" }, + // Hard ceiling: the consensus wire format packs the signer bitmap into a fixed uint64 + // (serializer.ts / validatorSetPack), so index 64 (the 65th validator) overflows on + // serialize. Guard it here until the wire format is redesigned as a variable bitset. + roundValidators: { maximum: 64, minimum: 0, type: "integer" }, satoshi: milestoneSatoshi, snapshot: milestoneSnapshot, timeouts: milestoneTimeouts,