feat(fee): chainspec floor with code-driven activation schedule#337
Merged
nekomoto911 merged 3 commits intoGalxe:mainfrom Apr 26, 2026
Merged
feat(fee): chainspec floor with code-driven activation schedule#337nekomoto911 merged 3 commits intoGalxe:mainfrom
nekomoto911 merged 3 commits intoGalxe:mainfrom
Conversation
Replaces commit 7d0483e's global hardcoded 50 Gwei floor with a schedule-driven approach where: - Genesis JSON `config.gravityMinBaseFee` carries only the **latest** segment's floor value. Its presence marks the chainspec as Gravity; non-Gravity chainspecs (Ethereum mainnet during reth history sync) omit it and keep upstream EIP-1559 semantics. - Activation block(s) and any historical-segment floor values live in branch-specific code constants. main encodes a single segment `[GRAVITY_MIN_BASE_FEE_ACTIVATION_BLOCK = 0, ∞)` returning the genesis value. Released testnet branches override the constant; future step-up upgrades extend the schedule with hardcoded historical segments. This way a node restarted across multiple hardforks always has the full historical schedule baked into its binary, which the earlier genesis-only designs could not guarantee. Reverts the broken parts of commit 7d0483e: - crates/consensus/common/src/validation.rs: London-transition initial base fee back to INITIAL_BASE_FEE. This function runs in EthBeaconConsensus.validate_header_against_parent — the path used by P2P headers downloader during history sync; the hardcoded 50 Gwei broke validation of the actual Ethereum mainnet London transition (block 12,965,000) where base_fee = 1 Gwei. - crates/ethereum/evm/src/lib.rs: same revert at the London-fork- boundary basefee assignment in next_evm_env. - crates/chainspec/src/spec.rs: make_genesis_header and initial_base_fee use INITIAL_BASE_FEE as the genesis fallback again. DEV chain test hashes revert to upstream values. - crates/node/core/src/args/txpool.rs: TxPoolArgs default for --txpool.minimal-protocol-fee back to MIN_PROTOCOL_BASE_FEE (7 wei). - crates/transaction-pool/src/test_utils/tx_gen.rs and config.rs: revert Gravity-specific tweaks that were workarounds for the global default. - crates/e2e-test-utils/src/transaction.rs and crates/ethereum/node/tests/e2e/dev.rs: revert tx fee bumps and the custom_chain genesis tweak; no longer needed once the production pool default is back to 7 wei. Adds the new design: - ChainSpec.gravity_min_base_fee: Option<u64>, parsed from genesis JSON `config.gravityMinBaseFee`. - crates/chainspec/src/constants.rs: GRAVITY_MIN_BASE_FEE_ACTIVATION_BLOCK = 0 (main schedule). - EthChainSpec::gravity_min_base_fee_at_block(block) -> Option<u64> trait method, default returns None (safe for non-Gravity chainspecs). ChainSpec impl encodes main's single-segment schedule. - next_block_base_fee uses the helper: when active for the next block, EIP-1559 result is clamped at the floor and the floor is the parent fallback for pre-London headers. - crates/ethereum/node/src/node.rs EthereumPoolBuilder::build_pool: raises pool admission floor with .max(...) when the schedule is active for `head + 1`. Static decision at startup; missing the tighten across activation only causes sub-floor txs to sit unincluded (pipe-exec rejects them at production), no consensus impact. - crates/optimism/chainspec/src/lib.rs: forward gravity_hardforks (pre-existing missing impl from commit d0666f2, unrelated to fee work but blocks workspace compile) and gravity_min_base_fee_at_block to inner ChainSpec. Behavior matrix: - Ethereum mainnet history sync: gravity_min_base_fee = None -> schedule helper returns None for any block -> upstream EIP-1559 with no clamp; upstream pool admission floor (7 wei). - Gravity main: chainspec genesis sets gravityMinBaseFee = 50e9, top- level baseFeePerGas = 50e9. Schedule activates at block 0, so floor is enforced for every block in pipe-exec block production and pool admission. - Released testnet branches: override GRAVITY_MIN_BASE_FEE_ACTIVATION_BLOCK with the rolling-upgrade activation height; before the activation block, schedule returns None (matches v1.4 behavior). Verified: cargo test on reth-chainspec / reth-transaction-pool / reth-ethereum-consensus / reth-evm-ethereum all pass; cargo check on the relevant downstream crates clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`GRAVITY_MIN_BASE_FEE_ACTIVATION_BLOCK == 0` on main makes `block >= 0` trivially true, which clippy::absurd_extreme_comparisons rejects. Keep the `>=` form for branch parity (released testnet branches override the constant to a non-zero block, future schedule extensions add more segments) and silence the lint with a local allow + comment. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nspec field Replaces the `GRAVITY_MIN_BASE_FEE_ACTIVATION_BLOCK` code constant with a new `ChainSpec.gravity_min_base_fee_activation_block: u64` field, populated by `From<Genesis>` per branch: - main: hardcoded to `0` in `From<Genesis>` (floor active from genesis, not configurable per genesis JSON). - Released testnet branches: read from genesis `config.extra_fields` (e.g. `epsilonBlock`), reusing the same mechanism Alpha/Beta/Gamma/ Delta already use to read their activation blocks. This lets ops set the rolling-upgrade activation height per-network without code changes. Schedule helper `EthChainSpec::gravity_min_base_fee_at_block` now queries the chainspec field instead of the constant, which also removes the `clippy::absurd_extreme_comparisons` issue from Galxe#337 (no more `block >= 0u64` literal comparison). Historical-segment floor values from prior schedule steps still live in branch-specific code, so nodes restarted across multiple hardforks validate older blocks correctly from the binary's full schedule — unchanged from the previous commits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ByteYue
approved these changes
Apr 26, 2026
nekomoto911
approved these changes
Apr 26, 2026
ByteYue
added a commit
that referenced
this pull request
Apr 26, 2026
…torage patches Zeta ships the v1.4 → v1.5 contract changes and the matching storage-side bootstrapping that the new Solidity code requires. Bytecode upgrades (ZETA_SYSTEM_UPGRADES + ZETA_EXTRA_UPGRADES): - Governance (PR #83): initialize(address) + _initialized slot - StakingConfig (PR #85): per-field setMinimumStakeForNextEpoch / setLockupDurationForNextEpoch / setUnbondingDelayForNextEpoch setters - ValidatorManagement (PR #85): per-pool whitelist + permissionless-join flag - Reconfiguration (PR #82): applyPendingConfig before DKG snapshot - JWKManager (PR #79): non-empty JWK field validation on setPatches - StakePool (PR #73): 2-step timelock for staker/operator/voter role changes (Gamma template is re-used; StakePool has no new immutables so the per-pool immutable patch still applies) Storage patches (ZETA_STORAGE_PATCHES): - Governance slot 0 (_owner) ← configured admin address - Governance slot 8 (_initialized) ← 1 (locks out future initialize() calls) - ValidatorManagement._allowedPools[pool] = true for every active pool. _allowedPools lives at slot 7; the real per-pool slot is keccak256(pool_padded || slot7_padded). Those four keccak outputs are precomputed offline (no_std keccak can't run in const context) and committed as B256 literals with a regeneration hint in-source. _permissionlessJoinEnabled (slot 8, one byte) stays at 0 intentionally — the network launches permissioned and governance flips it later. Base fee floor activation (#337's schedule): - gravity_min_base_fee_activation_block is read from the same zetaBlock field used for the bytecode/storage dispatch above. So the consensus- level 50 Gwei floor (gravity_min_base_fee_at_block) and the contract upgrades transition at the exact same height — one ops knob, one rolling-upgrade window, one logical event. - Defaults to u64::MAX when zetaBlock is absent, e.g. a v1.5 binary pointed at a v1.4 chain config that predates Zeta. This disables the floor schedule and falls through to upstream EIP-1559, preserving v1.4 base fee semantics for re-syncing historical blocks. Defaulting to 0 there would silently enforce the 50 Gwei floor on historical v1.4 blocks (whose base_fee may be < 50 Gwei) and break re-sync. Wiring: - crates/chainspec/src/gravity.rs : add Zeta to GravityHardfork enum - crates/chainspec/src/spec.rs : parse zetaBlock from extra_fields, and use the same value as the gravity_min_base_fee schedule activation block - crates/ethereum/evm/src/hardfork/mod.rs : pub mod zeta - crates/ethereum/evm/src/parallel_execute.rs : dispatch at Zeta block Validated with cargo check --release on the full workspace and cargo test on reth-chainspec / reth-pipe-exec-layer-ext-v2 gravity_hardfork_test.
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
Replaces the global hardcoded 50 Gwei floor from #335 with a chainspec-field + code-driven schedule. Non-Gravity chainspecs (Ethereum mainnet history sync) keep upstream EIP-1559 semantics; Gravity chainspecs apply the floor according to a per-branch schedule.
Design
Genesis (
config.gravityMinBaseFee): carries only the floor value applicable to the latest schedule segment. Its presence marks the chainspec as Gravity; absence means no floor.Code (this branch — main):
crates/chainspec/src/constants.rs::GRAVITY_MIN_BASE_FEE_ACTIVATION_BLOCK = 0, with a single-segment schedule[0, ∞)returning the genesis value.Trait helper:
EthChainSpec::gravity_min_base_fee_at_block(block) -> Option<u64>— defaults toNone, ChainSpec impl encodes the schedule. Used by bothnext_block_base_fee(consensus / pipe-exec block production) andEthereumPoolBuilder::build_pool(pool admission floor, queried athead + 1and combined with the operator CLI override viamax()).Compatibility
gravityMinBaseFee→ schedule returnsNonefor any block → upstream EIP-1559, no clamp, pool admission floor =MIN_PROTOCOL_BASE_FEE(7 wei). Fixes #335 breaking the Ethereum mainnet London transition (block 12,965,000) where actual base_fee = 1 Gwei.gravityMinBaseFee = 50_000_000_000, activation block = 0 → floor enforced from genesis. Pipe-exec and pool admission both clamp at 50 Gwei.GRAVITY_MIN_BASE_FEE_ACTIVATION_BLOCKwith the rolling-upgrade activation height N. Before block N the schedule returnsNone(matches pre-floor behavior, rolling-upgrade safe); from block N onward it returns the genesis value.Future upgrade method
When a future release changes the floor from value X to Y at block N:
gravityMinBaseFee = YHistorical floor values are hardcoded in code, so a node restarted across multiple hardforks always validates older blocks correctly from the binary's full schedule. Genesis-only designs (which carry only the current state) cannot guarantee this.
Drive-by
OpChainSpecwas missing thegravity_hardforksimpl introduced in #309 — a pre-existing bug that blocked workspace compile. Added forwarding impl forgravity_hardforksandgravity_min_base_fee_at_block.