Skip to content

forkchoiceUpdated that does not move the head discards safeBlockHash and finalizedBlockHash #24028

Description

@CPerezz

Context

We are consolidating the reorg tests that live in each execution client's repository into one declarative cross-client suite run through hive, so the same payload DAGs and Engine API step lists execute against every client:

Running it against go-ethereum, reth, nethermind, besu and erigon surfaced this.

Case

A forkchoiceUpdated whose headBlockHash is already the current head does not persist the safeBlockHash / finalizedBlockHash it carries. The identical call persists both when it also advances the head.

Canonical a1 ... a6, each delivered with newPayload + forkchoiceUpdated(head=aN, safe=0x0, finalized=0x0) so both markers start unset and the head is a6. Then:

forkchoiceUpdated(head = a6, safe = a4, finalized = a2)   → VALID
eth_getBlockByNumber("safe")       → -39001 block "safe" not available (head block: 6)
eth_getBlockByNumber("finalized")  → -39001

What the suite sees in one of the affected fixtures (test_safe_finalized_labels_follow_fcu), where the previous call had set safe = b2 while advancing the head and the call under test re-sends the same head with safe = b3:

step 11/12 forkchoiceUpdated(head=b3, safe=b3, fin=b3): status=VALID lvh=0x6751a5...fc495f -> 'applied'
  assertHead: latest == b3
  assertHead: 'safe' is b2 (0x2603fd...e5e8b9), expected b3 (0x6751a5...fc495f)     <-- FAIL

and in another fixture, where no marker had ever been set (test_fcu_below_finalized_with_inconsistent_state):

step 21/22 forkchoiceUpdated(head=a10, safe=a10, fin=a5): status=VALID lvh=0x648940...67e454 -> 'applied'
  assertHead: latest == a10
  eth_getBlockByNumber('safe') error -39001: block "safe" not available (head block: 10)
  assertHead: 'safe' is null (None), expected a10 (0x648940...67e454)               <-- FAIL

Boundary

Six cases, fresh client each, canonical chain built with zero markers, one call under test, markers read back afterwards:

case call under test erigon 3.7.0-dev-ab8e9fde geth / reth / nethermind / besu
head advances newPayload(a6), FCU(head=a6, safe=a4, fin=a2) pass pass
same head, both markers head already a6, FCU(head=a6, safe=a4, fin=a2) safe null pass
same head, safe only FCU(head=a6, safe=a4, fin=0x0) safe null pass
same head, finalized only FCU(head=a6, safe=0x0, fin=a2) finalized null pass
rewind markers set while advancing, then FCU(head=a4, safe=a4, fin=a2) pass pass

So the trigger is exactly "this call does not change the head", and both markers are affected. Six fixtures in the suite fail on this.

Reproduce

Requires go + docker only; the fixtures come from a preview release and the simulator is built from the PR branch:

git clone -b reorg-suite-pr https://github.com/CPerezz/hive
cd hive && go build .

./hive --sim ethereum/eels/consume-reorg \
       --sim.buildarg repo=https://github.com/CPerezz/execution-specs \
       --sim.buildarg branch=reorg-suite-pr \
       --sim.buildarg fixtures=https://github.com/CPerezz/execution-specs/releases/download/reorg-preview%40v1/fixtures_reorg.tar.gz \
       --client erigon --sim.limit ".*test_safe_finalized_labels_follow_fcu.*" --sim.loglevel 3

# results: workspace/logs/<ts>-<id>.json ; per-step trace in workspace/logs/details/*.log
# client output: workspace/logs/erigon/client-*.log

To regenerate the fixtures instead of using the preview release (needs uv):

git clone -b reorg-suite-pr https://github.com/CPerezz/execution-specs
simulators/ethereum/eels/consume-reorg/stage.sh ../execution-specs   # fills and stages fixtures.tar.gz
./hive --sim ethereum/eels/consume-reorg \
       --sim.buildarg repo=https://github.com/CPerezz/execution-specs \
       --sim.buildarg branch=reorg-suite-pr \
       --sim.buildarg fixtures=/fixtures \
       --client erigon --sim.limit ".*test_safe_finalized_labels_follow_fcu.*" --docker.nocache consume-reorg

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions