feat: unified hardfork upgrade system with CollectUpgrades registry#68
Merged
feat: unified hardfork upgrade system with CollectUpgrades registry#68
Conversation
Introduce a centralized hardfork registration mechanism via ChainConfig.CollectUpgrades(), replacing scattered manual append patterns. Block-0 hardforks are now applied during genesis through InjectContracts overlay, and runtime hardforks via WBFT block finalization with merged StateTransitions. Key changes: - Add CollectUpgrades() as single source of truth for hardfork registry - Add InjectContracts with block-0 hardfork overlay support - Refactor SetConfigFromChainConfig to use CollectUpgrades() - Add upgrade*() functions for partial-param state migrations - Fix genesis-updater to preserve block-0 hardforks in stripHardforkConfig - Update mainnet genesis hash to reflect Boho overlay at genesis - Add comprehensive tests for all upgrade paths
Update mainnet genesis gasLimit and difficulty to non-zero values. Regenerate devp2p ethtest testdata with new validator keys and fix snap test expected values to match updated state trie.
Replace manual Boho hardfork registration with CollectUpgrades() to keep testutil.go in sync with ethconfig.SetConfigFromChainConfig.
Warn that contract params may have been modified by governance proposals before a hardfork upgrade, and that upgrade authors must query on-chain state to avoid silently overwriting governance-approved values.
12cfee9 to
3a63463
Compare
Enforce code-only upgrade principle: hardfork upgrades now only deploy new contract bytecode without modifying on-chain state. State initialization is restricted to v1 (genesis) only, preserving governance-modified values.
eomti-wm
reviewed
Apr 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ChainConfig.CollectUpgrades()as the single source of truth for hardfork registration, replacing scattered manual append patternsInjectContractsso mainnet genesis correctly includes all hardforks activated at genesis (e.g., Boho GovMinter v2)GetSystemContractsStateTransitionto merge multiple upgrades at the same block heightinitialize*) and upgrade (upgrade*) pathsgenesis-updatertool to preserve block-0 hardforks instripHardforkConfigSystem Contract Versioning
System contracts follow a strict versioning protocol:
v1is mandatory as the initial version. Theinitialize*()functions (e.g.,initializeValidator,initializeMinter) are only invoked whenVersion == "v1". These functions set up the required storage layout (owner, quorum, members, etc.). Starting with any other version will skip initialization, leaving the contract in an uninitialized state.Initialize vs Upgrade
initialize*()Version == "v1"upgrade*()Version != "v1"(v2, v3, ...)Paramsare written; missing keys preserve on-chain stateWhen
Paramsisnil, only the contract bytecode is replaced and no state changes are made. This is the standard hardfork upgrade scenario where code is swapped but existing governance state is preserved.Genesis Hardfork Overlay
When a hardfork activates at block 0 (e.g., mainnet
BohoBlock=0),InjectContractsapplies it as an overlay on top of the baseline v1 contracts:Hardforks with
Block > 0are skipped during genesis — they are applied at runtime viaprocessFinalize→GetSystemContractsStateTransition.Adding a New Hardfork
Adding a new hardfork requires only:
ChainConfig(CForkBlock,CFork,IsCFork(), etc.)CollectUpgrades()— genesis and runtime paths are handled automaticallyNo changes needed in
InjectContracts,SetConfigFromChainConfig, orprocessFinalize.Changed files
params/config.goCollectUpgrades()method, updated genesis hashcore/genesis.goInjectContractsblock-0 overlay viaapplyUpgradeOverlayconsensus/wbft/config.goGetSystemContractsStateTransitioneth/ethconfig/config.goSetConfigFromChainConfigusesCollectUpgrades()systemcontracts/*.goinitialize*/upgrade*split, Params nil guardscripts/cmd/genesis-updater/main.gostripHardforkConfig*_test.go(10 files)Test plan
go test ./params/— CollectUpgrades ordering, Boho at block 0 and block Ngo test ./core/— Genesis hash consistency, SetupGenesis, Anzeon init, Boho overlaygo test ./consensus/wbft/— Merge-based state transition, multi-upgrade at same blockgo test ./systemcontracts/— initialize/upgrade routing, Params nil guard, partial paramsgo test ./eth/ethconfig/— Engine config setupgo vetandgofmtpass on all modified packages