Skip to content

feat(chain): implement BRIP-0008 hysteresis update for Fulu fork#3081

Open
calbera wants to merge 14 commits intomainfrom
claude/implement-brip-0008-pol-vnext-p043C
Open

feat(chain): implement BRIP-0008 hysteresis update for Fulu fork#3081
calbera wants to merge 14 commits intomainfrom
claude/implement-brip-0008-pol-vnext-p043C

Conversation

@calbera
Copy link
Copy Markdown
Contributor

@calbera calbera commented Apr 16, 2026

https://github.com/berachain/BRIPs/blob/main/meta/BRIP-0010.md

Add the Fulu fork (CL component of the Fusaka hardfork) with:

  • BRIP-0008: Fork-gated hysteresis parameter updates that improve validator capital efficiency by reducing the upward buffer from 125% to ~110% of the effective balance increment (HysteresisQuotient: 4->100, UpwardMultiplier: 5->10, DownwardMultiplier: 1->1 unchanged).

  • PoL vNext: New EVMInflationAddressFulu and EVMInflationPerBlockFulu fields following the same pattern as the Genesis-to-Deneb1 inflation migration. Values are placeholders pending PoL vNext design finalization.

  • Fulu fork plumbing: version constant (0x06000000), fork time in chain spec, fork ordering validation, state upgrade handler, Engine API routing, consensus type support, and osakaTime in EL genesis configs.

All devnet/testnet/mainnet chain specs and TOML configs updated with placeholder values (devnet: active from genesis, testnet/mainnet: far future).

claude and others added 3 commits April 16, 2026 10:57
…Fulu fork

Add the Fulu fork (CL component of the Fusaka hardfork) with:

- BRIP-0008: Fork-gated hysteresis parameter updates that improve validator
  capital efficiency by reducing the upward buffer from 125% to ~110% of the
  effective balance increment (HysteresisQuotient: 4->100, UpwardMultiplier:
  5->10, DownwardMultiplier: 1->1 unchanged).

- PoL vNext: New EVMInflationAddressFulu and EVMInflationPerBlockFulu fields
  following the same pattern as the Genesis-to-Deneb1 inflation migration.
  Values are placeholders pending PoL vNext design finalization.

- Fulu fork plumbing: version constant (0x06000000), fork time in chain spec,
  fork ordering validation, state upgrade handler, Engine API routing,
  consensus type support, and osakaTime in EL genesis configs.

All devnet/testnet/mainnet chain specs and TOML configs updated with
placeholder values (devnet: active from genesis, testnet/mainnet: far future).

https://claude.ai/code/session_01WL9ZgSDBYTcr1X91AYuPrU
@calbera calbera requested a review from a team April 16, 2026 11:13
@calbera calbera marked this pull request as ready for review April 16, 2026 11:39
@calbera calbera requested a review from a team as a code owner April 16, 2026 11:39
Copilot AI review requested due to automatic review settings April 16, 2026 11:39
…enesis

Devnet now starts at the Fulu fork (FuluForkTime=0), so e2e tests that
hardcoded Electra1 as the expected genesis fork version need updating.

https://claude.ai/code/session_01WL9ZgSDBYTcr1X91AYuPrU
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements the Fulu fork across the consensus layer, including fork-gated BRIP-0008 hysteresis parameter updates, PoL vNext inflation placeholders, and the required fork plumbing/routing so both CL and EL config paths recognize the new fork.

Changes:

  • Add Fulu (0x06000000) fork version support and fork-time plumbing across chain spec, state upgrade handling, and consensus types.
  • Gate hysteresis parameters by active fork (timestamp-based) per BRIP-0008, and add Fulu-specific spec fields.
  • Extend EL genesis / network config fixtures with osakaTime and add Fulu placeholders to TOML chain specs.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
testing/simulated/el-genesis-files/eth-genesis.json Adds osakaTime to simulated EL genesis config.
testing/networks/80094/spec.toml Adds fulu-fork-time and Fulu hysteresis/PoL placeholder fields for network 80094.
testing/networks/80094/eth-genesis.json Adds osakaTime placeholder for network 80094 EL genesis.
testing/networks/80069/spec.toml Adds fulu-fork-time and Fulu hysteresis/PoL placeholder fields for network 80069.
testing/networks/80069/eth-genesis.json Adds osakaTime placeholder for network 80069 EL genesis.
testing/files/spec.toml Adds Fulu fork time and Fulu hysteresis/PoL fields for test fixtures.
testing/files/eth-genesis.json Adds osakaTime to test fixture EL genesis config.
state-transition/core/state_processor_forks.go Adds Fulu fork processing, state upgrade handler, and fork logging.
state-transition/core/state_processor.go Uses fork-gated hysteresis parameters during effective balance updates.
primitives/version/versions.go Introduces Fulu() version constant and removes unused Electra2.
primitives/version/supported.go Adds Fulu to the supported fork version list.
primitives/version/name.go Adds human-readable name mapping for Fulu.
execution/client/ethclient/engine_test.go Updates invalid-version test to use Capella() instead of removed Electra2().
execution/client/ethclient/engine.go Routes Fulu to the same Engine API variants as Electra1 where applicable.
consensus-types/types/signed_beacon_block.go Allows creating empty signed blocks for Fulu version.
consensus-types/types/payload.go Allows payload header conversion for Fulu version.
consensus-types/types/block.go Allows beacon block creation for Fulu version.
config/spec/testnet.go Adds placeholder FuluForkTime to testnet chain spec data.
config/spec/mainnet.go Adds Fulu fork time + Fulu hysteresis/PoL placeholder constants and wires into mainnet spec data.
config/spec/devnet.go Sets devnet to start with Fulu active and adds Fulu PoL inflation config.
cli/commands/genesis/payload.go Extends supported fork range checks up to Fulu for genesis payload header handling.
chain/spec_test.go Updates fork-order validation tests to include Fulu.
chain/spec.go Adds Fulu fork time, enforces ordering including Fulu, gates hysteresis and PoL fields by active fork.
chain/helpers_test.go Updates helper spec construction to include FuluForkTime.
chain/helpers.go Extends fork-version selection by timestamp to include Fulu.
chain/data.go Adds SpecData fields for Fulu fork time, Fulu hysteresis parameters, and Fulu PoL inflation fields.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread state-transition/core/state_processor_forks.go
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 21e8648026

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread state-transition/core/state_processor.go
Comment thread state-transition/core/state_processor_forks.go
claude added 3 commits April 16, 2026 12:04
The simulated test chain spec providers override Electra1ForkTime to
far-future values but didn't set FuluForkTime, causing fork ordering
violations since testnet's FuluForkTime (9999999999999999) was smaller
than the overridden Electra1ForkTime (math.MaxInt64).

