-
Notifications
You must be signed in to change notification settings - Fork 983
Description
Description
Hi developers, I am running a multi-client Fulu devnet for testing and noticed that Lighthouse is the only client writing a non-zero nfd ENR field when no next fork is scheduled. All other CL clients (Prysm, Teku, Lodestar) correctly write nfd = 0x00000000 in this scenario.
Differential testing results from a 4-client Kurtosis devnet at Fulu genesis with no BPO fork scheduled:
# lighthouse
nfd=a51ad997 fork_digest=a51ad997 peers=3
# prysm
nfd=00000000 fork_digest=a51ad997 peers=3
# teku
nfd=00000000 fork_digest=a51ad997 peers=3
# lodestar
nfd=00000000 fork_digest=a51ad997 peers=3
Lighthouse sets nfd to the current fork digest (a51ad997) instead of the zero default.
Spec Reference
The Fulu spec specs/fulu/p2p-interface.md defines the nfd ENR field:
If no next fork is scheduled, the
nfdentry contains the default value for the type (i.e., the SSZ representation of a zero-filled array).
When next_fork_epoch == FAR_FUTURE_EPOCH (no fork scheduled), the expected nfd value is 0x00000000.
Root Cause
In beacon_node/lighthouse_network/src/service.rs, the nfd value is computed as:
let next_fork_digest = ctx
.fork_context
.next_fork_digest()
.unwrap_or_else(|| ctx.fork_context.current_fork_digest());When next_fork_digest() returns None (no next fork scheduled), the fallback is current_fork_digest() — a non-zero value. The spec expects the SSZ default (zeros) in this case.
The same value is passed to build_enr in beacon_node/lighthouse_network/src/discovery/enr.rs:
if spec.is_peer_das_scheduled() {
builder.add_value(PEERDAS_CUSTODY_GROUP_COUNT_ENR_KEY, &custody_group_count);
builder.add_value(NEXT_FORK_DIGEST_ENR_KEY, &next_fork_digest);
}Present Behaviour
When no next fork is scheduled, Lighthouse writes nfd = current_fork_digest (e.g., 0xa51ad997) to the ENR.
Expected Behaviour
When no next fork is scheduled, Lighthouse should write nfd = 0x00000000 (the SSZ default for ForkDigest).
Suggested Fix
Change the fallback in service.rs from current_fork_digest() to [0u8; 4]:
let next_fork_digest = ctx
.fork_context
.next_fork_digest()
.unwrap_or_default(); // [0, 0, 0, 0] when no next forkSteps to Reproduce
- Launch a multi-client Kurtosis devnet at Fulu genesis with no BPO fork:
# baseline.yaml
participants:
- el_type: geth
cl_type: prysm
el_image: ethereum/client-go:v1.17.1
cl_image: gcr.io/offchainlabs/prysm/beacon-chain:v7.1.3
supernode: true
validator_count: 32
- el_type: geth
cl_type: lighthouse
el_image: ethereum/client-go:v1.17.1
cl_image: sigp/lighthouse:v8.0.1
validator_count: 32
- el_type: geth
cl_type: teku
el_image: ethereum/client-go:v1.17.1
cl_image: consensys/teku:26.3.0
validator_count: 32
- el_type: geth
cl_type: lodestar
el_image: ethereum/client-go:v1.17.1
cl_image: chainsafe/lodestar:v1.40.0
validator_count: 32
network_params:
electra_fork_epoch: 0
fulu_fork_epoch: 0
additional_services:
- dorakurtosis run github.com/ethpandaops/ethereum-package --args-file baseline.yaml --enclave nfd-test- Query the ENR
nfdfield from each client's Beacon API:
# For each client:
curl -s http://127.0.0.1:<port>/eth/v1/node/identity | jq -r '.data.enr'
# Decode the ENR and inspect the 'nfd' keyLighthouse returns nfd=a51ad997 (the current fork digest), while all other clients return nfd=00000000.
Version
- Lighthouse/v8.0.1 (
sigp/lighthouse:v8.0.1) - Prysm/v7.1.3 (
gcr.io/offchainlabs/prysm/beacon-chain:v7.1.3) - Teku/v26.3.0 (
consensys/teku:26.3.0) - Lodestar/v1.40.0 (
chainsafe/lodestar:v1.40.0)
Thanks for your attention!