Skip to content

feat(hardfork): implement Epsilon hardfork with 4 contract bytecodes#327

Merged
nekomoto911 merged 2 commits intogravity-testnet-v1.4from
feat/epsilon-hardfork
Apr 13, 2026
Merged

feat(hardfork): implement Epsilon hardfork with 4 contract bytecodes#327
nekomoto911 merged 2 commits intogravity-testnet-v1.4from
feat/epsilon-hardfork

Conversation

@ByteYue
Copy link
Copy Markdown
Collaborator

@ByteYue ByteYue commented Apr 13, 2026

Summary

Mirrors the Delta pattern (commit 35f4e02 / 6ad34e3f8) for the v1.3 → v1.4 contract upgrade landed in gravity_chain_core_contracts PR #71. Replaces 4 contract bytecodes; no storage patches are needed — the contract-side fix in PR #71 keeps every storage byte at the same slot across the upgrade, so Epsilon is a pure bytecode-replacement hardfork.

Contracts replaced

Contract Address Change
ValidatorManagement 0x...2001 PR #56: D3-2 underbonded eviction (Phase 1) + percentage-based Phase 2 perf threshold + skip-epoch-1 rule
Reconfiguration 0x...2003 PR #63: evictUnderperformingValidators() call site moved out of _applyReconfiguration and into checkAndStartTransition / governanceReconfigure (fixes DKG sync halt)
ValidatorConfig 0x...1002 PR #63: new autoEvictThresholdPct uint64, declared between autoEvictEnabled and __deprecated_autoEvictThreshold so it packs with autoEvictEnabled in slot 4 — keeps storage layout byte-identical to v1.3
GBridgeReceiver 0x595475934ed7d9faa7fca28341c2ce583904a44e (testnet) PR #66: _processedNonces mapping → __deprecated_processedNonces gap, isProcessed() removed. Also adds the trustedSourceId immutable that did not exist on the originally-deployed v1.0 receiver

GBridgeReceiver — first upgrade since v1.0

The on-chain GBridgeReceiver at 0x595475934ed7d9faa7fca28341c2ce583904a44e was deployed by Genesis.sol at testnet bring-up using the v1.0 source — neither Gamma nor Delta touched it. v1.0 only had one immutable (trustedBridge) and did not validate sourceId. v1.4 adds a second immutable (trustedSourceId) and enforces it in _handlePortalMessage.

The on-chain values were obtained by reading the live testnet at 34.186.189.129:8545:

$ cast call 0x595475934ed7d9faa7fca28341c2ce583904a44e 'trustedBridge()(address)'
0x79226649b3A20231e6b468a9E1AbBD23d3DFbbC6

$ cast call 0x...4000 'getLatestNonce(uint32,uint256)(uint128)' 0 11155111
1
$ cast call 0x...4000 'getLatestNonce(uint32,uint256)(uint128)' 0 1
0

So the relayer source is Sepolia (11155111). The bytecodes/epsilon/GBridgeReceiver.bin shipped here has both immutables pre-patched with these testnet values via scripts/build_epsilon_bytecodes.sh in the contracts repo. For non-testnet environments, regenerate the .bin with the matching values.

The AST id ordering used by the patch script (lower id = trustedBridge, higher id = trustedSourceId) is verified by EpsilonGBridgeReceiverPatchSanity.t.sol in the contracts repo, which deploys a fresh receiver and asserts the offsets match.

Wiring

Mirrors Delta exactly:

  • crates/chainspec/src/gravity.rs — add Epsilon variant to GravityHardfork
  • crates/chainspec/src/spec.rs — parse epsilonBlock from genesis.config.extra_fields
  • crates/ethereum/evm/src/hardfork/mod.rspub mod epsilon
  • crates/ethereum/evm/src/hardfork/epsilon.rsHardforkUpgrades impl with 3 system_upgrades and 1 extra_upgrades for GBridgeReceiver. No storage_patches (uses trait default).
  • crates/ethereum/evm/src/parallel_execute.rs — dispatch alongside Gamma/Delta in the post-balance-increment block
  • crates/ethereum/evm/src/hardfork/bytecodes/epsilon/{ValidatorManagement,Reconfiguration,ValidatorConfig,GBridgeReceiver}.bin — embedded via include_bytes!

Test wiring

  • crates/pipe-exec-layer-ext-v2/execute/gravity_hardfork.json"epsilonBlock": 30
  • crates/pipe-exec-layer-ext-v2/execute/tests/gravity_hardfork_test.rsEPSILON_BLOCK = 30 constant + the same 2-assertion transitions_at_block pattern Delta uses.

Also drops a stale use reth_ethereum_forks::Hardforks; import that was preventing the test from even compiling on the v1.4 branch (reth-ethereum-forks is not a direct dependency of the test crate). The trait method is reached via the inherent ChainHardforks impl, so the use line is unnecessary.

Test plan

  • cargo build -p reth-chainspec -p reth-evm-ethereum — clean
  • cargo test -p reth-pipe-exec-layer-ext-v2 --test gravity_hardfork_testtest_gamma_hardfork passes. The test pushes blocks past EPSILON_BLOCK=30 (target = GAMMA_BLOCK + 30 = 50), so apply_hardfork_upgrades(&EpsilonHardfork, state) runs end-to-end without panicking.
  • After deploying to testnet: bump epsilonBlock in the testnet genesis to a target activation block, restart nodes, then run bash scripts/verify_hardfork/verify.sh epsilon http://34.186.189.129:8545 from the contracts repo. Phase 1 (codehash check on the 3 system contracts) and Phase 2 (smoke tests for autoEvictThresholdPct exists, autoEvictThreshold reverts, isProcessed reverts) should all pass.
  • Verify post-Epsilon bridge transfers still work end-to-end (relayer continues to send sourceId=11155111, which now must match the new trustedSourceId immutable baked into the receiver bytecode).

