From 2cd110886bbac974228e1c3afa2423858b9ad513 Mon Sep 17 00:00:00 2001 From: Andreas Bigger Date: Thu, 27 Aug 2026 13:39:47 -0400 Subject: [PATCH] feat(net): allow updating the fork filter at runtime Co-Authored-By: Claude --- crates/net/network/src/manager.rs | 4 ++++ crates/net/network/src/network.rs | 24 +++++++++++++++++++++++- crates/net/network/src/session/mod.rs | 17 +++++++++++++++++ crates/net/p2p/src/sync.rs | 13 ++++++++++++- 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/crates/net/network/src/manager.rs b/crates/net/network/src/manager.rs index 515b27d690b..63255c41daa 100644 --- a/crates/net/network/src/manager.rs +++ b/crates/net/network/src/manager.rs @@ -776,6 +776,10 @@ impl NetworkManager { self.swarm.state_mut().update_fork_id(transition.current); } } + NetworkHandleMessage::SetForkFilter { fork_filter } => { + let fork_id = self.swarm.sessions_mut().set_fork_filter(fork_filter); + self.swarm.state_mut().update_fork_id(fork_id); + } NetworkHandleMessage::GetPeerInfos(tx) => { let _ = tx.send(self.get_peer_infos()); } diff --git a/crates/net/network/src/network.rs b/crates/net/network/src/network.rs index 18f381b3cbf..0ad5356bfee 100644 --- a/crates/net/network/src/network.rs +++ b/crates/net/network/src/network.rs @@ -12,7 +12,7 @@ use reth_eth_wire::{ BlockRangeUpdate, BroadcastPoolTransactions, DisconnectReason, EthNetworkPrimitives, NetworkPrimitives, NewPooledTransactionHashes, SharedTransactions, }; -use reth_ethereum_forks::Head; +use reth_ethereum_forks::{ForkFilter, Head}; use reth_network_api::{ events::{NetworkPeersEvents, PeerEvent, PeerEventStream}, test_utils::{PeersHandle, PeersHandleProvider}, @@ -112,6 +112,18 @@ impl NetworkHandle { self.send_message(NetworkHandleMessage::StatusUpdate { head }); } + /// Replaces the network's active [`ForkFilter`] with `fork_filter`, re-deriving the advertised + /// [`ForkId`](reth_ethereum_forks::ForkId) for future handshakes and updating the discovery + /// ENR entry. + /// + /// This lets a running node adopt a fork schedule that changed at runtime (e.g. an + /// L1-signalled upgrade) without a restart. The caller must build `fork_filter` from the + /// updated chain spec advanced to the node's current head, and should do so before the fork's + /// activation timestamp so the node announces the upcoming fork ahead of time. + pub fn set_fork_filter(&self, fork_filter: ForkFilter) { + self.send_message(NetworkHandleMessage::SetForkFilter { fork_filter }); + } + /// Announce a block over devp2p /// /// Caution: in `PoS` this is a noop because new blocks are no longer announced over devp2p. @@ -497,6 +509,11 @@ impl NetworkSyncUpdater for NetworkHandle { fn update_block_range(&self, update: reth_eth_wire::BlockRangeUpdate) { self.send_message(NetworkHandleMessage::InternalBlockRangeUpdate(update)); } + + /// Replaces the active fork filter to adopt a runtime fork-schedule change. + fn set_fork_filter(&self, fork_filter: ForkFilter) { + self.send_message(NetworkHandleMessage::SetForkFilter { fork_filter }); + } } impl BlockDownloaderProvider for NetworkHandle { @@ -614,6 +631,11 @@ pub(crate) enum NetworkHandleMessage), /// Gets `PeerInfo` for the specified peer IDs. diff --git a/crates/net/network/src/session/mod.rs b/crates/net/network/src/session/mod.rs index db95a71d984..a70a72742f4 100644 --- a/crates/net/network/src/session/mod.rs +++ b/crates/net/network/src/session/mod.rs @@ -263,6 +263,23 @@ impl SessionManager { transition } + /// Replaces the active [`ForkFilter`] with a newly derived one and returns the resulting + /// [`ForkId`]. + /// + /// This is used to adopt a fork schedule that changed at runtime (e.g. an L1-signalled + /// upgrade) without restarting the node. The caller is expected to build `fork_filter` from + /// the updated chain spec already advanced to the node's current head, so that + /// [`ForkFilter::current`] reflects the correct [`ForkId`] immediately. + /// + /// Every subsequent handshake clones this filter and advertises the returned [`ForkId`], so + /// the node's advertised fork identity stays aligned with the rules it now enforces. Existing + /// active sessions are not revalidated here; see [`SessionManager::active_sessions`]. + pub(crate) fn set_fork_filter(&mut self, fork_filter: ForkFilter) -> ForkId { + self.fork_filter = fork_filter; + self.status.forkid = self.fork_filter.current(); + self.status.forkid + } + /// An incoming TCP connection was received. This starts the authentication process to turn this /// stream into an active peer session. /// diff --git a/crates/net/p2p/src/sync.rs b/crates/net/p2p/src/sync.rs index 77b97116d19..6770c5d12a2 100644 --- a/crates/net/p2p/src/sync.rs +++ b/crates/net/p2p/src/sync.rs @@ -1,6 +1,6 @@ //! Traits used when interacting with the sync status of the network. -use alloy_eips::eip2124::Head; +use alloy_eips::eip2124::{ForkFilter, Head}; use reth_eth_wire_types::BlockRangeUpdate; /// A type that provides information about whether the node is currently syncing and the network is @@ -31,6 +31,17 @@ pub trait NetworkSyncUpdater: std::fmt::Debug + Send + Sync + 'static { /// Updates the advertised block range. fn update_block_range(&self, update: BlockRangeUpdate); + + /// Replaces the node's active [`ForkFilter`] to adopt a fork schedule that changed at runtime + /// (e.g. an L1-signalled upgrade) without a restart. + /// + /// The caller must build `fork_filter` from the updated chain spec advanced to the node's + /// current head, and should do so before the fork's activation timestamp so the node + /// announces the upcoming fork ahead of time. Defaults to a no-op for updaters that do not + /// track a fork filter. + fn set_fork_filter(&self, fork_filter: ForkFilter) { + let _ = fork_filter; + } } /// The state the network is currently in when it comes to synchronization.