https://claude.ai/code/session_01WL9ZgSDBYTcr1X91AYuPrU
Expand the comment in processEffectiveBalanceUpdates to explain that the
previous block's payload timestamp is used for fork gating because
processEpoch runs inside ProcessSlots (before ProcessFork updates the
state). This means Fulu hysteresis values take effect one epoch after
fork activation at an exact epoch boundary — acceptable since hysteresis
only affects capital efficiency, not consensus safety.

https://claude.ai/code/session_01WL9ZgSDBYTcr1X91AYuPrU
The testing/simulated/el-genesis-files/eth-genesis.json file is paired
with ProvideSimulationChainSpec where Electra, Electra1, and Fulu are
all far-future. The file intentionally omits pragueTime to test pre-
Pectra behavior. Adding osakaTime=0 created an invalid config (Osaka
without Prague), causing Geth containers to fail to start in simulated
tests.

https://claude.ai/code/session_01WL9ZgSDBYTcr1X91AYuPrU
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 17, 2026

Codecov Report

❌ Patch coverage is 83.03571% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.41%. Comparing base (2f41454) to head (872e2dc).

Files with missing lines Patch % Lines
state-transition/core/state_processor_forks.go 82.50% 5 Missing and 2 partials ⚠️
execution/client/ethclient/engine.go 0.00% 5 Missing ⚠️
chain/spec.go 87.09% 4 Missing ⚠️
state-transition/core/state_processor.go 62.50% 2 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #3081      +/-   ##
==========================================
- Coverage   60.44%   60.41%   -0.04%     
==========================================
  Files         367      367              
  Lines       18610    18698      +88     
==========================================
+ Hits        11249    11296      +47     
- Misses       6422     6458      +36     
- Partials      939      944       +5     
Files with missing lines Coverage Δ
chain/helpers.go 85.18% <100.00%> (+1.85%) ⬆️
cli/commands/genesis/payload.go 54.19% <100.00%> (ø)
config/spec/devnet.go 100.00% <100.00%> (ø)
config/spec/mainnet.go 100.00% <100.00%> (ø)
config/spec/testnet.go 100.00% <100.00%> (ø)
consensus-types/types/block.go 97.14% <100.00%> (ø)
consensus-types/types/payload.go 92.54% <100.00%> (ø)
consensus-types/types/signed_beacon_block.go 93.18% <100.00%> (ø)
primitives/version/name.go 45.45% <100.00%> (-4.55%) ⬇️
primitives/version/supported.go 100.00% <ø> (ø)
... and 5 more

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@fridrik01 fridrik01 self-requested a review April 19, 2026 15:52
Copy link
Copy Markdown
Contributor

@fridrik01 fridrik01 left a comment

Choose a reason for hiding this comment

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

mostsly nits

Comment thread chain/spec.go
switch fv {
case version.Deneb1(), version.Electra(), version.Electra1():
switch {
case version.EqualsOrIsAfter(fv, version.Fulu()):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we not check for exact version here? If we add Fulu1 (or future fork) then we would silently return here instead of panicking which is how the switch was written explicitly before.

Suggested change
case version.EqualsOrIsAfter(fv, version.Fulu()):
case version.Equals(fv, version.Fulu()):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Well I'd argue it's more correct now. For example the value we set in Deneb1 EVMInflationAddressDeneb1 is actually valid from fork Deneb1 and onwards (including Electra and Electra1) until its overriden with a new value. Which is now EVMInflationAddressFulu; this value is valid until overriden again. So for Fulu1 it would also be used by default.

Also adjusted other functions to work like this in 4d88d47

Comment thread chain/spec.go

// HysteresisDownwardMultiplier returns the multiplier used when checking
// if the effective balance should be decreased.
HysteresisDownwardMultiplier() math.U64
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looking at the code its unclear why HysteresisUpwardMultiplier takes an argument but HysteresisDownwardMultiplier does not. Since HysteresisDownwardMultiplier does not change in the fork there is no need to, but maybe add an inline comment (or pass a unused timetsamp)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added comment in 3cdc138

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.

4 participants