mirror of
https://github.com/sigp/lighthouse.git
synced 2026-06-10 01:26:44 +00:00
fix(network): clear ENR nfd field when no next fork is scheduled during runtime transitions (#9131)
No. But related to #9009 and #8996 - Change the `ForkContext::next_fork_digest()` to return `[u8; 4]` (returning `[0u8; 4]` for "no next fork"). - Update the initialization path and runtime fork transition path accordingly. Added tests: - [x] `test_next_fork_digest` — existing test passes with non-Option return type - [x] `test_next_fork_digest_returns_zero_when_no_next_fork` — init at last BPO fork returns `[0u8; 4]` - [x] `test_next_fork_digest_zero_after_runtime_transition_to_last_fork` — simulates `update_current_fork` to last fork, then verifies zero Co-Authored-By: alleysira <1367108378@qq.com> Co-Authored-By: Alleysira <56925051+Alleysira@users.noreply.github.com> Co-Authored-By: chonghe <44791194+chong-he@users.noreply.github.com>
This commit is contained in:
@@ -320,11 +320,12 @@ fn compare_enr(local_enr: &Enr, disk_enr: &Enr) -> bool {
|
||||
&& (local_enr.udp4().is_none() || local_enr.udp4() == disk_enr.udp4())
|
||||
&& (local_enr.udp6().is_none() || local_enr.udp6() == disk_enr.udp6())
|
||||
// we need the ATTESTATION_BITFIELD_ENR_KEY and SYNC_COMMITTEE_BITFIELD_ENR_KEY and
|
||||
// PEERDAS_CUSTODY_GROUP_COUNT_ENR_KEY key to match, otherwise we use a new ENR. This will
|
||||
// likely only be true for non-validating nodes.
|
||||
// PEERDAS_CUSTODY_GROUP_COUNT_ENR_KEY and NEXT_FORK_DIGEST_ENR_KEY keys to match,
|
||||
// otherwise we use a new ENR. This will likely only be true for non-validating nodes.
|
||||
&& local_enr.get_decodable::<Bytes>(ATTESTATION_BITFIELD_ENR_KEY) == disk_enr.get_decodable(ATTESTATION_BITFIELD_ENR_KEY)
|
||||
&& local_enr.get_decodable::<Bytes>(SYNC_COMMITTEE_BITFIELD_ENR_KEY) == disk_enr.get_decodable(SYNC_COMMITTEE_BITFIELD_ENR_KEY)
|
||||
&& local_enr.get_decodable::<Bytes>(PEERDAS_CUSTODY_GROUP_COUNT_ENR_KEY) == disk_enr.get_decodable(PEERDAS_CUSTODY_GROUP_COUNT_ENR_KEY)
|
||||
&& local_enr.get_decodable::<Bytes>(NEXT_FORK_DIGEST_ENR_KEY) == disk_enr.get_decodable(NEXT_FORK_DIGEST_ENR_KEY)
|
||||
}
|
||||
|
||||
/// Loads enr from the given directory
|
||||
|
||||
@@ -201,9 +201,7 @@ impl<E: EthSpec> Network<E> {
|
||||
|
||||
// set up a collection of variables accessible outside of the network crate
|
||||
// Create an ENR or load from disk if appropriate
|
||||
// Per [spec](https://github.com/ethereum/consensus-specs/blob/1baa05e71148b0975e28918ac6022d2256b56f4a/specs/fulu/p2p-interface.md?plain=1#L636-L637)
|
||||
// `nfd` must be zero-valued when no next fork is scheduled.
|
||||
let next_fork_digest = ctx.fork_context.next_fork_digest().unwrap_or_default();
|
||||
let next_fork_digest = ctx.fork_context.next_fork_digest();
|
||||
|
||||
let advertised_cgc = config
|
||||
.advertise_false_custody_group_count
|
||||
|
||||
@@ -883,10 +883,7 @@ impl<T: BeaconChainTypes> NetworkService<T> {
|
||||
|
||||
fork_context.update_current_fork(*new_fork_name, new_fork_digest, current_epoch);
|
||||
if self.beacon_chain.spec.is_peer_das_scheduled() {
|
||||
let next_fork_digest = fork_context
|
||||
.next_fork_digest()
|
||||
.unwrap_or_else(|| fork_context.current_fork_digest());
|
||||
self.libp2p.update_nfd(next_fork_digest);
|
||||
self.libp2p.update_nfd(fork_context.next_fork_digest());
|
||||
}
|
||||
|
||||
self.libp2p.update_fork_version(new_enr_fork_id);
|
||||
|
||||
@@ -93,14 +93,16 @@ impl ForkContext {
|
||||
pub fn current_fork_digest(&self) -> [u8; 4] {
|
||||
self.current_fork.read().fork_digest
|
||||
}
|
||||
|
||||
/// Returns the next fork digest. If there's no future fork, returns the current fork digest.
|
||||
pub fn next_fork_digest(&self) -> Option<[u8; 4]> {
|
||||
/// Per [spec](https://github.com/ethereum/consensus-specs/blob/1baa05e71148b0975e28918ac6022d2256b56f4a/specs/fulu/p2p-interface.md?plain=1#L636-L637)
|
||||
/// `nfd` must be zero-valued when no next fork is scheduled.
|
||||
/// Returns the next fork digest. If there's no future fork, returns zero-valued bytes.
|
||||
pub fn next_fork_digest(&self) -> [u8; 4] {
|
||||
let current_fork_epoch = self.current_fork_epoch();
|
||||
self.epoch_to_forks
|
||||
.range(current_fork_epoch..)
|
||||
.nth(1)
|
||||
.map(|(_, fork)| fork.fork_digest)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Updates the `digest_epoch` field to a new digest epoch.
|
||||
@@ -222,11 +224,46 @@ mod tests {
|
||||
|
||||
let context = ForkContext::new::<E>(electra_slot, genesis_root, &spec);
|
||||
|
||||
let next_digest = context.next_fork_digest().unwrap();
|
||||
let next_digest = context.next_fork_digest();
|
||||
let expected_digest = spec.compute_fork_digest(genesis_root, spec.fulu_fork_epoch.unwrap());
|
||||
assert_eq!(next_digest, expected_digest);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_fork_digest_returns_zero_when_no_next_fork() {
|
||||
let spec = make_chain_spec();
|
||||
let genesis_root = Hash256::ZERO;
|
||||
// Epoch 100 is the last BPO fork in make_chain_spec
|
||||
let last_bpo_slot = Epoch::new(100).end_slot(E::slots_per_epoch());
|
||||
|
||||
let context = ForkContext::new::<E>(last_bpo_slot, genesis_root, &spec);
|
||||
|
||||
// No next fork after the last BPO epoch — must return zero bytes per spec
|
||||
assert_eq!(context.next_fork_digest(), [0u8; 4]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_fork_digest_zero_after_runtime_transition_to_last_fork() {
|
||||
let spec = make_chain_spec();
|
||||
let genesis_root = Hash256::ZERO;
|
||||
// Start at Gloas (epoch 7)
|
||||
let gloas_epoch = spec.gloas_fork_epoch.unwrap();
|
||||
let gloas_slot = gloas_epoch.end_slot(E::slots_per_epoch());
|
||||
|
||||
let context = ForkContext::new::<E>(gloas_slot, genesis_root, &spec);
|
||||
|
||||
// Before: next fork exists (BPO at epoch 50)
|
||||
let bpo_50_digest = spec.compute_fork_digest(genesis_root, Epoch::new(50));
|
||||
assert_eq!(context.next_fork_digest(), bpo_50_digest);
|
||||
|
||||
// Simulate runtime transition to the last BPO fork (epoch 100)
|
||||
let last_digest = spec.compute_fork_digest(genesis_root, Epoch::new(100));
|
||||
context.update_current_fork(ForkName::Gloas, last_digest, Epoch::new(100));
|
||||
|
||||
// After: no next fork — must return zero bytes per spec
|
||||
assert_eq!(context.next_fork_digest(), [0u8; 4]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_fork_from_context_bytes() {
|
||||
let spec = make_chain_spec();
|
||||
|
||||
Reference in New Issue
Block a user