Skip to main content

reth_network_api/
noop.rs

1//! A network implementation that does nothing.
2//!
3//! This is useful for wiring components together that don't require network but still need to be
4//! generic over it.
5
6use core::{fmt, marker::PhantomData};
7use std::net::{IpAddr, SocketAddr};
8
9use crate::{
10    events::{NetworkPeersEvents, PeerEventStream},
11    test_utils::{PeersHandle, PeersHandleProvider},
12    BlockDownloaderProvider, CellCustody, DiscoveryEvent, NetworkError, NetworkEvent,
13    NetworkEventListenerProvider, NetworkInfo, NetworkStatus, PeerId, PeerInfo, PeerRequest, Peers,
14    PeersInfo,
15};
16use alloy_rpc_types_admin::EthProtocolInfo;
17use enr::{secp256k1::SecretKey, Enr};
18use reth_eth_wire_types::{
19    DisconnectReason, EthNetworkPrimitives, NetworkPrimitives, ProtocolVersion,
20};
21use reth_network_p2p::{sync::NetworkSyncUpdater, NoopFullBlockClient};
22use reth_network_peers::NodeRecord;
23use reth_network_types::{PeerKind, Reputation, ReputationChangeKind};
24use reth_tokio_util::{EventSender, EventStream};
25use tokio::sync::{mpsc, oneshot};
26use tokio_stream::wrappers::UnboundedReceiverStream;
27
28/// A type that implements all network trait that does nothing.
29///
30/// Intended for testing purposes where network is not used.
31#[derive(Debug, Clone)]
32#[non_exhaustive]
33pub struct NoopNetwork<Net = EthNetworkPrimitives> {
34    chain_id: u64,
35    client_version: String,
36    peers_handle: PeersHandle,
37    cell_custody: CellCustody,
38    _marker: PhantomData<Net>,
39}
40
41impl<Net> NoopNetwork<Net> {
42    /// Creates a new [`NoopNetwork`].
43    pub fn new() -> Self {
44        let (tx, _) = mpsc::unbounded_channel();
45
46        Self {
47            chain_id: 1, // mainnet
48            client_version: "reth-test".to_string(),
49            peers_handle: PeersHandle::new(tx),
50            cell_custody: CellCustody::default(),
51            _marker: PhantomData,
52        }
53    }
54
55    /// Creates a new [`NoopNetwork`] from an existing one but with a new chain id.
56    pub const fn with_chain_id(mut self, chain_id: u64) -> Self {
57        self.chain_id = chain_id;
58        self
59    }
60
61    /// Configures the client version returned by [`NetworkInfo::network_status`].
62    pub fn with_client_version(mut self, client_version: impl Into<String>) -> Self {
63        self.client_version = client_version.into();
64        self
65    }
66}
67
68impl Default for NoopNetwork<EthNetworkPrimitives> {
69    fn default() -> Self {
70        Self::new()
71    }
72}
73
74impl<Net> NetworkInfo for NoopNetwork<Net>
75where
76    Net: Send + Sync,
77{
78    fn local_addr(&self) -> SocketAddr {
79        (IpAddr::from(std::net::Ipv4Addr::UNSPECIFIED), 30303).into()
80    }
81
82    async fn network_status(&self) -> Result<NetworkStatus, NetworkError> {
83        #[expect(deprecated)]
84        Ok(NetworkStatus {
85            client_version: self.client_version.clone(),
86            protocol_version: ProtocolVersion::V5 as u64,
87            eth_protocol_info: EthProtocolInfo {
88                network: 1,
89                difficulty: None,
90                genesis: Default::default(),
91                config: Default::default(),
92                head: Default::default(),
93            },
94            capabilities: vec![],
95        })
96    }
97
98    fn chain_id(&self) -> u64 {
99        self.chain_id
100    }
101
102    fn cell_custody(&self) -> &CellCustody {
103        &self.cell_custody
104    }
105
106    fn is_syncing(&self) -> bool {
107        false
108    }
109
110    fn is_initially_syncing(&self) -> bool {
111        false
112    }
113}
114
115impl<Net> PeersInfo for NoopNetwork<Net>
116where
117    Net: Send + Sync,
118{
119    fn num_connected_peers(&self) -> usize {
120        0
121    }
122
123    fn local_node_record(&self) -> NodeRecord {
124        NodeRecord::new(self.local_addr(), PeerId::random())
125    }
126
127    fn local_enr(&self) -> Enr<SecretKey> {
128        let sk = SecretKey::from_slice(&[0xcd; 32]).unwrap();
129        Enr::builder().build(&sk).unwrap()
130    }
131}
132
133impl<Net> Peers for NoopNetwork<Net>
134where
135    Net: Send + Sync,
136{
137    fn add_trusted_peer_id(&self, _peer: PeerId) {}
138
139    fn add_peer_kind(
140        &self,
141        _peer: PeerId,
142        _kind: Option<PeerKind>,
143        _tcp_addr: SocketAddr,
144        _udp_addr: Option<SocketAddr>,
145    ) {
146    }
147
148    async fn get_peers_by_kind(&self, _kind: PeerKind) -> Result<Vec<PeerInfo>, NetworkError> {
149        Ok(vec![])
150    }
151
152    async fn get_all_peers(&self) -> Result<Vec<PeerInfo>, NetworkError> {
153        Ok(vec![])
154    }
155
156    async fn get_peer_by_id(&self, _peer_id: PeerId) -> Result<Option<PeerInfo>, NetworkError> {
157        Ok(None)
158    }
159
160    async fn get_peers_by_id(&self, _peer_id: Vec<PeerId>) -> Result<Vec<PeerInfo>, NetworkError> {
161        Ok(vec![])
162    }
163
164    fn remove_peer(&self, _peer: PeerId, _kind: PeerKind) {}
165
166    fn disconnect_peer(&self, _peer: PeerId) {}
167
168    fn disconnect_peer_with_reason(&self, _peer: PeerId, _reason: DisconnectReason) {}
169
170    fn ban_peer(&self, _peer: PeerId) {}
171
172    fn unban_peer(&self, _peer: PeerId) {}
173
174    fn connect_peer_kind(
175        &self,
176        _peer: PeerId,
177        _kind: PeerKind,
178        _tcp_addr: SocketAddr,
179        _udp_addr: Option<SocketAddr>,
180    ) {
181    }
182
183    fn reputation_change(&self, _peer_id: PeerId, _kind: ReputationChangeKind) {}
184
185    async fn reputation_by_id(&self, _peer_id: PeerId) -> Result<Option<Reputation>, NetworkError> {
186        Ok(None)
187    }
188}
189
190impl<Net> BlockDownloaderProvider for NoopNetwork<Net>
191where
192    Net: NetworkPrimitives,
193{
194    type Client = NoopFullBlockClient<Net>;
195
196    async fn fetch_client(&self) -> Result<Self::Client, oneshot::error::RecvError> {
197        Ok(NoopFullBlockClient::<Net>::default())
198    }
199}
200
201impl<Net> NetworkSyncUpdater for NoopNetwork<Net>
202where
203    Net: fmt::Debug + Send + Sync + 'static,
204{
205    fn update_status(&self, _head: reth_ethereum_forks::Head) {}
206
207    fn update_sync_state(&self, _state: reth_network_p2p::sync::SyncState) {}
208
209    fn update_block_range(&self, _: reth_eth_wire_types::BlockRangeUpdate) {}
210}
211
212impl<Net> NetworkEventListenerProvider for NoopNetwork<Net>
213where
214    Net: NetworkPrimitives,
215{
216    type Primitives = Net;
217
218    fn event_listener(&self) -> EventStream<NetworkEvent<PeerRequest<Self::Primitives>>> {
219        let event_sender: EventSender<NetworkEvent<PeerRequest<Net>>> = Default::default();
220        event_sender.new_listener()
221    }
222
223    fn discovery_listener(&self) -> UnboundedReceiverStream<DiscoveryEvent> {
224        let (_, rx) = mpsc::unbounded_channel();
225        UnboundedReceiverStream::new(rx)
226    }
227}
228
229impl<Net> NetworkPeersEvents for NoopNetwork<Net>
230where
231    Net: NetworkPrimitives,
232{
233    fn peer_events(&self) -> PeerEventStream {
234        let event_sender: EventSender<NetworkEvent<PeerRequest<Net>>> = Default::default();
235        PeerEventStream::new(event_sender.new_listener())
236    }
237}
238
239impl<Net> PeersHandleProvider for NoopNetwork<Net>
240where
241    Net: NetworkPrimitives,
242{
243    fn peers_handle(&self) -> &PeersHandle {
244        &self.peers_handle
245    }
246}
247
248#[cfg(test)]
249mod tests {
250    use super::*;
251
252    #[test]
253    fn configures_client_version() {
254        let network = NoopNetwork::default().with_client_version("custom-client/v1.0.0");
255
256        assert_eq!(
257            futures::executor::block_on(network.network_status()).unwrap().client_version,
258            "custom-client/v1.0.0"
259        );
260    }
261}