Skip to content

fix(chainspec): use MORPH_BASE_FEE as genesis default instead of INITIAL_BASE_FEE#74

Open
panos-xyz wants to merge 2 commits intomainfrom
fix/genesis-base-fee-default
Open

fix(chainspec): use MORPH_BASE_FEE as genesis default instead of INITIAL_BASE_FEE#74
panos-xyz wants to merge 2 commits intomainfrom
fix/genesis-base-fee-default

Conversation

@panos-xyz
Copy link
Copy Markdown
Contributor

@panos-xyz panos-xyz commented Mar 31, 2026

Summary

  • When baseFeePerGas is absent in genesis JSON, upstream reth defaults to Ethereum's INITIAL_BASE_FEE (1,000,000,000 = 1 Gwei)
  • morph-geth's CalcBaseFee() returns 1,000,000 (0.001 Gwei) as the default
  • This mismatch causes morph-reth and morph-geth to compute different genesis block header hashes, breaking batch submission since morphnode's GenesisBatchHeader is derived from the geth-computed hash

Fix

Override the default baseFeePerGas to MORPH_BASE_FEE (1,000,000) in both genesis header construction paths when baseFeePerGas is not explicitly set:

  • make_genesis_header() — used for predefined networks (mainnet/testnet) with custom state root
  • from_genesis_with_config() None branch — used for CLI-provided genesis (devnet)

The fix only applies when baseFeePerGas is absent; explicitly set values are preserved.

Root cause reference

morph/go-ethereum/consensus/misc/eip1559.go:

func CalcBaseFee(...) *big.Int {
    return big.NewInt(1000000) // 0.001 Gwei
}

morph/go-ethereum/core/genesis.go:

if g.BaseFee != nil {
    head.BaseFee = g.BaseFee
} else {
    head.BaseFee = misc.CalcBaseFee(...)  // → 1_000_000
}

Test plan

  • cargo check -p morph-chainspec passes
  • Devnet genesis block hash matches between morph-reth and morph-geth when baseFeePerGas is omitted from genesis JSON

Summary by CodeRabbit

  • Bug Fixes
    • Improved genesis block initialization to ensure the correct base fee per gas value is applied when constructing genesis headers and recomputing the corresponding hash to reflect accurate configuration.

…IAL_BASE_FEE

When `baseFeePerGas` is null/absent in genesis JSON, upstream reth
defaults to Ethereum's INITIAL_BASE_FEE (1_000_000_000 = 1 Gwei).
morph-geth defaults to CalcBaseFee() which returns 1_000_000 (0.001
Gwei). This mismatch causes different genesis block header hashes
between morph-reth and morph-geth.

Override the default in both genesis header construction paths
(make_genesis_header for predefined networks, and from_genesis_with_config
for CLI-provided genesis) to use MORPH_BASE_FEE when baseFeePerGas is
not explicitly set.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

📝 Walkthrough

Walkthrough

Modified genesis header construction in the chainspec to apply MORPH_BASE_FEE when base fee per gas is absent from genesis configuration. Updated two code paths to properly handle base fee assignment and recompute genesis hash accordingly.

Changes

Cohort / File(s) Summary
Genesis base fee handling
crates/chainspec/src/spec.rs
Added Sealable import and updated make_genesis_header and from_genesis_with_config to use MORPH_BASE_FEE as fallback when genesis lacks base fee per gas. Modified hash computation to use hash_slow() instead of cached genesis hash when base fee is corrected.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested reviewers

  • panos-xyz
  • anylots

Poem

🐰 A base fee now stands firm and true,
When genesis lacked what it should do,
MORPH steps in with steadfast grace,
Hashing slowly finds its place! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(chainspec): use MORPH_BASE_FEE as genesis default instead of INITIAL_BASE_FEE' accurately describes the main change in the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/genesis-base-fee-default

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@panos-xyz panos-xyz requested a review from chengwenxi March 31, 2026 15:59
Copy link
Copy Markdown
Contributor

@chengwenxi chengwenxi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/chainspec/src/spec.rs (1)

41-46: Extract the base-fee defaulting rule into one helper.

The same Morph-specific fallback is implemented twice (Lines 41-46 and Lines 269-275). Centralizing this logic would reduce drift risk in consensus-critical paths.

