Skip to content

fix: guard CalcBaseFee against divide-by-zero from malformed extraData#781

Open
latifkasuli wants to merge 1 commit intoethereum-optimism:optimismfrom
latifkasuli:fix/calcbasefee-zero-elasticity-panic
Open

fix: guard CalcBaseFee against divide-by-zero from malformed extraData#781
latifkasuli wants to merge 1 commit intoethereum-optimism:optimismfrom
latifkasuli:fix/calcbasefee-zero-elasticity-panic

Conversation

@latifkasuli
Copy link
Copy Markdown

Summary

Fixes #757

CalcBaseFee unconditionally decoded Holocene/Jovian extraData from the parent header and passed the result to calcBaseFeeInner. The decoders use best-effort logic and return 0, 0 on malformed input, which causes a divide-by-zero panic:

  • elasticity == 0 panics on integer division (parent.GasLimit / elasticity)
  • denominator == 0 panics on big.Int.Div

Root cause

The precondition documented in CalcBaseFee ("It is assumed the parent Header has valid extraData") is not upheld by all callers. Many non-consensus callers (RPC pending tx, fee history, GraphQL, txpool repricing, blobpool, miner, simulate, t8ntool) use CalcBaseFee on headers they assume are valid. A malformed parent header already present locally will trigger the panic.

Additionally, chains activating Holocene or Jovian at genesis can hit this because:

  • Genesis ExtraData is written without Optimism-specific validation (core/genesis.go)
  • Consensus header validation explicitly skips the genesis block (consensus/beacon/consensus.go)
  • Block 1 calls CalcBaseFee on the genesis parent, triggering the panic

Changes

  1. consensus/misc/eip1559/eip1559.go: Gate the DecodeOptimismExtraData call behind ValidateOptimismExtraData. If the parent extraData is invalid, chain-config defaults are preserved instead of zero values. No API signature changes.

  2. core/genesis.go: Reject invalid Optimism extraData at genesis commit time for chains that activate Holocene or later at genesis.

  3. consensus/misc/eip1559/eip1559_test.go: Add regression tests:

    • TestCalcBaseFeeOptimismMalformedExtraData: 10 sub-tests covering empty, short, wrong-version, wrong-length, zero-denominator, zero-elasticity, and both-zero extraData for both Holocene and Jovian parent times. Asserts no panic.
    • TestCalcBaseFeeOptimismMalformedFallsBackToConfig: Verifies that malformed extraData produces the same base fee as chain-config defaults (Canyon denominator/elasticity), not zeros.

What this does NOT change

  • ValidateHolocene1559Params is not tightened. (0, 0) in payload attributes is an intentional sentinel per the Holocene spec.
  • Consensus header validation already rejects zero denominator/elasticity in extraData via ValidateOptimismExtraData in beacon.verifyHeader.

Test plan

  • All 30 existing + new tests pass (go test ./consensus/misc/eip1559/ -v)
  • go build ./core/ compiles cleanly
  • CI passes

CalcBaseFee unconditionally decoded Holocene/Jovian extraData from the
parent header and passed the result to calcBaseFeeInner. The decoders
use best-effort logic and return zeros on malformed input, which causes
a divide-by-zero panic (elasticity==0 on integer division, denominator==0
on big.Int division).

Gate the decode behind ValidateOptimismExtraData so that chain-config
defaults are preserved when the parent extraData is invalid. This
protects all eight callers (header verification, miner, txpool, blobpool,
fee history, RPC API, GraphQL, simulate) without signature changes.

Also reject invalid extraData at genesis commit time for OP chains that
activate Holocene or later at genesis, closing the strongest
consensus-adjacent reachability path.

Fixes ethereum-optimism#757
@latifkasuli latifkasuli requested a review from a team as a code owner March 29, 2026 10:29
@latifkasuli latifkasuli requested a review from teddyknox March 29, 2026 10:29
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.

CalcBaseFee can divide by zero when elasticity == 0 (invalid extraData)

1 participant