Skip to content

on-chain node version reporting with vote-scheduled upgrade enforcement#190

Open
Andrew-Pohl wants to merge 1 commit into
upgrade-solidityfrom
add-version-reporting
Open

on-chain node version reporting with vote-scheduled upgrade enforcement#190
Andrew-Pohl wants to merge 1 commit into
upgrade-solidityfrom
add-version-reporting

Conversation

@Andrew-Pohl

Copy link
Copy Markdown
Member

Summary

Adds an on-chain mechanism to coordinate network (spec) upgrades. Validators self-report the
node/spec version they are running; a network upgrade (required version + activation height) is
scheduled through a validator ballot in the Voting contract; and on the last cycle boundary before
the new spec activates, the Consensus contract jails any validator still running an old version
provided more than 66% of the current validator set has upgraded. This guarantees the validator
set that is active when the fork block arrives consists only of upgraded nodes.

How it works end-to-end

  1. A new Nethermind client image (with the new spec baked in) is published; validators pull it.
  2. The validator app reports the spec version on chain automatically on restart.
  3. A validator opens a ballot: Voting.newNodeVersionBallot(startAfterCycles, cyclesDuration, version, activationBlock, description).
  4. Validators vote. Finalization uses the existing ballot machinery: stake-weighted accepts must
    exceed 20% of total stake (turnout) and accepts > rejects. On acceptance, Voting calls
    Consensus.setRequiredNodeVersion(version, activationBlock).
  5. On the last cycle boundary before activationBlock, Consensus counts upgraded validators.
    If more than 66% (UPGRADE_QUORUM_BP = 6600, headcount-based) have reported at least the
    required version, every outdated validator is jailed and excluded from the validator set that
    crosses the fork. The check runs exactly once per scheduled upgrade and emits
    NodeVersionCheckExecuted(required, upgradedCount, totalCount, quorumReached) either way.
  6. A jailed validator can only unJail() after reporting at least the required version — upgrading
    is the only way back into the set.

Contract changes

Consensus / ConsensusUtils

  • reportNodeVersion(uint256) — self-report, encoded major * 1e6 + minor * 1e3 + patch
    (spec 6.0.36000003). Emits NodeVersionReported.
  • setRequiredNodeVersion(version, activationBlock)onlyVoting (not owner-callable);
    schedules the upgrade, version 0 cancels a scheduled upgrade. Emits RequiredNodeVersionSet.
  • _checkNodeVersions(...) — runs in cycle() after the productivity jail check, before the new
    pending set is read, so jailed laggards are excluded from the fork cycle's set.
  • unJail() gate: while a required version is set, release requires having reported it.
  • Getters: getNodeVersion(validator), getRequiredNodeVersion(), getNodeVersionActivationBlock(),
    isNodeVersionCheckExecuted().
  • All new state lives in new keccak-keyed eternal-storage slots — storage layout unchanged, safe
    to roll out via the existing proxy upgrade flow.

Voting / VotingUtils / VotingBase

  • New BallotTypes enum (ContractAddress, NodeVersion). Legacy ballots (type 0) finalize through
    the contract-address path, so in-flight ballots survive the implementation swap.
  • newNodeVersionBallot(...) — validator-only creation, same duration rules and per-validator ballot
    limit as existing ballots. Stores proposed version + activation block.
  • Finalization calls into Consensus wrapped in try/catch: onCycleEnd runs inside the block-reward
    flow, so an accepted ballot whose activation block has gone stale fails gracefully instead of
    reverting cycle()reward() and stalling block sealing.

Validator app / ops

  • app/index.js: reports the version from the NODE_VERSION env var once per process lifetime
    (a container restart — i.e. an upgrade — triggers a fresh report). Parses the last x.y.z
    in the string, so an image tag like 1.29.0-v6.0.3 reports the spec version 6.0.3. Runs before
    the isValidator early-return (a version-jailed validator is out of the set but must still report
    to unjail), and failures are logged without crashing the cycle loop, so an updated app keeps
    working against a not-yet-upgraded contract.
  • nethermind/quickstart.sh: validator container now gets
    --env NODE_VERSION=$FUSE_CLIENT_DOCKER_IMAGE_VERSION.
  • ABIs regenerated (abis/*.json, app/abi/*.json).

Design decisions

  • Upgrade quorum is headcount-based (not stake-weighted) and strictly greater than 66% — matches
    AuRa's per-validator block production model.
  • Ballot quorum uses the standard voting rules (20% stake turnout + majority), the same bar as
    ballots that replace entire contract implementations. The >66%-actually-upgraded check at the
    cycle boundary is an independent, harder gate.
  • If the 66% upgrade quorum is not met at the boundary, nobody is jailed (the fork is at risk
    regardless at that point); old nodes then fall to the existing productivity jailing post-fork.
  • Scheduling should land at least 2 cycles before the activation block.

Testing

  • yarn test186 passing, 0 failing
  • New consensus tests: version reporting, voting-only authorization (owner rejected), jailing at the
    correct cycle boundary with quorum, no jailing below quorum, no early firing, unjail blocked until
    the required version is reported.
  • New voting tests: ballot creation/validation, accepted ballot sets the version on Consensus,
    rejected and below-turnout ballots don't, stale-activation ballot does not revert onCycleEnd.

Deployment notes

  • Ships as new Consensus and Voting implementations via the standard governance path (two ballots,
    contractType 1 and 4). No re-initialization; storage layout is untouched.
  • The validator-app docker image needs a rebuild/version bump to ship the app change, and
    validators pick up the quickstart change on their next run.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant