From 8a4315d83af8cd28b8e8264612552f97ed6aa7e6 Mon Sep 17 00:00:00 2001 From: klkvr Date: Fri, 24 Apr 2026 18:08:39 +0400 Subject: [PATCH 1/5] feat: reject block announcements before RLP deocde --- crates/net/eth-wire/src/ethstream.rs | 32 ++++++++++++++++++++++++-- crates/net/network/src/manager.rs | 8 ++++++- crates/net/network/src/session/conn.rs | 9 ++++++++ crates/net/network/src/session/mod.rs | 16 ++++++++++++- 4 files changed, 61 insertions(+), 4 deletions(-) diff --git a/crates/net/eth-wire/src/ethstream.rs b/crates/net/eth-wire/src/ethstream.rs index ee9f9f568cd..94e00df291a 100644 --- a/crates/net/eth-wire/src/ethstream.rs +++ b/crates/net/eth-wire/src/ethstream.rs @@ -7,7 +7,7 @@ use crate::{ errors::{EthHandshakeError, EthStreamError}, handshake::EthereumEthHandshake, - message::{EthBroadcastMessage, ProtocolBroadcastMessage, MAX_MESSAGE_SIZE}, + message::{EthBroadcastMessage, EthMessageID, ProtocolBroadcastMessage, MAX_MESSAGE_SIZE}, p2pstream::HANDSHAKE_TIMEOUT, CanDisconnect, DisconnectReason, EthMessage, EthNetworkPrimitives, EthVersion, ProtocolMessage, UnifiedStatus, @@ -108,6 +108,9 @@ pub struct EthStreamInner { version: EthVersion, /// Maximum allowed ETH message size. max_message_size: usize, + /// When true, `NewBlock` (0x07) and `NewBlockHashes` (0x01) messages are rejected before RLP + /// decoding to avoid memory amplification from deserializing blocks that will be discarded. + reject_block_announcements: bool, _pd: std::marker::PhantomData, } @@ -122,7 +125,12 @@ where /// Creates a new [`EthStreamInner`] with the given eth version and message size limit. pub const fn with_max_message_size(version: EthVersion, max_message_size: usize) -> Self { - Self { version, max_message_size, _pd: std::marker::PhantomData } + Self { + version, + max_message_size, + reject_block_announcements: false, + _pd: std::marker::PhantomData, + } } /// Returns the eth version @@ -131,12 +139,26 @@ where self.version } + /// Sets whether to reject block announcement messages (`NewBlock`, `NewBlockHashes`) before + /// RLP decoding. + pub const fn set_reject_block_announcements(&mut self, reject: bool) { + self.reject_block_announcements = reject; + } + /// Decodes incoming bytes into an [`EthMessage`]. pub fn decode_message(&self, bytes: BytesMut) -> Result, EthStreamError> { if bytes.len() > self.max_message_size { return Err(EthStreamError::MessageTooBig(bytes.len())); } + if self.reject_block_announcements + && let Some(&id) = bytes.first() + && (id == EthMessageID::NewBlock.to_u8() || + id == EthMessageID::NewBlockHashes.to_u8()) + { + return Err(EthStreamError::UnsupportedMessage { message_id: id }); + } + let msg = match ProtocolMessage::decode_message(self.version, &mut bytes.as_ref()) { Ok(m) => m, Err(err) => { @@ -208,6 +230,12 @@ impl EthStream { self.eth.version() } + /// Sets whether to reject block announcement messages (`NewBlock`, `NewBlockHashes`) before + /// RLP decoding. + pub const fn set_reject_block_announcements(&mut self, reject: bool) { + self.eth.set_reject_block_announcements(reject); + } + /// Returns the underlying stream. #[inline] pub const fn inner(&self) -> &S { diff --git a/crates/net/network/src/manager.rs b/crates/net/network/src/manager.rs index 7225c947882..ec9b6cb378c 100644 --- a/crates/net/network/src/manager.rs +++ b/crates/net/network/src/manager.rs @@ -327,7 +327,13 @@ impl NetworkManager { Arc::clone(&num_active_peers), ); - let swarm = Swarm::new(incoming, sessions, state); + let mut swarm = Swarm::new(incoming, sessions, state); + + // On PoS networks, reject NewBlock/NewBlockHashes before RLP decoding to prevent + // memory amplification from deserialized blocks that would be immediately discarded. + if network_mode.is_stake() { + swarm.sessions_mut().set_reject_block_announcements(true); + } let (to_manager_tx, from_handle_rx) = mpsc::unbounded_channel(); diff --git a/crates/net/network/src/session/conn.rs b/crates/net/network/src/session/conn.rs index ea13cef4f01..85b3f17dbc3 100644 --- a/crates/net/network/src/session/conn.rs +++ b/crates/net/network/src/session/conn.rs @@ -93,6 +93,15 @@ impl EthRlpxConnection { Self::Satellite(conn) => conn.primary_mut().start_send_raw(msg), } } + + /// Sets whether to reject block announcement messages (`NewBlock`, `NewBlockHashes`) before + /// RLP decoding to avoid memory amplification from deserializing blocks that will be discarded. + pub fn set_reject_block_announcements(&mut self, reject: bool) { + match self { + Self::EthOnly(conn) => conn.set_reject_block_announcements(reject), + Self::Satellite(conn) => conn.primary_mut().set_reject_block_announcements(reject), + } + } } impl From> for EthRlpxConnection { diff --git a/crates/net/network/src/session/mod.rs b/crates/net/network/src/session/mod.rs index 943e7ad1a0b..4eac8cc2f2e 100644 --- a/crates/net/network/src/session/mod.rs +++ b/crates/net/network/src/session/mod.rs @@ -123,6 +123,9 @@ pub struct SessionManager { /// Shared local range information that gets propagated to active sessions. /// This represents the range of blocks that this node can serve to other peers. local_range_info: BlockRangeInfo, + /// When true, block announcement messages (`NewBlock`, `NewBlockHashes`) are rejected before + /// RLP decoding on new sessions to avoid memory amplification. + reject_block_announcements: bool, } // === impl SessionManager === @@ -176,9 +179,16 @@ impl SessionManager { handshake, eth_max_message_size, local_range_info, + reject_block_announcements: false, } } + /// Sets whether to reject block announcement messages (`NewBlock`, `NewBlockHashes`) before + /// RLP decoding on all future sessions. + pub const fn set_reject_block_announcements(&mut self, reject: bool) { + self.reject_block_announcements = reject; + } + /// Returns the currently tracked [`ForkId`]. pub(crate) const fn fork_id(&self) -> ForkId { self.fork_filter.current() @@ -496,7 +506,7 @@ impl SessionManager { local_addr, peer_id, capabilities, - conn, + mut conn, status, direction, client_id, @@ -563,6 +573,10 @@ impl SessionManager { BlockRangeInfo::new(update.earliest, update.latest, update.latest_hash) }); + if self.reject_block_announcements { + conn.set_reject_block_announcements(true); + } + let session = ActiveSession { next_id: 0, remote_peer_id: peer_id, From 61aa2d43994c424b73d520e5ecc96ae13eb0d87b Mon Sep 17 00:00:00 2001 From: klkvr Date: Fri, 24 Apr 2026 18:09:24 +0400 Subject: [PATCH 2/5] fix --- crates/net/eth-wire/src/ethstream.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/net/eth-wire/src/ethstream.rs b/crates/net/eth-wire/src/ethstream.rs index 94e00df291a..8bb3a4ab901 100644 --- a/crates/net/eth-wire/src/ethstream.rs +++ b/crates/net/eth-wire/src/ethstream.rs @@ -109,7 +109,7 @@ pub struct EthStreamInner { /// Maximum allowed ETH message size. max_message_size: usize, /// When true, `NewBlock` (0x07) and `NewBlockHashes` (0x01) messages are rejected before RLP - /// decoding to avoid memory amplification from deserializing blocks that will be discarded. + /// decoding to avoid any memory impact for non-PoW networks. reject_block_announcements: bool, _pd: std::marker::PhantomData, } From b6834f5c008edb22329d5df532272c9a5e028f95 Mon Sep 17 00:00:00 2001 From: klkvr Date: Fri, 24 Apr 2026 18:10:03 +0400 Subject: [PATCH 3/5] fix --- crates/net/network/src/manager.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/net/network/src/manager.rs b/crates/net/network/src/manager.rs index ec9b6cb378c..2285e2dbd22 100644 --- a/crates/net/network/src/manager.rs +++ b/crates/net/network/src/manager.rs @@ -329,8 +329,6 @@ impl NetworkManager { let mut swarm = Swarm::new(incoming, sessions, state); - // On PoS networks, reject NewBlock/NewBlockHashes before RLP decoding to prevent - // memory amplification from deserialized blocks that would be immediately discarded. if network_mode.is_stake() { swarm.sessions_mut().set_reject_block_announcements(true); } From bd1c83b9c51b4416ba28af7550d68557ac7443a0 Mon Sep 17 00:00:00 2001 From: klkvr Date: Fri, 24 Apr 2026 18:13:53 +0400 Subject: [PATCH 4/5] fix --- crates/net/network/src/manager.rs | 7 ++----- crates/net/network/src/session/mod.rs | 9 ++------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/crates/net/network/src/manager.rs b/crates/net/network/src/manager.rs index 2285e2dbd22..b9428653ac6 100644 --- a/crates/net/network/src/manager.rs +++ b/crates/net/network/src/manager.rs @@ -318,6 +318,7 @@ impl NetworkManager { extra_protocols, handshake, eth_max_message_size, + network_mode.is_stake(), ); let state = NetworkState::new( @@ -327,11 +328,7 @@ impl NetworkManager { Arc::clone(&num_active_peers), ); - let mut swarm = Swarm::new(incoming, sessions, state); - - if network_mode.is_stake() { - swarm.sessions_mut().set_reject_block_announcements(true); - } + let swarm = Swarm::new(incoming, sessions, state); let (to_manager_tx, from_handle_rx) = mpsc::unbounded_channel(); diff --git a/crates/net/network/src/session/mod.rs b/crates/net/network/src/session/mod.rs index 4eac8cc2f2e..2322d07567d 100644 --- a/crates/net/network/src/session/mod.rs +++ b/crates/net/network/src/session/mod.rs @@ -143,6 +143,7 @@ impl SessionManager { extra_protocols: RlpxSubProtocols, handshake: Arc, eth_max_message_size: usize, + reject_block_announcements: bool, ) -> Self { let (pending_sessions_tx, pending_sessions_rx) = mpsc::channel(config.session_event_buffer); let (active_session_tx, active_session_rx) = mpsc::channel(config.session_event_buffer); @@ -179,16 +180,10 @@ impl SessionManager { handshake, eth_max_message_size, local_range_info, - reject_block_announcements: false, + reject_block_announcements, } } - /// Sets whether to reject block announcement messages (`NewBlock`, `NewBlockHashes`) before - /// RLP decoding on all future sessions. - pub const fn set_reject_block_announcements(&mut self, reject: bool) { - self.reject_block_announcements = reject; - } - /// Returns the currently tracked [`ForkId`]. pub(crate) const fn fork_id(&self) -> ForkId { self.fork_filter.current() From f8c8507fa2a35a7d19f3ae6eb30b47780a64e3d5 Mon Sep 17 00:00:00 2001 From: klkvr Date: Fri, 24 Apr 2026 19:54:32 +0400 Subject: [PATCH 5/5] fmt --- crates/net/eth-wire/src/ethstream.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/net/eth-wire/src/ethstream.rs b/crates/net/eth-wire/src/ethstream.rs index 8bb3a4ab901..805d27d9760 100644 --- a/crates/net/eth-wire/src/ethstream.rs +++ b/crates/net/eth-wire/src/ethstream.rs @@ -151,10 +151,9 @@ where return Err(EthStreamError::MessageTooBig(bytes.len())); } - if self.reject_block_announcements - && let Some(&id) = bytes.first() - && (id == EthMessageID::NewBlock.to_u8() || - id == EthMessageID::NewBlockHashes.to_u8()) + if self.reject_block_announcements && + let Some(&id) = bytes.first() && + (id == EthMessageID::NewBlock.to_u8() || id == EthMessageID::NewBlockHashes.to_u8()) { return Err(EthStreamError::UnsupportedMessage { message_id: id }); }