Skip to content

Fix: Ensure deterministic sub-protocol registration in ConsensusSchedule#9515

Closed
saeeddawod wants to merge 1 commit into
besu-eth:mainfrom
settlemint:fix/consensus-migration-protocol-selection
Closed

Fix: Ensure deterministic sub-protocol registration in ConsensusSchedule#9515
saeeddawod wants to merge 1 commit into
besu-eth:mainfrom
settlemint:fix/consensus-migration-protocol-selection

Conversation

@saeeddawod
Copy link
Copy Markdown
Contributor

This PR fixes a critical bug that prevents successful IBFT2 →
QBFT consensus migration
in Besu. During migration, nodes
would stall and fail to produce blocks because they could only
speak one BFT wire protocol instead of both.

Problem

When running a consensus migration from IBFT2 to QBFT using a
transitions configuration in genesis, the network would stall
before reaching the fork block. Nodes could not exchange IBFT2
messages even though they were still running IBFT2 consensus
pre-fork.

Root Cause

In ConsensusScheduleBesuControllerBuilder.createSubProtocolCon figuration(), the original code was:

return besuControllerBuilderSchedule
    .get(besuControllerBuilderSchedule.keySet().stream().skip(1
).findFirst().orElseThrow())
    .createSubProtocolConfiguration(ethProtocolManager,
maybeSnapProtocolManager);

Issues with this approach:

  1. Non-deterministic ordering:
    besuControllerBuilderSchedule is a Map (HashMap), so
    keySet().stream().skip(1) produces unpredictable results
    depending on hash ordering
  2. Only one protocol registered: Only the sub-protocol
    configuration from ONE consensus mechanism was registered,
    meaning nodes could only speak QBFT on the wire while still
    needing IBFT2 consensus pre-fork
  3. Peer communication failure: Nodes couldn't exchange
    IBFT2 messages → block production stalls before fork height

Solution

The fix registers all BFT sub-protocols from all scheduled
consensus mechanisms:

  • IBF/1 (IBFT2) - for pre-migration communication
  • istanbul/100 (QBFT) - for post-migration communication

This allows nodes to communicate using both protocols during
the transition period. The fix:

  1. Iterates through all consensus builders (sorted by block
    number for determinism)
  2. Merges their sub-protocol configurations
  3. Avoids duplicates (e.g., EthProtocol which is common to
    both)

Testing

Migration Test Performed

  1. Started IBFT2 network - 4 validator nodes producing
    blocks under IBFT2 consensus
  2. Verified IBFT2 active - Logs showed
    IbftBesuControllerBuilder for blocks #0-49
  3. Stopped network at block ~46 (before fork block 50)
  4. Restarted with migration genesis containing both IBFT2
    and QBFT config with startBlock: 50
  5. Verified seamless transition:
  6. Confirmed continuous operation - Network continued
    producing blocks past [PIE-2026] Separate in-sync from sync-status listeners #100 under QBFT

Log Evidence

# Pre-fork (IBFT2)
IbftBesuControllerBuilder | Produced #49 / 0 tx / ...

# Fork block (QBFT takes over)
QbftBesuControllerBuilder | Imported empty block #50 / 0 tx /
...

# Post-fork (QBFT continues)
QbftBesuControllerBuilder | Produced empty block #73 / 0 tx /
...

Genesis Configuration Used

{
  "config": {
    "ibft2": {
      "blockperiodseconds": 1,
      "epochlength": 30000,
      "requesttimeoutseconds": 2
    },
    "qbft": {
      "blockperiodseconds": 1,
      "epochlength": 30000,
      "requesttimeoutseconds": 2,
      "startBlock": 50
    }
  }
}

Fixed Issue(s)

Fixes consensus migration failures when transitioning from
IBFT2 to QBFT using scheduled transitions.

Checklist

  • Checked out [contribution guidelines](https://github.com/
    hyperledger/besu/blob/main/CONTRIBUTING.md)
  • Considered documentation - doc-change-required may be
    needed to clarify migration requirements
  • Considered changelog - This is a bug fix for consensus
    migration
  • No database changes

Local Tests

  • Manual migration test (IBFT2 → QBFT) - PASSED
  • spotless: ./gradlew spotlessApply
  • unit tests: ./gradlew build
  • acceptance tests: ./gradlew acceptanceTest
  • integration tests: ./gradlew integrationTest

@saeeddawod saeeddawod closed this Dec 1, 2025
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.

1 participant