diff --git a/scripts/genesis/ValidatorManager.ts b/scripts/genesis/ValidatorManager.ts index 713546cd..ff98357a 100644 --- a/scripts/genesis/ValidatorManager.ts +++ b/scripts/genesis/ValidatorManager.ts @@ -22,7 +22,7 @@ import { enforceOperatorsNotProxyAdmin, schemaAddress, schemaBigInt, - schemaHex, + schemaBytes32, slotForAddressMap, slotForBytes32Map, slotIndex, @@ -54,7 +54,11 @@ export const schemaValidatorManager = z */ validators: z.array( z.object({ - publicKey: schemaHex, + /** + * Ed25519 public key, exactly 32 bytes. ValidatorRegistry.registerValidator + * rejects any other length with InvalidPublicKeyFormat, so the schema does too. + */ + publicKey: schemaBytes32, votingPower: schemaBigInt.max(UINT64_MAX), /** * Controllers authorized to manage this validator. Each controller is @@ -217,12 +221,8 @@ export const buildValidatorManagerGenesisAllocs = async (ctx: BuilderContext, co const idSetArraySlot = fromHex(keccak256(slotIndex(REGISTRY_STORAGE_LOCATION + 1n)), 'bigint') + BigInt(index) const idSetMapSlotHex = slotForBytes32Map(REGISTRY_STORAGE_LOCATION + 2n, registrationId) const validatorSlot = fromHex(slotForBytes32Map(REGISTRY_STORAGE_LOCATION + 0n, registrationId), 'bigint') - const publicKeyLength = BigInt(fromHex(validator.publicKey, 'bytes').length) - - if (publicKeyLength !== 32n) { - // Only support 32 bytes public key now. - throw new Error(`Public key must be 32 bytes`) - } + // schemaBytes32 already guarantees the key is exactly 32 bytes. + const publicKeyLength = 32n return [ // _validatorsByRegistrationId, mapping(uint256 => Validator = (enum, bytes, uint64)) diff --git a/tests/unit/validator-manager-genesis-validation.test.ts b/tests/unit/validator-manager-genesis-validation.test.ts index 4ab6d5fd..8cdfb80b 100644 --- a/tests/unit/validator-manager-genesis-validation.test.ts +++ b/tests/unit/validator-manager-genesis-validation.test.ts @@ -71,3 +71,34 @@ describe('ValidatorManager genesis validator-set validation', () => { expect(result.success).to.be.true }) }) + +describe('ValidatorManager genesis public-key validation', () => { + // ValidatorRegistry.registerValidator requires exactly 32 bytes; the schema + // has to say so too, or a bad key only fails later inside the alloc builder. + const PUBLIC_KEY_31 = `0x${'11'.repeat(31)}` + const PUBLIC_KEY_33 = `0x${'11'.repeat(33)}` + + it('rejects a 31-byte public key at the schema', () => { + const result = schemaValidatorManager.safeParse(configWithValidators([validator(PUBLIC_KEY_31, CONTROLLER_A, 20n)])) + + expect(result.success).to.be.false + if (!result.success) { + expect(result.error.issues.some((issue) => issue.path.join('.') === 'validators.0.publicKey')).to.be.true + } + }) + + it('rejects a 33-byte public key at the schema', () => { + const result = schemaValidatorManager.safeParse(configWithValidators([validator(PUBLIC_KEY_33, CONTROLLER_A, 20n)])) + + expect(result.success).to.be.false + if (!result.success) { + expect(result.error.issues.some((issue) => issue.path.join('.') === 'validators.0.publicKey')).to.be.true + } + }) + + it('accepts a 32-byte public key', () => { + const result = schemaValidatorManager.safeParse(configWithValidators([validator(PUBLIC_KEY_A, CONTROLLER_A, 20n)])) + + expect(result.success).to.be.true + }) +})