fix(op): discv5 ENR (#7991)

This commit is contained in:
Emilia Hane
2024-04-30 21:18:19 +02:00
committed by GitHub
parent e09895257a
commit d532217afb
12 changed files with 1241 additions and 1025 deletions

View File

@ -9,9 +9,9 @@ use std::{
use derive_more::Display;
use discv5::ListenConfig;
use multiaddr::{Multiaddr, Protocol};
use reth_primitives::{Bytes, EnrForkIdEntry, ForkId, NodeRecord, MAINNET};
use reth_primitives::{Bytes, EnrForkIdEntry, ForkId, NodeRecord};
use crate::{enr::discv4_id_to_multiaddr_id, filter::MustNotIncludeKeys, network_key};
use crate::{enr::discv4_id_to_multiaddr_id, filter::MustNotIncludeKeys, NetworkStackId};
/// Default interval in seconds at which to run a lookup up query.
///
@ -50,7 +50,7 @@ impl ConfigBuilder {
let Config {
discv5_config,
bootstrap_nodes,
fork: (network_key, fork_id),
fork,
tcp_port,
other_enr_kv_pairs,
lookup_interval,
@ -60,7 +60,7 @@ impl ConfigBuilder {
Self {
discv5_config: Some(discv5_config),
bootstrap_nodes,
fork: Some((network_key, fork_id.fork_id)),
fork: fork.map(|(key, fork_id)| (key, fork_id.fork_id)),
tcp_port,
other_enr_kv_pairs,
lookup_interval: Some(lookup_interval),
@ -117,8 +117,8 @@ impl ConfigBuilder {
/// Set fork ID kv-pair to set in local [`Enr`](discv5::enr::Enr). This lets peers on discovery
/// network know which chain this node belongs to.
pub fn fork(mut self, network_key: &'static [u8], fork_id: ForkId) -> Self {
self.fork = Some((network_key, fork_id));
pub fn fork(mut self, fork_key: &'static [u8], fork_id: ForkId) -> Self {
self.fork = Some((fork_key, fork_id));
self
}
@ -160,13 +160,12 @@ impl ConfigBuilder {
let discv5_config = discv5_config
.unwrap_or_else(|| discv5::ConfigBuilder::new(ListenConfig::default()).build());
let (network_key, fork_id) = fork.unwrap_or((network_key::ETH, MAINNET.latest_fork_id()));
let fork = (network_key, fork_id.into());
let fork = fork.map(|(key, fork_id)| (key, fork_id.into()));
let lookup_interval = lookup_interval.unwrap_or(DEFAULT_SECONDS_LOOKUP_INTERVAL);
let discovered_peer_filter =
discovered_peer_filter.unwrap_or_else(|| MustNotIncludeKeys::new(&[network_key::ETH2]));
let discovered_peer_filter = discovered_peer_filter
.unwrap_or_else(|| MustNotIncludeKeys::new(&[NetworkStackId::ETH2]));
Config {
discv5_config,
@ -190,7 +189,7 @@ pub struct Config {
pub(super) bootstrap_nodes: HashSet<BootNode>,
/// Fork kv-pair to set in local node record. Identifies which network/chain/fork the node
/// belongs, e.g. `(b"opstack", ChainId)` or `(b"eth", [ForkId])`.
pub(super) fork: (&'static [u8], EnrForkIdEntry),
pub(super) fork: Option<(&'static [u8], EnrForkIdEntry)>,
/// RLPx TCP port to advertise.
pub(super) tcp_port: u16,
/// Additional kv-pairs (besides tcp port, udp port and fork) that should be advertised to

View File

@ -11,6 +11,9 @@ pub enum Error {
/// Node record has incompatible key type.
#[error("incompatible key type (not secp256k1)")]
IncompatibleKeyType,
/// No key used to identify rlpx network is configured.
#[error("network stack identifier is not configured")]
NetworkStackIdNotConfigured,
/// Missing key used to identify rlpx network.
#[error("fork missing on enr, key missing")]
ForkMissing(&'static [u8]),

View File

@ -96,7 +96,7 @@ mod tests {
use alloy_rlp::Bytes;
use discv5::enr::{CombinedKey, Enr};
use crate::network_key::{ETH, ETH2};
use crate::NetworkStackId;
use super::*;
@ -104,16 +104,21 @@ mod tests {
fn must_not_include_key_filter() {
// rig test
let filter = MustNotIncludeKeys::new(&[ETH, ETH2]);
let filter = MustNotIncludeKeys::new(&[NetworkStackId::ETH, NetworkStackId::ETH2]);
// enr_1 advertises a fork from one of the keys configured in filter
let sk = CombinedKey::generate_secp256k1();
let enr_1 =
Enr::builder().add_value_rlp(ETH as &[u8], Bytes::from("cancun")).build(&sk).unwrap();
let enr_1 = Enr::builder()
.add_value_rlp(NetworkStackId::ETH as &[u8], Bytes::from("cancun"))
.build(&sk)
.unwrap();
// enr_2 advertises a fork from one the other key configured in filter
let sk = CombinedKey::generate_secp256k1();
let enr_2 = Enr::builder().add_value_rlp(ETH2, Bytes::from("deneb")).build(&sk).unwrap();
let enr_2 = Enr::builder()
.add_value_rlp(NetworkStackId::ETH2, Bytes::from("deneb"))
.build(&sk)
.unwrap();
// test

View File

@ -33,7 +33,7 @@ pub mod enr;
pub mod error;
pub mod filter;
pub mod metrics;
pub mod network_key;
pub mod network_stack_id;
pub use discv5::{self, IpMode};
@ -41,6 +41,7 @@ pub use config::{BootNode, Config, ConfigBuilder};
pub use enr::enr_to_discv4_id;
pub use error::Error;
pub use filter::{FilterOutcome, MustNotIncludeKeys};
pub use network_stack_id::NetworkStackId;
use metrics::{DiscoveredPeersMetrics, Discv5Metrics};
@ -75,7 +76,7 @@ pub struct Discv5 {
/// [`IpMode`] of the the node.
ip_mode: IpMode,
/// Key used in kv-pair to ID chain, e.g. 'opstack' or 'eth'.
fork_key: &'static [u8],
fork_key: Option<&'static [u8]>,
/// Filter applied to a discovered peers before passing it up to app.
discovered_peer_filter: MustNotIncludeKeys,
/// Metrics for underlying [`discv5::Discv5`] node and filtered discovered peers.
@ -217,7 +218,7 @@ impl Discv5 {
fn build_local_enr(
sk: &SecretKey,
config: &Config,
) -> (Enr<SecretKey>, NodeRecord, &'static [u8], IpMode) {
) -> (Enr<SecretKey>, NodeRecord, Option<&'static [u8]>, IpMode) {
let mut builder = discv5::enr::Enr::builder();
let Config { discv5_config, fork, tcp_port, other_enr_kv_pairs, .. } = config;
@ -258,8 +259,10 @@ impl Discv5 {
};
// identifies which network node is on
let (network, fork_value) = fork;
builder.add_value_rlp(network, alloy_rlp::encode(fork_value).into());
let network_stack_id = fork.as_ref().map(|(network_stack_id, fork_value)| {
builder.add_value_rlp(network_stack_id, alloy_rlp::encode(fork_value).into());
*network_stack_id
});
// add other data
for (key, value) in other_enr_kv_pairs {
@ -273,7 +276,7 @@ impl Discv5 {
// backwards compatible enr
let bc_enr = NodeRecord::from_secret_key(socket, sk);
(enr, bc_enr, network, ip_mode)
(enr, bc_enr, network_stack_id, ip_mode)
}
/// Bootstraps underlying [`discv5::Discv5`] node with configured peers.
@ -438,8 +441,10 @@ impl Discv5 {
return None
}
let fork_id =
(self.fork_key == network_key::ETH).then(|| self.get_fork_id(enr).ok()).flatten();
// todo: extend for all network stacks in reth-network rlpx logic
let fork_id = (self.fork_key == Some(NetworkStackId::ETH))
.then(|| self.get_fork_id(enr).ok())
.flatten();
trace!(target: "net::discovery::discv5",
?fork_id,
@ -483,12 +488,13 @@ impl Discv5 {
self.discovered_peer_filter.filter(enr)
}
/// Returns the [`ForkId`] of the given [`Enr`](discv5::Enr), if field is set.
/// Returns the [`ForkId`] of the given [`Enr`](discv5::Enr) w.r.t. the local node's network
/// stack, if field is set.
fn get_fork_id<K: discv5::enr::EnrKey>(
&self,
enr: &discv5::enr::Enr<K>,
) -> Result<ForkId, Error> {
let key = self.fork_key;
let Some(key) = self.fork_key else { return Err(Error::NetworkStackIdNotConfigured) };
let fork_id = enr
.get_decodable::<EnrForkIdEntry>(key)
.ok_or(Error::ForkMissing(key))?
@ -519,7 +525,7 @@ impl Discv5 {
}
/// Returns the key to use to identify the [`ForkId`] kv-pair on the [`Enr`](discv5::Enr).
pub fn fork_key(&self) -> &[u8] {
pub fn fork_key(&self) -> Option<&[u8]> {
self.fork_key
}
}
@ -625,7 +631,7 @@ mod tests {
.unwrap(),
),
ip_mode: IpMode::Ip4,
fork_key: b"noop",
fork_key: None,
discovered_peer_filter: MustNotIncludeKeys::default(),
metrics: Discv5Metrics::default(),
}
@ -831,13 +837,16 @@ mod tests {
const TCP_PORT: u16 = 30303;
let fork_id = MAINNET.latest_fork_id();
let config = Config::builder(TCP_PORT).fork(network_key::ETH, fork_id).build();
let config = Config::builder(TCP_PORT).fork(NetworkStackId::ETH, fork_id).build();
let sk = SecretKey::new(&mut thread_rng());
let (enr, _, _, _) = Discv5::build_local_enr(&sk, &config);
let decoded_fork_id =
enr.get_decodable::<EnrForkIdEntry>(network_key::ETH).unwrap().map(Into::into).unwrap();
let decoded_fork_id = enr
.get_decodable::<EnrForkIdEntry>(NetworkStackId::ETH)
.unwrap()
.map(Into::into)
.unwrap();
assert_eq!(fork_id, decoded_fork_id);
assert_eq!(TCP_PORT, enr.tcp4().unwrap()); // listen config is defaulting to ip mode ipv4

View File

@ -2,7 +2,7 @@
use metrics::{Counter, Gauge};
use reth_metrics::Metrics;
use crate::network_key::{ETH, ETH2, OPSTACK};
use crate::NetworkStackId;
/// Information tracked by [`Discv5`](crate::Discv5).
#[derive(Debug, Default, Clone)]
@ -91,27 +91,34 @@ impl DiscoveredPeersMetrics {
#[derive(Metrics, Clone)]
#[metrics(scope = "discv5")]
pub struct AdvertisedChainMetrics {
/// Frequency of node records with a kv-pair with [`OPSTACK`](crate::network_key) as
/// Frequency of node records with a kv-pair with [`OPEL`](NetworkStackId::OPEL) as
/// key.
opel: Counter,
/// Frequency of node records with a kv-pair with [`OPSTACK`](NetworkStackId::OPSTACK) as
/// key.
opstack: Counter,
/// Frequency of node records with a kv-pair with [`ETH`](crate::network_key) as key.
/// Frequency of node records with a kv-pair with [`ETH`](NetworkStackId::ETH) as key.
eth: Counter,
/// Frequency of node records with a kv-pair with [`ETH2`](crate::network_key) as key.
/// Frequency of node records with a kv-pair with [`ETH2`](NetworkStackId::ETH2) as key.
eth2: Counter,
}
impl AdvertisedChainMetrics {
/// Counts each recognised network type that is advertised on node record, once.
/// Counts each recognised network stack type that is advertised on node record, once.
pub fn increment_once_by_network_type(&self, enr: &discv5::Enr) {
if enr.get_raw_rlp(OPSTACK).is_some() {
if enr.get_raw_rlp(NetworkStackId::OPEL).is_some() {
self.opel.increment(1u64)
}
if enr.get_raw_rlp(NetworkStackId::OPSTACK).is_some() {
self.opstack.increment(1u64)
}
if enr.get_raw_rlp(ETH).is_some() {
if enr.get_raw_rlp(NetworkStackId::ETH).is_some() {
self.eth.increment(1u64)
}
if enr.get_raw_rlp(ETH2).is_some() {
if enr.get_raw_rlp(NetworkStackId::ETH2).is_some() {
self.eth2.increment(1u64)
}
}

View File

@ -1,11 +0,0 @@
//! Keys of ENR [`ForkId`](reth_primitives::ForkId) kv-pair. Identifies which network a node
//! belongs to.
/// ENR fork ID kv-pair key, for an Ethereum L1 EL node.
pub const ETH: &[u8] = b"eth";
/// ENR fork ID kv-pair key, for an Ethereum L1 CL node.
pub const ETH2: &[u8] = b"eth2";
/// ENR fork ID kv-pair key, for an Optimism CL node.
pub const OPSTACK: &[u8] = b"opstack";

View File

@ -0,0 +1,33 @@
//! Keys of ENR [`ForkId`](reth_primitives::ForkId) kv-pair. Identifies which network stack a node
//! belongs to.
use reth_primitives::ChainSpec;
/// Identifies which Ethereum network stack a node belongs to, on the discovery network.
#[derive(Debug)]
pub struct NetworkStackId;
impl NetworkStackId {
/// ENR fork ID kv-pair key, for an Ethereum L1 EL node.
pub const ETH: &'static [u8] = b"eth";
/// ENR fork ID kv-pair key, for an Ethereum L1 CL node.
pub const ETH2: &'static [u8] = b"eth2";
/// ENR fork ID kv-pair key, for an Optimism EL node.
pub const OPEL: &'static [u8] = b"opel";
/// ENR fork ID kv-pair key, for an Optimism CL node.
pub const OPSTACK: &'static [u8] = b"opstack";
/// Returns the [`NetworkStackId`] that matches the given [`ChainSpec`].
pub fn id(chain: &ChainSpec) -> Option<&'static [u8]> {
if chain.is_optimism() {
return Some(Self::OPEL)
} else if chain.is_eth() {
return Some(Self::ETH)
}
None
}
}

View File

@ -9,17 +9,18 @@ use crate::{
NetworkHandle, NetworkManager,
};
use reth_discv4::{Discv4Config, Discv4ConfigBuilder, DEFAULT_DISCOVERY_ADDRESS};
use reth_discv5::network_key;
use reth_discv5::NetworkStackId;
use reth_dns_discovery::DnsDiscoveryConfig;
use reth_eth_wire::{HelloMessage, HelloMessageWithProtocols, Status};
use reth_network_types::{pk2id, PeerId};
use reth_primitives::{
mainnet_nodes, sepolia_nodes, ChainSpec, ForkFilter, Head, NamedChain, NodeRecord, MAINNET,
mainnet_nodes, sepolia_nodes, ChainSpec, ForkFilter, Head, NodeRecord, MAINNET,
};
use reth_provider::{BlockReader, HeaderProvider};
use reth_tasks::{TaskSpawner, TokioTaskExecutor};
use secp256k1::SECP256K1;
use std::{collections::HashSet, net::SocketAddr, sync::Arc};
// re-export for convenience
use crate::protocol::{IntoRlpxSubProtocol, RlpxSubProtocols};
pub use secp256k1::SecretKey;
@ -121,20 +122,16 @@ impl<C> NetworkConfig<C> {
f: impl FnOnce(reth_discv5::ConfigBuilder) -> reth_discv5::Config,
) -> Self {
let rlpx_port = self.listener_addr.port();
let chain = self.chain_spec.chain;
let network_stack_id = NetworkStackId::id(&self.chain_spec);
let fork_id = self.chain_spec.latest_fork_id();
let boot_nodes = self.boot_nodes.clone();
let mut builder =
reth_discv5::Config::builder(rlpx_port).add_unsigned_boot_nodes(boot_nodes.into_iter());
if chain.named() == Some(NamedChain::Mainnet) {
builder = builder.fork(network_key::ETH, fork_id)
if let Some(id) = network_stack_id {
builder = builder.fork(id, fork_id);
}
// todo: set op EL fork id
/*if chain.is_optimism() {
builder = builder.fork(network_key::, fork_id)
}*/
self.set_discovery_v5(f(builder))
}

View File

@ -1,4 +1,4 @@
pub use alloy_chains::{Chain, NamedChain};
pub use alloy_chains::{Chain, ChainKind, NamedChain};
pub use info::ChainInfo;
pub use spec::{
AllGenesisFormats, BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder,

View File

@ -4,8 +4,9 @@ use crate::{
net::{goerli_nodes, mainnet_nodes, sepolia_nodes},
proofs::state_root_ref_unhashed,
revm_primitives::{address, b256},
Address, BlockNumber, Chain, ForkFilter, ForkFilterKey, ForkHash, ForkId, Genesis, Hardfork,
Head, Header, NamedChain, NodeRecord, SealedHeader, B256, EMPTY_OMMER_ROOT_HASH, U256,
Address, BlockNumber, Chain, ChainKind, ForkFilter, ForkFilterKey, ForkHash, ForkId, Genesis,
Hardfork, Head, Header, NamedChain, NodeRecord, SealedHeader, B256, EMPTY_OMMER_ROOT_HASH,
U256,
};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
@ -577,6 +578,24 @@ impl ChainSpec {
self.chain
}
/// Returns `true` if this chain contains Ethereum configuration.
#[inline]
pub fn is_eth(&self) -> bool {
matches!(
self.chain.kind(),
ChainKind::Named(
NamedChain::Mainnet |
NamedChain::Morden |
NamedChain::Ropsten |
NamedChain::Rinkeby |
NamedChain::Goerli |
NamedChain::Kovan |
NamedChain::Holesky |
NamedChain::Sepolia
)
)
}
/// Returns `true` if this chain contains Optimism configuration.
#[inline]
pub fn is_optimism(&self) -> bool {

View File

@ -55,7 +55,7 @@ pub use block::{
ForkBlock, RpcBlockHash, SealedBlock, SealedBlockWithSenders,
};
pub use chain::{
AllGenesisFormats, BaseFeeParams, BaseFeeParamsKind, Chain, ChainInfo, ChainSpec,
AllGenesisFormats, BaseFeeParams, BaseFeeParamsKind, Chain, ChainInfo, ChainKind, ChainSpec,
ChainSpecBuilder, DisplayHardforks, ForkBaseFeeParams, ForkCondition, ForkTimestamps,
NamedChain, DEV, GOERLI, HOLESKY, MAINNET, SEPOLIA,
};

File diff suppressed because it is too large Load Diff