Also applies to: 269-275

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/chainspec/src/spec.rs` around lines 41 - 46, The duplicated
Morph-specific fallback for base fee (checking genesis.base_fee_per_gas and
setting inner.base_fee_per_gas to MORPH_BASE_FEE) should be extracted into a
single helper to avoid drift: add a helper like apply_morph_base_fee(header:
&mut Header) or default_base_fee_if_missing(header: &mut Header) that sets
header.base_fee_per_gas = Some(MORPH_BASE_FEE) when it is None, then replace
both occurrences (the blocks that inspect genesis.base_fee_per_gas /
inner.base_fee_per_gas and the similar block later) with calls to that helper;
keep the MORPH_BASE_FEE symbol and use the existing Header type and field names
(base_fee_per_gas) to maintain consensus semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/chainspec/src/spec.rs`:
- Around line 269-279: Add two unit tests in this module covering genesis
baseFeePerGas: one that builds a spec where genesis.base_fee_per_gas is None and
asserts the resulting header (via base_spec.genesis_header.header().clone(),
then MorphHeader::from(...) and SealedHeader::new(...)) has
header.base_fee_per_gas == Some(MORPH_BASE_FEE) and computes the expected hash,
and another where genesis.base_fee_per_gas is set explicitly and assert that the
explicit value is preserved in the header and that the resulting hash differs
from the omitted case; use the existing helper construction (base_spec, genesis,
MorphHeader::from, header.hash_slow, SealedHeader::new) to create the headers
and compare expected values.

---

Nitpick comments:
In `@crates/chainspec/src/spec.rs`:
- Around line 41-46: The duplicated Morph-specific fallback for base fee
(checking genesis.base_fee_per_gas and setting inner.base_fee_per_gas to
MORPH_BASE_FEE) should be extracted into a single helper to avoid drift: add a
helper like apply_morph_base_fee(header: &mut Header) or
default_base_fee_if_missing(header: &mut Header) that sets
header.base_fee_per_gas = Some(MORPH_BASE_FEE) when it is None, then replace
both occurrences (the blocks that inspect genesis.base_fee_per_gas /
inner.base_fee_per_gas and the similar block later) with calls to that helper;
keep the MORPH_BASE_FEE symbol and use the existing Header type and field names
(base_fee_per_gas) to maintain consensus semantics.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: be01b4d6-5042-4bb8-bd89-395427117618

📥 Commits

Reviewing files that changed from the base of the PR and between ab638b1 and 34e82d4.

📒 Files selected for processing (1)
  • crates/chainspec/src/spec.rs

Comment on lines +269 to 279
let mut header = base_spec.genesis_header.header().clone();
// morph-geth defaults to MORPH_BASE_FEE (1_000_000) when
// baseFeePerGas is absent, not Ethereum's INITIAL_BASE_FEE
// (1_000_000_000). Override to match.
if genesis.base_fee_per_gas.is_none() {
header.base_fee_per_gas = Some(MORPH_BASE_FEE);
}
let header = MorphHeader::from(header);
// Recompute genesis hash with the corrected base fee
let hash = header.hash_slow();
SealedHeader::new(header, hash)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add regression tests for omitted vs explicit baseFeePerGas in genesis.

This branch changes consensus-critical genesis header/hash behavior, but there’s no accompanying test in this module asserting: (1) omitted baseFeePerGas becomes MORPH_BASE_FEE, and (2) explicit baseFeePerGas is preserved. Please add both cases to prevent future hash regressions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/chainspec/src/spec.rs` around lines 269 - 279, Add two unit tests in
this module covering genesis baseFeePerGas: one that builds a spec where
genesis.base_fee_per_gas is None and asserts the resulting header (via
base_spec.genesis_header.header().clone(), then MorphHeader::from(...) and
SealedHeader::new(...)) has header.base_fee_per_gas == Some(MORPH_BASE_FEE) and
computes the expected hash, and another where genesis.base_fee_per_gas is set
explicitly and assert that the explicit value is preserved in the header and
that the resulting hash differs from the omitted case; use the existing helper
construction (base_spec, genesis, MorphHeader::from, header.hash_slow,
SealedHeader::new) to create the headers and compare expected values.

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