Risks

  • GBridgeReceiver immutables are environment-specific. Any non-testnet deployment must regenerate bytecodes/epsilon/GBridgeReceiver.bin with its own TRUSTED_BRIDGE / TRUSTED_SOURCE_ID values via scripts/build_epsilon_bytecodes.sh in the contracts repo. The hardcoded testnet address constant in epsilon.rs would also need updating.
  • Solc version drift. The .bin files were built with solc 0.8.30 (pinned in the contracts repo's foundry.toml). Any node running a different Solidity version locally will produce a different on-chain codehash post-hardfork than what cargo-built nodes apply — cargo-built node bytecode is the source of truth.
  • Relayer sourceId mismatch would brick the bridge. Post-Epsilon the receiver enforces sourceId == trustedSourceId. If the relayer is reconfigured to a different chain id (e.g., mainnet 1) without rebuilding the receiver .bin first, every bridge transfer will revert with InvalidSourceChain.

🤖 Generated with Claude Code

ByteYue and others added 2 commits April 13, 2026 17:33
Mirrors the Delta pattern (commit 35f4e02 / 6ad34e3) for the v1.3 → v1.4
contract upgrade landed in gravity_chain_core_contracts PR #71. Replaces
4 contract bytecodes; no storage patches needed (the contract-side fix in
PR #71 keeps every storage byte at the same slot across the upgrade).

Contracts replaced:
- ValidatorManagement (PR #56): D3-2 underbonded eviction (Phase 1) and
  percentage-based Phase 2 performance threshold, skip-epoch-1 rule
- Reconfiguration (PR #63): evictUnderperformingValidators() call site moved
  out of _applyReconfiguration and into checkAndStartTransition /
  governanceReconfigure, fixing the DKG synchronization halt
- ValidatorConfig (PR #63): new autoEvictThresholdPct uint64 field, declared
  between autoEvictEnabled and __deprecated_autoEvictThreshold so it packs
  with autoEvictEnabled in slot 4 — keeps storage layout byte-identical to
  v1.3 (PendingConfig stays at 6 slots; _initialized stays at slot 12)
- GBridgeReceiver (PR #66): _processedNonces mapping replaced with a
  __deprecated_processedNonces gap; isProcessed() removed. This hardfork is
  also the first time GBridgeReceiver is upgraded since v1.0, so it adds the
  trustedSourceId immutable that didn't exist on the originally-deployed
  receiver.

Wiring touchpoints (mirror Delta exactly):
- crates/chainspec/src/gravity.rs: add Epsilon variant to GravityHardfork
- crates/chainspec/src/spec.rs: parse epsilonBlock from genesis extra_fields
- crates/ethereum/evm/src/hardfork/mod.rs: pub mod epsilon
- crates/ethereum/evm/src/hardfork/epsilon.rs: HardforkUpgrades impl with
  3 system_upgrades and 1 extra_upgrade for GBridgeReceiver. Modeled on
  delta.rs but with no storage_patches.
- crates/ethereum/evm/src/parallel_execute.rs: dispatch alongside Gamma/Delta
  in the post-balance-increment block

Bytecodes embedded via include_bytes! from
bytecodes/epsilon/{ValidatorManagement,Reconfiguration,ValidatorConfig,
GBridgeReceiver}.bin. Generated by the new
scripts/build_epsilon_bytecodes.sh in the contracts repo, which extracts
deployedBytecode from forge artifacts and patches the GBridgeReceiver
immutables with the testnet values:
  trustedBridge    = 0x79226649b3A20231e6b468a9E1AbBD23d3DFbbC6
  trustedSourceId  = 11155111  (Sepolia)
Both values were obtained by reading the live testnet at 34.186.189.129:8545
(trustedBridge() getter on the existing receiver; getLatestNonce(0,11155111)
on NativeOracle to confirm the relayer source). The AST id ordering of the
patch script is verified by EpsilonGBridgeReceiverPatchSanity in the
contracts repo.

Test wiring:
- crates/pipe-exec-layer-ext-v2/execute/gravity_hardfork.json: add
  epsilonBlock=30
- crates/pipe-exec-layer-ext-v2/execute/tests/gravity_hardfork_test.rs: add
  EPSILON_BLOCK constant and the same 2-assertion transitions_at_block
  pattern Delta uses. Also drops a stale unused
  `use reth_ethereum_forks::Hardforks;` import that was preventing the test
  from even compiling on the v1.4 branch (the trait method is reached via
  the inherent ChainHardforks impl).

Verification:
- cargo build -p reth-chainspec -p reth-evm-ethereum: clean
- cargo test -p reth-pipe-exec-layer-ext-v2 --test gravity_hardfork_test:
  test_gamma_hardfork passes; the test pushes blocks past EPSILON_BLOCK so
  apply_hardfork_upgrades(&EpsilonHardfork, state) runs end-to-end without
  panicking.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- epsilon.rs / parallel_execute.rs: apply fmt diffs from CI (line wrapping)
- epsilon.rs / alpha.rs / beta.rs / gamma.rs: add backticks to bare type
  names in doc comments (clippy::doc-markdown). Pre-existing on alpha/beta/
  gamma but only surfaced now because v1.4 is the first branch to push a PR
  that triggers full clippy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@nekomoto911 nekomoto911 merged commit 15e07d9 into gravity-testnet-v1.4 Apr 13, 2026
29 checks passed
ByteYue added a commit that referenced this pull request Apr 23, 2026
…327)

* feat(hardfork): implement Epsilon hardfork with 4 contract bytecodes

Mirrors the Delta pattern (commit 35f4e02 / 6ad34e3) for the v1.3 → v1.4
contract upgrade landed in gravity_chain_core_contracts PR #71. Replaces
4 contract bytecodes; no storage patches needed (the contract-side fix in
PR #71 keeps every storage byte at the same slot across the upgrade).

Contracts replaced:
- ValidatorManagement (PR #56): D3-2 underbonded eviction (Phase 1) and
  percentage-based Phase 2 performance threshold, skip-epoch-1 rule
- Reconfiguration (PR #63): evictUnderperformingValidators() call site moved
  out of _applyReconfiguration and into checkAndStartTransition /
  governanceReconfigure, fixing the DKG synchronization halt
- ValidatorConfig (PR #63): new autoEvictThresholdPct uint64 field, declared
  between autoEvictEnabled and __deprecated_autoEvictThreshold so it packs
  with autoEvictEnabled in slot 4 — keeps storage layout byte-identical to
  v1.3 (PendingConfig stays at 6 slots; _initialized stays at slot 12)
- GBridgeReceiver (PR #66): _processedNonces mapping replaced with a
  __deprecated_processedNonces gap; isProcessed() removed. This hardfork is
  also the first time GBridgeReceiver is upgraded since v1.0, so it adds the
  trustedSourceId immutable that didn't exist on the originally-deployed
  receiver.

Wiring touchpoints (mirror Delta exactly):
- crates/chainspec/src/gravity.rs: add Epsilon variant to GravityHardfork
- crates/chainspec/src/spec.rs: parse epsilonBlock from genesis extra_fields
- crates/ethereum/evm/src/hardfork/mod.rs: pub mod epsilon
- crates/ethereum/evm/src/hardfork/epsilon.rs: HardforkUpgrades impl with
  3 system_upgrades and 1 extra_upgrade for GBridgeReceiver. Modeled on
  delta.rs but with no storage_patches.
- crates/ethereum/evm/src/parallel_execute.rs: dispatch alongside Gamma/Delta
  in the post-balance-increment block

Bytecodes embedded via include_bytes! from
bytecodes/epsilon/{ValidatorManagement,Reconfiguration,ValidatorConfig,
GBridgeReceiver}.bin. Generated by the new
scripts/build_epsilon_bytecodes.sh in the contracts repo, which extracts
deployedBytecode from forge artifacts and patches the GBridgeReceiver
immutables with the testnet values:
  trustedBridge    = 0x79226649b3A20231e6b468a9E1AbBD23d3DFbbC6
  trustedSourceId  = 11155111  (Sepolia)
Both values were obtained by reading the live testnet at 34.186.189.129:8545
(trustedBridge() getter on the existing receiver; getLatestNonce(0,11155111)
on NativeOracle to confirm the relayer source). The AST id ordering of the
patch script is verified by EpsilonGBridgeReceiverPatchSanity in the
contracts repo.

Test wiring:
- crates/pipe-exec-layer-ext-v2/execute/gravity_hardfork.json: add
  epsilonBlock=30
- crates/pipe-exec-layer-ext-v2/execute/tests/gravity_hardfork_test.rs: add
  EPSILON_BLOCK constant and the same 2-assertion transitions_at_block
  pattern Delta uses. Also drops a stale unused
  `use reth_ethereum_forks::Hardforks;` import that was preventing the test
  from even compiling on the v1.4 branch (the trait method is reached via
  the inherent ChainHardforks impl).

Verification:
- cargo build -p reth-chainspec -p reth-evm-ethereum: clean
- cargo test -p reth-pipe-exec-layer-ext-v2 --test gravity_hardfork_test:
  test_gamma_hardfork passes; the test pushes blocks past EPSILON_BLOCK so
  apply_hardfork_upgrades(&EpsilonHardfork, state) runs end-to-end without
  panicking.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style(hardfork): apply cargo fmt + clippy doc-markdown fixes from CI

- epsilon.rs / parallel_execute.rs: apply fmt diffs from CI (line wrapping)
- epsilon.rs / alpha.rs / beta.rs / gamma.rs: add backticks to bare type
  names in doc comments (clippy::doc-markdown). Pre-existing on alpha/beta/
  gamma but only surfaced now because v1.4 is the first branch to push a PR
  that triggers full clippy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
(cherry picked from commit 15e07d9)
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.

2 participants