mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(net): add NetworkMode to enforce POS rules (#215)
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
import::{BlockImport, NoopBlockImport},
|
||||
import::{BlockImport, ProofOfStakeBlockImport},
|
||||
peers::PeersConfig,
|
||||
session::SessionsConfig,
|
||||
};
|
||||
@ -36,6 +36,8 @@ pub struct NetworkConfig<C> {
|
||||
pub genesis_hash: H256,
|
||||
/// The block importer type.
|
||||
pub block_import: Box<dyn BlockImport>,
|
||||
/// The default mode of the network.
|
||||
pub network_mode: NetworkMode,
|
||||
}
|
||||
|
||||
// === impl NetworkConfig ===
|
||||
@ -90,6 +92,8 @@ pub struct NetworkConfigBuilder<C> {
|
||||
genesis_hash: H256,
|
||||
/// The block importer type.
|
||||
block_import: Box<dyn BlockImport>,
|
||||
/// The default mode of the network.
|
||||
network_mode: NetworkMode,
|
||||
}
|
||||
|
||||
// === impl NetworkConfigBuilder ===
|
||||
@ -108,7 +112,8 @@ impl<C> NetworkConfigBuilder<C> {
|
||||
fork_id: None,
|
||||
chain: Chain::Named(reth_primitives::rpc::Chain::Mainnet),
|
||||
genesis_hash: Default::default(),
|
||||
block_import: Box::<NoopBlockImport>::default(),
|
||||
block_import: Box::<ProofOfStakeBlockImport>::default(),
|
||||
network_mode: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,6 +143,7 @@ impl<C> NetworkConfigBuilder<C> {
|
||||
chain,
|
||||
genesis_hash,
|
||||
block_import,
|
||||
network_mode,
|
||||
} = self;
|
||||
NetworkConfig {
|
||||
client,
|
||||
@ -155,6 +161,30 @@ impl<C> NetworkConfigBuilder<C> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<BlockImportOutcome> {
|
||||
|
||||
@ -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<F>(&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);
|
||||
}
|
||||
|
||||
@ -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<NetworkHandleMessage>,
|
||||
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).
|
||||
|
||||
Reference in New Issue
Block a user