diff --git a/crates/net/network/src/config.rs b/crates/net/network/src/config.rs index a999019e2..e7be93e75 100644 --- a/crates/net/network/src/config.rs +++ b/crates/net/network/src/config.rs @@ -1,5 +1,5 @@ use crate::{ - import::{BlockImport, NoopBlockImport}, + import::{BlockImport, ProofOfStakeBlockImport}, peers::PeersConfig, session::SessionsConfig, }; @@ -36,6 +36,8 @@ pub struct NetworkConfig { pub genesis_hash: H256, /// The block importer type. pub block_import: Box, + /// The default mode of the network. + pub network_mode: NetworkMode, } // === impl NetworkConfig === @@ -90,6 +92,8 @@ pub struct NetworkConfigBuilder { genesis_hash: H256, /// The block importer type. block_import: Box, + /// The default mode of the network. + network_mode: NetworkMode, } // === impl NetworkConfigBuilder === @@ -108,7 +112,8 @@ impl NetworkConfigBuilder { fork_id: None, chain: Chain::Named(reth_primitives::rpc::Chain::Mainnet), genesis_hash: Default::default(), - block_import: Box::::default(), + block_import: Box::::default(), + network_mode: Default::default(), } } @@ -138,6 +143,7 @@ impl NetworkConfigBuilder { chain, genesis_hash, block_import, + network_mode, } = self; NetworkConfig { client, @@ -155,6 +161,30 @@ impl NetworkConfigBuilder { chain, genesis_hash, block_import, + network_mode, } } } + +/// Describes the mode of the network wrt. POS or POW. +/// +/// This affects block propagation in the `eth` sub-protocol [EIP-3675](https://eips.ethereum.org/EIPS/eip-3675#devp2p) +/// +/// In POS `NewBlockHashes` and `NewBlock` messages become invalid. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] +pub enum NetworkMode { + /// Network is in proof-of-work mode. + Work, + /// Network is in proof-of-stake mode + #[default] + Stake, +} + +// === impl NetworkMode === + +impl NetworkMode { + /// Returns true if network has entered proof-of-stake + pub fn is_stake(&self) -> bool { + matches!(self, NetworkMode::Stake) + } +} diff --git a/crates/net/network/src/import.rs b/crates/net/network/src/import.rs index b5fb3342e..87f7e30de 100644 --- a/crates/net/network/src/import.rs +++ b/crates/net/network/src/import.rs @@ -50,12 +50,14 @@ pub enum BlockImportError { Consensus(#[from] reth_interfaces::consensus::Error), } -/// An implementation of `BlockImport` that does nothing +/// An implementation of `BlockImport` used in Proof-of-Stake consensus that does nothing. +/// +/// Block propagation over devp2p is invalid in POS: [EIP-3675](https://eips.ethereum.org/EIPS/eip-3675#devp2p) #[derive(Debug, Default)] #[non_exhaustive] -pub struct NoopBlockImport; +pub struct ProofOfStakeBlockImport; -impl BlockImport for NoopBlockImport { +impl BlockImport for ProofOfStakeBlockImport { fn on_new_block(&mut self, _peer_id: PeerId, _incoming_block: NewBlockMessage) {} fn poll(&mut self, _cx: &mut Context<'_>) -> Poll { diff --git a/crates/net/network/src/manager.rs b/crates/net/network/src/manager.rs index 399c9eaf1..192df3329 100644 --- a/crates/net/network/src/manager.rs +++ b/crates/net/network/src/manager.rs @@ -115,6 +115,7 @@ where sessions_config, genesis_hash, block_import, + network_mode, .. } = config; @@ -142,6 +143,7 @@ where to_manager_tx, local_node_id, peers_handle, + network_mode, ); Ok(Self { @@ -211,18 +213,40 @@ where } } + /// Enforces [EIP-3675](https://eips.ethereum.org/EIPS/eip-3675#devp2p) consensus rules for the network protocol + /// + /// Depending on the mode of the network: + /// - disconnect peer if in POS + /// - execute the closure if in POW + fn within_pow_or_disconnect(&mut self, peer_id: PeerId, only_pow: F) + where + F: FnOnce(&mut Self), + { + // reject message in POS + if self.handle.mode().is_stake() { + // connections to peers which send invalid messages should be terminated + self.swarm.sessions_mut().disconnect(peer_id); + } else { + only_pow(self); + } + } + /// Handles a received Message from the peer. fn on_peer_message(&mut self, peer_id: PeerId, msg: PeerMessage) { match msg { PeerMessage::NewBlockHashes(hashes) => { - let hashes = Arc::try_unwrap(hashes).unwrap_or_else(|arc| (*arc).clone()); - // update peer's state, to track what blocks this peer has seen - self.swarm.state_mut().on_new_block_hashes(peer_id, hashes.0) + self.within_pow_or_disconnect(peer_id, |this| { + let hashes = Arc::try_unwrap(hashes).unwrap_or_else(|arc| (*arc).clone()); + // update peer's state, to track what blocks this peer has seen + this.swarm.state_mut().on_new_block_hashes(peer_id, hashes.0) + }) } PeerMessage::NewBlock(block) => { - self.swarm.state_mut().on_new_block(peer_id, block.hash); - // start block import process - self.block_import.on_new_block(peer_id, block); + self.within_pow_or_disconnect(peer_id, move |this| { + this.swarm.state_mut().on_new_block(peer_id, block.hash); + // start block import process + this.block_import.on_new_block(peer_id, block); + }); } PeerMessage::PooledTransactions(msg) => { self.event_listeners @@ -245,6 +269,10 @@ where self.event_listeners.listeners.push(tx); } NetworkHandleMessage::AnnounceBlock(block, hash) => { + if self.handle.mode().is_stake() { + error!(target = "net", "Block propagation is not supported in POS - [EIP-3675](https://eips.ethereum.org/EIPS/eip-3675#devp2p)"); + return + } let msg = NewBlockMessage { hash, block: Arc::new(block) }; self.swarm.state_mut().announce_new_block(msg); } diff --git a/crates/net/network/src/network.rs b/crates/net/network/src/network.rs index 3b797c9f4..9ae672015 100644 --- a/crates/net/network/src/network.rs +++ b/crates/net/network/src/network.rs @@ -1,4 +1,4 @@ -use crate::{manager::NetworkEvent, message::PeerRequest, peers::PeersHandle}; +use crate::{config::NetworkMode, manager::NetworkEvent, message::PeerRequest, peers::PeersHandle}; use parking_lot::Mutex; use reth_eth_wire::{NewBlock, NewPooledTransactionHashes, Transactions}; use reth_primitives::{PeerId, H256}; @@ -27,6 +27,7 @@ impl NetworkHandle { to_manager_tx: UnboundedSender, local_node_id: PeerId, peers: PeersHandle, + network_mode: NetworkMode, ) -> Self { let inner = NetworkInner { num_active_peers, @@ -34,6 +35,7 @@ impl NetworkHandle { listener_address, local_node_id, peers, + network_mode, }; Self { inner: Arc::new(inner) } } @@ -49,6 +51,11 @@ impl NetworkHandle { rx } + /// Returns the mode of the network, either pow, or pos + pub fn mode(&self) -> &NetworkMode { + &self.inner.network_mode + } + /// Sends a [`NetworkHandleMessage`] to the manager pub(crate) fn send_message(&self, msg: NetworkHandleMessage) { let _ = self.inner.to_manager_tx.send(msg); @@ -71,6 +78,8 @@ struct NetworkInner { local_node_id: PeerId, /// Access to the all the nodes peers: PeersHandle, + /// The mode of the network + network_mode: NetworkMode, } /// Internal messages that can be passed to the [`NetworkManager`](crate::NetworkManager).