feat(net): add NetworkMode to enforce POS rules (#215)

This commit is contained in:
Matthias Seitz
2022-11-16 20:42:58 +01:00
committed by GitHub
parent 814640cccd
commit 09ba65fa75
4 changed files with 81 additions and 12 deletions

View File

@ -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)
}
}

View File

@ -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> {

View File

@ -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) => {
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
self.swarm.state_mut().on_new_block_hashes(peer_id, hashes.0)
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);
self.within_pow_or_disconnect(peer_id, move |this| {
this.swarm.state_mut().on_new_block(peer_id, block.hash);
// start block import process
self.block_import.on_new_block(peer_id, block);
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);
}

View File

@ -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).