Final changes for fusaka-devnet-2 (#7655)

Closes #7467.

This PR primarily addresses [the P2P changes](https://github.com/ethereum/EIPs/pull/9840) in [fusaka-devnet-2](https://fusaka-devnet-2.ethpandaops.io/). Specifically:

* [the new `nfd` parameter added to the `ENR`](https://github.com/ethereum/EIPs/pull/9840)
* [the modified `compute_fork_digest()` changes for every BPO fork](https://github.com/ethereum/EIPs/pull/9840)

90% of this PR was absolutely hacked together as fast as possible during the Berlinterop as fast as I could while running between Glamsterdam debates. Luckily, it seems to work. But I was unable to be as careful in avoiding bugs as I usually am. I've cleaned up the things *I remember* wanting to come back and have a closer look at. But still working on this.

Progress:
* [x] get it working on `fusaka-devnet-2`
* [ ] [*optional* disconnect from peers with incorrect `nfd` at the fork boundary](https://github.com/ethereum/consensus-specs/pull/4407) - Can be addressed in a future PR if necessary
* [x] first pass clean-up
* [x] fix up all the broken tests
* [x] final self-review
* [x] more thorough review from people more familiar with affected code
This commit is contained in:
ethDreamer
2025-07-10 16:32:58 -05:00
committed by GitHub
parent 3826fe91f4
commit b43e0b446c
26 changed files with 1047 additions and 581 deletions

View File

@@ -3,6 +3,7 @@
pub use discv5::enr::CombinedKey;
use super::enr_ext::CombinedKeyExt;
use super::enr_ext::{EnrExt, QUIC6_ENR_KEY, QUIC_ENR_KEY};
use super::ENR_FILENAME;
use crate::types::{Enr, EnrAttestationBitfield, EnrSyncCommitteeBitfield};
use crate::NetworkConfig;
@@ -18,10 +19,10 @@ use std::str::FromStr;
use tracing::{debug, warn};
use types::{ChainSpec, EnrForkId, EthSpec};
use super::enr_ext::{EnrExt, QUIC6_ENR_KEY, QUIC_ENR_KEY};
/// The ENR field specifying the fork id.
pub const ETH2_ENR_KEY: &str = "eth2";
/// The ENR field specifying the next fork digest.
pub const NEXT_FORK_DIGEST_ENR_KEY: &str = "nfd";
/// The ENR field specifying the attestation subnet bitfield.
pub const ATTESTATION_BITFIELD_ENR_KEY: &str = "attnets";
/// The ENR field specifying the sync committee subnet bitfield.
@@ -42,6 +43,9 @@ pub trait Eth2Enr {
/// The peerdas custody group count associated with the ENR.
fn custody_group_count<E: EthSpec>(&self, spec: &ChainSpec) -> Result<u64, &'static str>;
/// The next fork digest associated with the ENR.
fn next_fork_digest(&self) -> Result<[u8; 4], &'static str>;
fn eth2(&self) -> Result<EnrForkId, &'static str>;
}
@@ -81,6 +85,12 @@ impl Eth2Enr for Enr {
}
}
fn next_fork_digest(&self) -> Result<[u8; 4], &'static str> {
self.get_decodable::<[u8; 4]>(NEXT_FORK_DIGEST_ENR_KEY)
.ok_or("ENR next fork digest non-existent")?
.map_err(|_| "Could not decode the ENR next fork digest")
}
fn eth2(&self) -> Result<EnrForkId, &'static str> {
let eth2_bytes: Bytes = self
.get_decodable(ETH2_ENR_KEY)
@@ -149,13 +159,14 @@ pub fn build_or_load_enr<E: EthSpec>(
local_key: Keypair,
config: &NetworkConfig,
enr_fork_id: &EnrForkId,
next_fork_digest: [u8; 4],
spec: &ChainSpec,
) -> Result<Enr, String> {
// Build the local ENR.
// Note: Discovery should update the ENR record's IP to the external IP as seen by the
// majority of our peers, if the CLI doesn't expressly forbid it.
let enr_key = CombinedKey::from_libp2p(local_key)?;
let mut local_enr = build_enr::<E>(&enr_key, config, enr_fork_id, spec)?;
let mut local_enr = build_enr::<E>(&enr_key, config, enr_fork_id, next_fork_digest, spec)?;
use_or_load_enr(&enr_key, &mut local_enr, config)?;
Ok(local_enr)
@@ -166,6 +177,7 @@ pub fn build_enr<E: EthSpec>(
enr_key: &CombinedKey,
config: &NetworkConfig,
enr_fork_id: &EnrForkId,
next_fork_digest: [u8; 4],
spec: &ChainSpec,
) -> Result<Enr, String> {
let mut builder = discv5::enr::Enr::builder();
@@ -257,7 +269,7 @@ pub fn build_enr<E: EthSpec>(
&bitfield.as_ssz_bytes().into(),
);
// only set `cgc` if PeerDAS fork epoch has been scheduled
// only set `cgc` and `nfd` if PeerDAS fork (Fulu) epoch has been scheduled
if spec.is_peer_das_scheduled() {
let custody_group_count =
if let Some(false_cgc) = config.advertise_false_custody_group_count {
@@ -268,6 +280,7 @@ pub fn build_enr<E: EthSpec>(
spec.custody_requirement
};
builder.add_value(PEERDAS_CUSTODY_GROUP_COUNT_ENR_KEY, &custody_group_count);
builder.add_value(NEXT_FORK_DIGEST_ENR_KEY, &next_fork_digest);
}
builder
@@ -340,6 +353,7 @@ mod test {
use types::{Epoch, MainnetEthSpec};
type E = MainnetEthSpec;
const TEST_NFD: [u8; 4] = [0x01, 0x02, 0x03, 0x04];
fn make_fulu_spec() -> ChainSpec {
let mut spec = E::default_spec();
@@ -351,10 +365,17 @@ mod test {
let keypair = libp2p::identity::secp256k1::Keypair::generate();
let enr_key = CombinedKey::from_secp256k1(&keypair);
let enr_fork_id = EnrForkId::default();
let enr = build_enr::<E>(&enr_key, &config, &enr_fork_id, spec).unwrap();
let enr = build_enr::<E>(&enr_key, &config, &enr_fork_id, TEST_NFD, spec).unwrap();
(enr, enr_key)
}
#[test]
fn test_nfd_enr_encoding() {
let spec = make_fulu_spec();
let enr = build_enr_with_config(NetworkConfig::default(), &spec).0;
assert_eq!(enr.next_fork_digest().unwrap(), TEST_NFD);
}
#[test]
fn custody_group_count_default() {
let config = NetworkConfig {

View File

@@ -49,7 +49,7 @@ use tracing::{debug, error, info, trace, warn};
use types::{ChainSpec, EnrForkId, EthSpec};
mod subnet_predicate;
use crate::discovery::enr::PEERDAS_CUSTODY_GROUP_COUNT_ENR_KEY;
use crate::discovery::enr::{NEXT_FORK_DIGEST_ENR_KEY, PEERDAS_CUSTODY_GROUP_COUNT_ENR_KEY};
pub use subnet_predicate::subnet_predicate;
use types::non_zero_usize::new_non_zero_usize;
@@ -570,6 +570,19 @@ impl<E: EthSpec> Discovery<E> {
Ok(())
}
pub fn update_enr_nfd(&mut self, nfd: [u8; 4]) -> Result<(), String> {
self.discv5
.enr_insert::<Bytes>(NEXT_FORK_DIGEST_ENR_KEY, &nfd.as_ssz_bytes().into())
.map_err(|e| format!("{:?}", e))?;
info!(
next_fork_digest = ?nfd,
"Updating the ENR nfd"
);
enr::save_enr_to_disk(Path::new(&self.enr_dir), &self.local_enr());
*self.network_globals.local_enr.write() = self.discv5.local_enr();
Ok(())
}
/// Updates the `eth2` field of our local ENR.
pub fn update_eth2_enr(&mut self, enr_fork_id: EnrForkId) {
// to avoid having a reference to the spec constant, for the logging we assume
@@ -1217,7 +1230,15 @@ mod tests {
config.set_listening_addr(crate::ListenAddress::unused_v4_ports());
let config = Arc::new(config);
let enr_key: CombinedKey = CombinedKey::from_secp256k1(&keypair);
let enr: Enr = build_enr::<E>(&enr_key, &config, &EnrForkId::default(), &spec).unwrap();
let next_fork_digest = [0; 4];
let enr: Enr = build_enr::<E>(
&enr_key,
&config,
&EnrForkId::default(),
next_fork_digest,
&spec,
)
.unwrap();
let globals = NetworkGlobals::new(
enr,
MetaData::V2(MetaDataV2 {