Skip to content

bug(genesis): ValidatorManager schema allows zero-address roles and negative voting power bounds #491

Open

Description

@Lesnak1

Summary

The schemaValidatorManager in scripts/genesis/ValidatorManager.ts lacks validation for zero addresses on key governance roles and does not enforce a lower bound on voting power values.

This causes two issues:

  1. Governance Bricking via Zero-Address Initialization: The runtime contract PermissionedValidatorManager.sol explicitly forbids zero addresses in initialize via ZeroOwnerAddress and ZeroPauserAddress. Likewise, Controller.sol and ValidatorRegisterer.sol enforce non-zero addresses via ZeroControllerAddress and ZeroValidatorRegistererAddress. In genesis, writing address(0) into the _owner slot permanently bricks all onlyOwner governance functions (addValidatorRegisterer, configureController, transferOwnership).
  2. Genesis Builder Crash on Negative Voting Power: votingPower and votingPowerLimit use schemaBigInt.max(UINT64_MAX) without .min(0n). Supplying negative values passes schema validation, but crashes the genesis builder during toBytes32() with an unhandled IntegerOutOfRangeError: Number "-1n" is not in safe 256-bit unsigned integer range.
  3. Case-Insensitive Uniqueness: Registerer and controller address sets are checked with case-sensitive equality, failing to catch mixed-casing duplicates that share storage slots.

Proposed Changes

  • Enforce schemaBigInt.min(0n).max(UINT64_MAX) on both votingPower and votingPowerLimit.
  • In schemaValidatorManager.superRefine, validate that PermissionedValidatorManager.owner, pauser, validatorRegisterers, and controllers are not the zero address.
  • Normalize registerer and controller uniqueness checks to be case-insensitive.
  • Add comprehensive regression tests in tests/unit/validator-manager-genesis-validation.test.ts.

Assignment Request

I have the fix and 12 passing unit tests implemented, verified locally, and signed. Could maintainers ( @ZhiyuCircle @romac @sergio-mena ) please assign this issue to me () so I can open the pull request under the contribution policy?

Activity

  1. osr21 commented on Oct 11, 2026

    @osr21

    I checked the current genesis builder and the core validation gaps described here appear real. In particular, the builder writes Ownable._owner and the pauser directly into proxy storage, so the runtime initialize() zero-address checks do not protect this genesis-config path. schemaAddress is format-only, and schemaBigInt.max(UINT64_MAX) still accepts negative bigint values; the later toBytes32 conversion is where those values fail.

    A few points that may help make the fix and regression coverage precise:

    • Test safeParse rejection for zero owner, pauser, validator registerers, and controller addresses, plus negative validators[].votingPower and controllers[].votingPowerLimit. Assert validation fails at the schema boundary rather than later during storage encoding.
    • Use a non-negative lower bound (0n), not a positive one, unless the contract semantics require otherwise: the current schema permits zero-power entries and separately requires at least one validator with positive power.
    • Normalize addresses to the same 20-byte identity before duplicate checks. Two strings with different hex casing represent the same EVM address; duplicate controller entries can collide in the address-keyed registration mapping, so test that mixed-case duplicates are rejected before storage generation.
    • Please describe the threat boundary explicitly: these values come from genesis/operator configuration, so this is an initialization-time misconfiguration/build-validation issue, not a permissionless remote input. The zero-owner case can still brick governance if such a genesis is accepted, but severity should reflect who controls the config and when validation runs.
  2. Lesnak1 commented on Oct 11, 2026

    @Lesnak1
    Author

    Thank you @osr21 for the detailed review and guidance!

    We completely agree with all four points:

    1. Schema Boundary Rejection: All validations are enforced strictly at the Zod schemaValidatorManager schema boundary via safeParse. Rejections for zero-address roles (owner, pauser, validatorRegisterers, and controllers) and negative bounds fail upfront before storage encoding.
    2. Non-Negative Lower Bound (0n): We enforce schemaBigInt.min(0n).max(UINT64_MAX) on both votingPower and votingPowerLimit, maintaining contract semantics where 0-power entries are valid provided the existing non-empty positive total power requirement is met.
    3. Address Identity Normalization: Addresses are normalized to lower-case 20-byte identity before duplicate checks for both validatorRegisterers and controllers, and we have specific regression tests ensuring mixed-case duplicate entries are caught and rejected.
    4. Threat Boundary Context: The PR description will explicitly frame this as an initialization-time configuration validation / defense-in-depth fix preventing governance bricking and build crashes during genesis compilation.

    We have the fix implemented along with 12 regression tests passing locally (12 passing in 7ms) and verified with ESLint.

    cc @ZhiyuCircle @romac : The validation gaps are confirmed and aligned above. Could you please assign this issue so we can submit the PR in accordance with the contributing guidelines?

  3. Lesnak1 commented on Oct 13, 2026

    @Lesnak1
    Author

    Hi @ZhiyuCircle @romac ,following up on the validation confirmation above with @osr21, we have the complete fix and 12 passing regression tests ready locally. Could you please assign this issue so we can submit the PR in accordance with the contributing guidelines?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions