mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-02-19 03:04:27 -05:00
feat(net): enforce EIP-868 fork ID for discovered peers (#22013)
Co-authored-by: Emma <emma@tempo.xyz> Co-authored-by: Matthias Seitz <mattsse@users.noreply.github.com> Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: Emma Jamieson-Hoare <ejamieson19@gmail.com> Co-authored-by: Emma Jamieson-Hoare <emmajam@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
4a62d38af2
commit
0b8e6c6ed3
9
.changelog/nice-trees-drink.md
Normal file
9
.changelog/nice-trees-drink.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
reth-network-api: minor
|
||||
reth-network-types: minor
|
||||
reth-network: minor
|
||||
reth-node-core: minor
|
||||
reth: minor
|
||||
---
|
||||
|
||||
Added optional ENR fork ID enforcement to filter out peers from incompatible networks during peer discovery, controlled by the `--enforce-enr-fork-id` CLI flag.
|
||||
@@ -8,7 +8,7 @@ use reth_eth_wire_types::{
|
||||
};
|
||||
use reth_ethereum_forks::ForkId;
|
||||
use reth_network_p2p::error::{RequestError, RequestResult};
|
||||
use reth_network_peers::PeerId;
|
||||
use reth_network_peers::{NodeRecord, PeerId};
|
||||
use reth_network_types::{PeerAddr, PeerKind};
|
||||
use reth_tokio_util::EventStream;
|
||||
use std::{
|
||||
@@ -152,8 +152,13 @@ pub trait NetworkEventListenerProvider: NetworkPeersEvents {
|
||||
pub enum DiscoveryEvent {
|
||||
/// Discovered a node
|
||||
NewNode(DiscoveredEvent),
|
||||
/// Retrieved a [`ForkId`] from the peer via ENR request, See <https://eips.ethereum.org/EIPS/eip-868>
|
||||
EnrForkId(PeerId, ForkId),
|
||||
/// Retrieved a [`ForkId`] from the peer via ENR request.
|
||||
///
|
||||
/// Contains the full [`NodeRecord`] (peer ID + address) and the reported [`ForkId`].
|
||||
/// Used to verify fork compatibility before admitting the peer.
|
||||
///
|
||||
/// See also <https://eips.ethereum.org/EIPS/eip-868>
|
||||
EnrForkId(NodeRecord, ForkId),
|
||||
}
|
||||
|
||||
/// Represents events related to peer discovery in the network.
|
||||
|
||||
@@ -172,6 +172,11 @@ pub struct PeersConfig {
|
||||
/// IPs within the specified CIDR ranges will be allowed.
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
pub ip_filter: IpFilter,
|
||||
/// If true, discovered peers without a confirmed ENR [`ForkId`](alloy_eip2124::ForkId)
|
||||
/// (EIP-868) will not be added to the peer set until their fork ID is verified.
|
||||
///
|
||||
/// This filters out peers from other networks that pollute the discovery table.
|
||||
pub enforce_enr_fork_id: bool,
|
||||
}
|
||||
|
||||
impl Default for PeersConfig {
|
||||
@@ -191,6 +196,7 @@ impl Default for PeersConfig {
|
||||
max_backoff_count: 5,
|
||||
incoming_ip_throttle_duration: INBOUND_IP_THROTTLE_DURATION,
|
||||
ip_filter: IpFilter::default(),
|
||||
enforce_enr_fork_id: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -314,6 +320,13 @@ impl PeersConfig {
|
||||
self
|
||||
}
|
||||
|
||||
/// If set, discovered peers without a confirmed ENR [`ForkId`](alloy_eip2124::ForkId) will not
|
||||
/// be added to the peer set until their fork ID is verified via EIP-868.
|
||||
pub const fn with_enforce_enr_fork_id(mut self, enforce: bool) -> Self {
|
||||
self.enforce_enr_fork_id = enforce;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns settings for testing
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
pub fn test() -> Self {
|
||||
|
||||
@@ -240,7 +240,7 @@ impl Discovery {
|
||||
self.on_node_record_update(record, None);
|
||||
}
|
||||
DiscoveryUpdate::EnrForkId(node, fork_id) => {
|
||||
self.queued_events.push_back(DiscoveryEvent::EnrForkId(node.id, fork_id))
|
||||
self.queued_events.push_back(DiscoveryEvent::EnrForkId(node, fork_id))
|
||||
}
|
||||
DiscoveryUpdate::Removed(peer_id) => {
|
||||
self.discovered_nodes.remove(&peer_id);
|
||||
|
||||
@@ -92,6 +92,9 @@ pub struct PeersManager {
|
||||
incoming_ip_throttle_duration: Duration,
|
||||
/// IP address filter for restricting network connections to specific IP ranges.
|
||||
ip_filter: reth_net_banlist::IpFilter,
|
||||
/// If true, discovered peers without a confirmed ENR fork ID will not be added until their
|
||||
/// fork ID is verified via EIP-868.
|
||||
enforce_enr_fork_id: bool,
|
||||
}
|
||||
|
||||
impl PeersManager {
|
||||
@@ -111,6 +114,7 @@ impl PeersManager {
|
||||
max_backoff_count,
|
||||
incoming_ip_throttle_duration,
|
||||
ip_filter,
|
||||
enforce_enr_fork_id,
|
||||
} = config;
|
||||
let (manager_tx, handle_rx) = mpsc::unbounded_channel();
|
||||
let now = Instant::now();
|
||||
@@ -167,6 +171,7 @@ impl PeersManager {
|
||||
net_connection_state: NetworkConnectionState::default(),
|
||||
incoming_ip_throttle_duration,
|
||||
ip_filter,
|
||||
enforce_enr_fork_id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +180,11 @@ impl PeersManager {
|
||||
PeersHandle::new(self.manager_tx.clone())
|
||||
}
|
||||
|
||||
/// Returns `true` if discovered peers must have a confirmed ENR fork ID before being added.
|
||||
pub(crate) const fn enforce_enr_fork_id(&self) -> bool {
|
||||
self.enforce_enr_fork_id
|
||||
}
|
||||
|
||||
/// Returns the number of peers in the peer set
|
||||
#[inline]
|
||||
pub(crate) fn num_known_peers(&self) -> usize {
|
||||
@@ -738,17 +748,6 @@ impl PeersManager {
|
||||
}
|
||||
}
|
||||
|
||||
/// Called as follow-up for a discovered peer.
|
||||
///
|
||||
/// The [`ForkId`] is retrieved from an ENR record that the peer announces over the discovery
|
||||
/// protocol
|
||||
pub(crate) fn set_discovered_fork_id(&mut self, peer_id: PeerId, fork_id: ForkId) {
|
||||
if let Some(peer) = self.peers.get_mut(&peer_id) {
|
||||
trace!(target: "net::peers", ?peer_id, ?fork_id, "set discovered fork id");
|
||||
peer.fork_id = Some(Box::new(fork_id));
|
||||
}
|
||||
}
|
||||
|
||||
/// Called for a newly discovered peer.
|
||||
///
|
||||
/// If the peer already exists, then the address, kind and `fork_id` will be updated.
|
||||
|
||||
@@ -332,9 +332,19 @@ impl<N: NetworkPrimitives> NetworkState<N> {
|
||||
fork_id,
|
||||
});
|
||||
}
|
||||
DiscoveryEvent::EnrForkId(peer_id, fork_id) => {
|
||||
self.queued_messages
|
||||
.push_back(StateAction::DiscoveredEnrForkId { peer_id, fork_id });
|
||||
DiscoveryEvent::EnrForkId(record, fork_id) => {
|
||||
let peer_id = record.id;
|
||||
let tcp_addr = record.tcp_addr();
|
||||
if tcp_addr.port() == 0 {
|
||||
return
|
||||
}
|
||||
let udp_addr = record.udp_addr();
|
||||
let addr = PeerAddr::new(tcp_addr, Some(udp_addr));
|
||||
self.queued_messages.push_back(StateAction::DiscoveredEnrForkId {
|
||||
peer_id,
|
||||
addr,
|
||||
fork_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -552,6 +562,8 @@ pub(crate) enum StateAction<N: NetworkPrimitives> {
|
||||
/// Retrieved a [`ForkId`] from the peer via ENR request, See <https://eips.ethereum.org/EIPS/eip-868>
|
||||
DiscoveredEnrForkId {
|
||||
peer_id: PeerId,
|
||||
/// The address of the peer.
|
||||
addr: PeerAddr,
|
||||
/// The reported [`ForkId`] by this peer.
|
||||
fork_id: ForkId,
|
||||
},
|
||||
|
||||
@@ -246,18 +246,28 @@ impl<N: NetworkPrimitives> Swarm<N> {
|
||||
StateAction::PeerAdded(peer_id) => return Some(SwarmEvent::PeerAdded(peer_id)),
|
||||
StateAction::PeerRemoved(peer_id) => return Some(SwarmEvent::PeerRemoved(peer_id)),
|
||||
StateAction::DiscoveredNode { peer_id, addr, fork_id } => {
|
||||
// Don't try to connect to peer if node is shutting down
|
||||
if self.is_shutting_down() {
|
||||
return None
|
||||
}
|
||||
// Insert peer only if no fork id or a valid fork id
|
||||
if fork_id.map_or_else(|| true, |f| self.sessions.is_valid_fork_id(f)) {
|
||||
|
||||
// When `enforce_enr_fork_id` is enabled, peers discovered without a confirmed
|
||||
// fork ID (via EIP-868 ENR) are deferred — they'll only be added once a
|
||||
// `DiscoveredEnrForkId` event arrives with a validated fork ID.
|
||||
//
|
||||
// When disabled (default), peers without a fork ID are admitted immediately.
|
||||
// Peers that *do* carry a fork ID are always validated against ours.
|
||||
let enforce = self.state().peers().enforce_enr_fork_id();
|
||||
let allow = match fork_id {
|
||||
Some(f) => self.sessions.is_valid_fork_id(f),
|
||||
None => !enforce,
|
||||
};
|
||||
if allow {
|
||||
self.state_mut().peers_mut().add_peer(peer_id, addr, fork_id);
|
||||
}
|
||||
}
|
||||
StateAction::DiscoveredEnrForkId { peer_id, fork_id } => {
|
||||
StateAction::DiscoveredEnrForkId { peer_id, addr, fork_id } => {
|
||||
if self.sessions.is_valid_fork_id(fork_id) {
|
||||
self.state_mut().peers_mut().set_discovered_fork_id(peer_id, fork_id);
|
||||
self.state_mut().peers_mut().add_peer(peer_id, addr, Some(fork_id));
|
||||
} else {
|
||||
trace!(target: "net", ?peer_id, remote_fork_id=?fork_id, our_fork_id=?self.sessions.fork_id(), "fork id mismatch, removing peer");
|
||||
self.state_mut().peers_mut().remove_peer(peer_id);
|
||||
|
||||
@@ -227,6 +227,14 @@ pub struct NetworkArgs {
|
||||
/// Example: --netrestrict "192.168.0.0/16,10.0.0.0/8"
|
||||
#[arg(long, value_name = "NETRESTRICT")]
|
||||
pub netrestrict: Option<String>,
|
||||
|
||||
/// Enforce EIP-868 ENR fork ID validation for discovered peers.
|
||||
///
|
||||
/// When enabled, peers discovered without a confirmed fork ID are not added to the peer set
|
||||
/// until their fork ID is verified via EIP-868 ENR request. This filters out peers from other
|
||||
/// networks that pollute the discovery table.
|
||||
#[arg(long)]
|
||||
pub enforce_enr_fork_id: bool,
|
||||
}
|
||||
|
||||
impl NetworkArgs {
|
||||
@@ -333,7 +341,8 @@ impl NetworkArgs {
|
||||
)
|
||||
.with_max_inbound_opt(self.resolved_max_inbound_peers())
|
||||
.with_max_outbound_opt(self.resolved_max_outbound_peers())
|
||||
.with_ip_filter(ip_filter);
|
||||
.with_ip_filter(ip_filter)
|
||||
.with_enforce_enr_fork_id(self.enforce_enr_fork_id);
|
||||
|
||||
// Configure basic network stack
|
||||
NetworkConfigBuilder::<N>::new(secret_key)
|
||||
@@ -491,6 +500,7 @@ impl Default for NetworkArgs {
|
||||
required_block_hashes: vec![],
|
||||
network_id: None,
|
||||
netrestrict: None,
|
||||
enforce_enr_fork_id: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,6 +293,11 @@ Networking:
|
||||
|
||||
Example: --netrestrict "192.168.0.0/16,10.0.0.0/8"
|
||||
|
||||
--enforce-enr-fork-id
|
||||
Enforce EIP-868 ENR fork ID validation for discovered peers.
|
||||
|
||||
When enabled, peers discovered without a confirmed fork ID are not added to the peer set until their fork ID is verified via EIP-868 ENR request. This filters out peers from other networks that pollute the discovery table.
|
||||
|
||||
RPC:
|
||||
--http
|
||||
Enable the HTTP-RPC server
|
||||
|
||||
@@ -233,6 +233,11 @@ Networking:
|
||||
|
||||
Example: --netrestrict "192.168.0.0/16,10.0.0.0/8"
|
||||
|
||||
--enforce-enr-fork-id
|
||||
Enforce EIP-868 ENR fork ID validation for discovered peers.
|
||||
|
||||
When enabled, peers discovered without a confirmed fork ID are not added to the peer set until their fork ID is verified via EIP-868 ENR request. This filters out peers from other networks that pollute the discovery table.
|
||||
|
||||
Datadir:
|
||||
--datadir <DATA_DIR>
|
||||
The path to the data dir for all reth files and subdirectories.
|
||||
|
||||
@@ -233,6 +233,11 @@ Networking:
|
||||
|
||||
Example: --netrestrict "192.168.0.0/16,10.0.0.0/8"
|
||||
|
||||
--enforce-enr-fork-id
|
||||
Enforce EIP-868 ENR fork ID validation for discovered peers.
|
||||
|
||||
When enabled, peers discovered without a confirmed fork ID are not added to the peer set until their fork ID is verified via EIP-868 ENR request. This filters out peers from other networks that pollute the discovery table.
|
||||
|
||||
Datadir:
|
||||
--datadir <DATA_DIR>
|
||||
The path to the data dir for all reth files and subdirectories.
|
||||
|
||||
@@ -460,6 +460,11 @@ Networking:
|
||||
|
||||
Example: --netrestrict "192.168.0.0/16,10.0.0.0/8"
|
||||
|
||||
--enforce-enr-fork-id
|
||||
Enforce EIP-868 ENR fork ID validation for discovered peers.
|
||||
|
||||
When enabled, peers discovered without a confirmed fork ID are not added to the peer set until their fork ID is verified via EIP-868 ENR request. This filters out peers from other networks that pollute the discovery table.
|
||||
|
||||
Logging:
|
||||
--log.stdout.format <FORMAT>
|
||||
The format to use for logs written to stdout
|
||||
|
||||
Reference in New Issue
Block a user