1use crate::{
4 cache::LruCache,
5 discovery::Discovery,
6 fetch::{BlockResponseOutcome, FetchAction, NewPeerInfo, StateFetcher},
7 message::{BlockRequest, NewBlockMessage, PeerResponse, PeerResponseResult},
8 peers::{PeerAction, PeersManager},
9 session::BlockRangeInfo,
10 FetchClient,
11};
12use alloy_consensus::BlockHeader;
13use alloy_primitives::{
14 map::{FbBuildHasher, HashMap},
15 B256,
16};
17use rand::seq::SliceRandom;
18use reth_eth_wire::{
19 BlockHashNumber, Capabilities, DisconnectReason, EthNetworkPrimitives, EthVersion,
20 GetReceipts70, NetworkPrimitives, NewBlockHashes, NewBlockPayload, UnifiedStatus,
21};
22use reth_ethereum_forks::ForkId;
23use reth_network_api::{DiscoveredEvent, DiscoveryEvent, PeerRequest, PeerRequestSender};
24use reth_network_p2p::receipts::client::ReceiptsResponse;
25use reth_network_peers::PeerId;
26use reth_network_types::{PeerAddr, PeerKind};
27use reth_primitives_traits::Block;
28use std::{
29 collections::VecDeque,
30 fmt,
31 net::{IpAddr, SocketAddr},
32 ops::Deref,
33 sync::{
34 atomic::{AtomicU64, AtomicUsize},
35 Arc,
36 },
37 task::{Context, Poll},
38};
39use tokio::sync::oneshot;
40use tracing::{debug, trace};
41
42const PEER_BLOCK_CACHE_LIMIT: u32 = 512;
44
45pub(crate) struct BlockNumReader(Box<dyn reth_storage_api::BlockNumReader>);
47
48impl BlockNumReader {
49 pub fn new(reader: impl reth_storage_api::BlockNumReader + 'static) -> Self {
51 Self(Box::new(reader))
52 }
53}
54
55impl fmt::Debug for BlockNumReader {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 f.debug_struct("BlockNumReader").field("inner", &"<dyn BlockNumReader>").finish()
58 }
59}
60
61impl Deref for BlockNumReader {
62 type Target = Box<dyn reth_storage_api::BlockNumReader>;
63
64 fn deref(&self) -> &Self::Target {
65 &self.0
66 }
67}
68
69#[derive(Debug)]
80pub struct NetworkState<N: NetworkPrimitives = EthNetworkPrimitives> {
81 active_peers: HashMap<PeerId, ActivePeer<N>, FbBuildHasher<64>>,
83 peers_manager: PeersManager,
85 queued_messages: VecDeque<StateAction<N>>,
87 client: BlockNumReader,
92 discovery: Discovery,
94 state_fetcher: StateFetcher<N>,
100}
101
102impl<N: NetworkPrimitives> NetworkState<N> {
103 pub(crate) fn new(
105 client: BlockNumReader,
106 discovery: Discovery,
107 peers_manager: PeersManager,
108 num_active_peers: Arc<AtomicUsize>,
109 ) -> Self {
110 let state_fetcher = StateFetcher::new(peers_manager.handle(), num_active_peers);
111 Self {
112 active_peers: Default::default(),
113 peers_manager,
114 queued_messages: Default::default(),
115 client,
116 discovery,
117 state_fetcher,
118 }
119 }
120
121 pub(crate) const fn peers_mut(&mut self) -> &mut PeersManager {
123 &mut self.peers_manager
124 }
125
126 pub(crate) const fn discovery_mut(&mut self) -> &mut Discovery {
128 &mut self.discovery
129 }
130
131 pub(crate) const fn peers(&self) -> &PeersManager {
133 &self.peers_manager
134 }
135
136 pub(crate) fn fetch_client(&self) -> FetchClient<N> {
138 self.state_fetcher.client()
139 }
140
141 pub fn num_active_peers(&self) -> usize {
143 self.active_peers.len()
144 }
145
146 pub(crate) fn on_session_activated(&mut self, activation: SessionActivation<N>) {
151 let SessionActivation {
152 peer,
153 capabilities,
154 version,
155 status,
156 request_tx,
157 timeout,
158 range_info,
159 supports_snap,
160 } = activation;
161
162 debug_assert!(!self.active_peers.contains_key(&peer), "Already connected; not possible");
163
164 let block_number = status.latest_block.unwrap_or_else(|| {
167 self.client.block_number(status.blockhash).ok().flatten().unwrap_or_default()
168 });
169 self.state_fetcher.new_active_peer(NewPeerInfo {
170 peer_id: peer,
171 best_hash: status.blockhash,
172 best_number: block_number,
173 capabilities: Arc::clone(&capabilities),
174 timeout,
175 range_info,
176 supports_snap,
177 });
178
179 self.active_peers.insert(
180 peer,
181 ActivePeer {
182 best_hash: status.blockhash,
183 version,
184 request_tx,
185 pending_response: None,
186 blocks: LruCache::new(PEER_BLOCK_CACHE_LIMIT),
187 },
188 );
189 }
190
191 pub(crate) fn on_session_closed(&mut self, peer: PeerId) {
195 self.active_peers.remove(&peer);
196 self.state_fetcher.on_session_closed(&peer);
197 }
198
199 pub(crate) fn announce_new_block(&mut self, msg: NewBlockMessage<N::NewBlockPayload>) {
208 let num_propagate = (self.active_peers.len() as f64).sqrt() as u64 + 1;
211
212 let number = msg.block.block().header().number();
213 let mut count = 0;
214
215 let mut peers: Vec<_> = self.active_peers.iter_mut().collect();
217 peers.shuffle(&mut rand::rng());
218
219 for (peer_id, peer) in peers {
220 if peer.blocks.contains(&msg.hash) {
221 continue
223 }
224
225 if count < num_propagate {
227 self.queued_messages
228 .push_back(StateAction::NewBlock { peer_id: *peer_id, block: msg.clone() });
229
230 if self.state_fetcher.update_peer_block(peer_id, msg.hash, number) {
232 peer.best_hash = msg.hash;
233 }
234
235 peer.blocks.insert(msg.hash);
237
238 count += 1;
239 }
240
241 if count >= num_propagate {
242 break
243 }
244 }
245 }
246
247 pub(crate) fn announce_new_block_hash(&mut self, msg: NewBlockMessage<N::NewBlockPayload>) {
250 let number = msg.block.block().header().number();
251 let hashes = NewBlockHashes(vec![BlockHashNumber { hash: msg.hash, number }]);
252 for (peer_id, peer) in &mut self.active_peers {
253 if peer.blocks.contains(&msg.hash) {
254 continue
256 }
257
258 if self.state_fetcher.update_peer_block(peer_id, msg.hash, number) {
259 peer.best_hash = msg.hash;
260 }
261
262 self.queued_messages.push_back(StateAction::NewBlockHashes {
263 peer_id: *peer_id,
264 hashes: hashes.clone(),
265 });
266 }
267 }
268
269 pub(crate) fn update_peer_block(&mut self, peer_id: &PeerId, hash: B256, number: u64) {
271 if let Some(peer) = self.active_peers.get_mut(peer_id) {
272 peer.best_hash = hash;
273 }
274 self.state_fetcher.update_peer_block(peer_id, hash, number);
275 }
276
277 pub(crate) fn update_fork_id(&self, fork_id: ForkId) {
279 self.discovery.update_fork_id(fork_id)
280 }
281
282 pub(crate) fn on_new_block(&mut self, peer_id: PeerId, hash: B256) {
286 if let Some(peer) = self.active_peers.get_mut(&peer_id) {
288 peer.blocks.insert(hash);
289 }
290 }
291
292 pub(crate) fn on_new_block_hashes(&mut self, peer_id: PeerId, hashes: Vec<BlockHashNumber>) {
294 if let Some(peer) = self.active_peers.get_mut(&peer_id) {
296 peer.blocks.extend(hashes.into_iter().map(|b| b.hash));
297 }
298 }
299
300 pub(crate) fn ban_ip_discovery(&self, ip: IpAddr) {
302 trace!(target: "net", ?ip, "Banning discovery");
303 self.discovery.ban_ip(ip)
304 }
305
306 pub(crate) fn ban_discovery(&self, peer_id: PeerId, ip: IpAddr) {
308 trace!(target: "net", ?peer_id, ?ip, "Banning discovery");
309 self.discovery.ban(peer_id, ip)
310 }
311
312 pub(crate) fn add_trusted_peer_id(&mut self, peer_id: PeerId) {
314 self.peers_manager.add_trusted_peer_id(peer_id)
315 }
316
317 pub(crate) fn add_trusted_peer_node(&mut self, trusted: reth_network_peers::TrustedPeer) {
319 self.peers_manager.add_trusted_peer_node(trusted)
320 }
321
322 pub(crate) fn add_peer_kind(
325 &mut self,
326 peer_id: PeerId,
327 kind: Option<PeerKind>,
328 addr: PeerAddr,
329 ) {
330 self.peers_manager.add_requested_peer(peer_id, kind, addr)
331 }
332
333 pub(crate) fn add_and_connect(&mut self, peer_id: PeerId, kind: PeerKind, addr: PeerAddr) {
335 self.peers_manager.add_and_connect_kind(peer_id, kind, addr, None)
336 }
337
338 pub(crate) fn remove_peer_kind(&mut self, peer_id: PeerId, kind: PeerKind) {
340 match kind {
341 PeerKind::Basic | PeerKind::Static => self.peers_manager.remove_peer(peer_id),
342 PeerKind::Trusted => self.peers_manager.remove_peer_from_trusted_set(peer_id),
343 }
344 }
345
346 fn on_discovery_event(&mut self, event: DiscoveryEvent) {
348 match event {
349 DiscoveryEvent::NewNode(DiscoveredEvent::EventQueued { peer_id, addr, fork_id }) => {
350 self.queued_messages.push_back(StateAction::DiscoveredNode {
351 peer_id,
352 addr,
353 fork_id,
354 });
355 }
356 DiscoveryEvent::EnrForkId(record, fork_id) => {
357 let peer_id = record.id;
358 let tcp_addr = record.tcp_addr();
359 if tcp_addr.port() == 0 {
360 return
361 }
362 let udp_addr = record.udp_addr();
363 let addr = PeerAddr::new(tcp_addr, Some(udp_addr));
364 self.queued_messages.push_back(StateAction::DiscoveredEnrForkId {
365 peer_id,
366 addr,
367 fork_id,
368 });
369 }
370 }
371 }
372
373 fn on_peer_action(&mut self, action: PeerAction) {
375 match action {
376 PeerAction::Connect { peer_id, remote_addr } => {
377 self.queued_messages.push_back(StateAction::Connect { peer_id, remote_addr });
378 }
379 PeerAction::Disconnect { peer_id, reason } => {
380 self.state_fetcher.on_pending_disconnect(&peer_id);
381 self.queued_messages.push_back(StateAction::Disconnect { peer_id, reason });
382 }
383 PeerAction::DisconnectBannedIncoming { peer_id } |
384 PeerAction::DisconnectUntrustedIncoming { peer_id } => {
385 self.state_fetcher.on_pending_disconnect(&peer_id);
386 self.queued_messages.push_back(StateAction::Disconnect { peer_id, reason: None });
387 }
388 PeerAction::DiscoveryBanPeerId { peer_id, ip_addr } => {
389 self.ban_discovery(peer_id, ip_addr)
390 }
391 PeerAction::DiscoveryBanIp { ip_addr } => self.ban_ip_discovery(ip_addr),
392 PeerAction::PeerAdded(peer_id) => {
393 self.queued_messages.push_back(StateAction::PeerAdded(peer_id))
394 }
395 PeerAction::PeerRemoved(peer_id) => {
396 self.queued_messages.push_back(StateAction::PeerRemoved(peer_id))
397 }
398 PeerAction::BanPeer { .. } | PeerAction::UnBanPeer { .. } => {}
399 }
400 }
401
402 fn handle_block_request(&mut self, peer_id: PeerId, request: BlockRequest) {
407 if let Some(ref mut peer) = self.active_peers.get_mut(&peer_id) {
408 let (request, response) = match request {
409 BlockRequest::GetBlockHeaders(request) => {
410 let (response, rx) = oneshot::channel();
411 let request = PeerRequest::GetBlockHeaders { request, response };
412 let response = PeerResponse::BlockHeaders { response: rx };
413 (request, response)
414 }
415 BlockRequest::GetBlockBodies(request) => {
416 let (response, rx) = oneshot::channel();
417 let request = PeerRequest::GetBlockBodies { request, response };
418 let response = PeerResponse::BlockBodies { response: rx };
419 (request, response)
420 }
421 BlockRequest::GetBlockAccessLists(request) => {
422 let (response, rx) = oneshot::channel();
423 let request = PeerRequest::GetBlockAccessLists { request, response };
424 let response = PeerResponse::BlockAccessLists { response: rx };
425 (request, response)
426 }
427 BlockRequest::GetReceipts(request) => {
428 if peer.version >= EthVersion::Eth70 {
429 let (response, rx) = oneshot::channel();
430 let request = PeerRequest::GetReceipts70 {
431 request: GetReceipts70 {
432 first_block_receipt_index: 0,
433 block_hashes: request.0,
434 },
435 response,
436 };
437 let response = PeerResponse::Receipts70 { response: rx };
438 (request, response)
439 } else if peer.version == EthVersion::Eth69 {
440 let (response, rx) = oneshot::channel();
441 let request = PeerRequest::GetReceipts69 { request, response };
442 let response = PeerResponse::Receipts69 { response: rx };
443 (request, response)
444 } else {
445 let (response, rx) = oneshot::channel();
446 let request = PeerRequest::GetReceipts { request, response };
447 let response = PeerResponse::Receipts { response: rx };
448 (request, response)
449 }
450 }
451 BlockRequest::GetSnap(request) => {
452 let (response, rx) = oneshot::channel();
453 let request = PeerRequest::GetSnap { request: *request, response };
454 let response = PeerResponse::Snap { response: rx };
455 (request, response)
456 }
457 };
458 let _ = peer.request_tx.to_session_tx.try_send(request);
459 peer.pending_response = Some(response);
460 }
461 }
462
463 fn on_block_response_outcome(&mut self, outcome: BlockResponseOutcome) {
465 match outcome {
466 BlockResponseOutcome::Request(peer, request) => {
467 self.handle_block_request(peer, request);
468 }
469 BlockResponseOutcome::BadResponse(peer, reputation_change) => {
470 self.peers_manager.apply_reputation_change(&peer, reputation_change);
471 }
472 }
473 }
474
475 fn on_eth_response(&mut self, peer: PeerId, resp: PeerResponseResult<N>) {
481 let outcome = match resp {
482 PeerResponseResult::BlockHeaders(res) => {
483 self.state_fetcher.on_block_headers_response(peer, res)
484 }
485 PeerResponseResult::BlockBodies(res) => {
486 self.state_fetcher.on_block_bodies_response(peer, res)
487 }
488 PeerResponseResult::Receipts(res) => {
489 let normalized = res.map(|blocks| {
491 let receipts = blocks
492 .into_iter()
493 .map(|block_receipts| {
494 block_receipts.into_iter().map(|rwb| rwb.receipt).collect()
495 })
496 .collect();
497 ReceiptsResponse::new(receipts)
498 });
499 self.state_fetcher.on_receipts_response(peer, normalized)
500 }
501 PeerResponseResult::Receipts69(res) => {
502 let normalized = res.map(ReceiptsResponse::new);
503 self.state_fetcher.on_receipts_response(peer, normalized)
504 }
505 PeerResponseResult::Receipts70(res) => {
506 let normalized = res.map(ReceiptsResponse::from);
507 self.state_fetcher.on_receipts_response(peer, normalized)
508 }
509 PeerResponseResult::BlockAccessLists(res) => {
510 self.state_fetcher.on_block_access_lists_response(peer, res)
511 }
512 PeerResponseResult::Snap(res) => self.state_fetcher.on_snap_response(peer, res),
513 _ => None,
514 };
515
516 if let Some(outcome) = outcome {
517 self.on_block_response_outcome(outcome);
518 }
519 }
520
521 pub(crate) fn poll(&mut self, cx: &mut Context<'_>) -> Poll<StateAction<N>> {
523 loop {
524 if let Some(message) = self.queued_messages.pop_front() {
526 return Poll::Ready(message)
527 }
528
529 while let Poll::Ready(discovery) = self.discovery.poll(cx) {
530 self.on_discovery_event(discovery);
531 }
532
533 while let Poll::Ready(action) = self.state_fetcher.poll(cx) {
534 match action {
535 FetchAction::BlockRequest { peer_id, request } => {
536 self.handle_block_request(peer_id, request)
537 }
538 }
539 }
540
541 loop {
542 let mut closed_sessions = Vec::new();
544 let mut received_responses = Vec::new();
545
546 for (id, peer) in &mut self.active_peers {
548 let Some(mut response) = peer.pending_response.take() else { continue };
549 match response.poll(cx) {
550 Poll::Ready(res) => {
551 if res.err().is_some_and(|err| err.is_channel_closed()) {
553 debug!(
554 target: "net",
555 ?id,
556 "Request canceled, response channel from session closed."
557 );
558 closed_sessions.push(*id);
564 } else {
565 received_responses.push((*id, res));
566 }
567 }
568 Poll::Pending => {
569 peer.pending_response = Some(response);
571 }
572 };
573 }
574
575 for peer in closed_sessions {
576 self.on_session_closed(peer)
577 }
578
579 if received_responses.is_empty() {
580 break;
581 }
582
583 for (peer_id, resp) in received_responses {
584 self.on_eth_response(peer_id, resp);
585 }
586 }
587
588 while let Poll::Ready(action) = self.peers_manager.poll(cx) {
590 self.on_peer_action(action);
591 }
592
593 if self.queued_messages.is_empty() {
596 return Poll::Pending
597 }
598 }
599 }
600}
601
602#[derive(Debug)]
606pub(crate) struct ActivePeer<N: NetworkPrimitives> {
607 pub(crate) best_hash: B256,
609 pub(crate) version: EthVersion,
611 pub(crate) request_tx: PeerRequestSender<PeerRequest<N>>,
613 pub(crate) pending_response: Option<PeerResponse<N>>,
615 pub(crate) blocks: LruCache<B256>,
617}
618
619pub(crate) struct SessionActivation<N: NetworkPrimitives> {
622 pub(crate) peer: PeerId,
624 pub(crate) capabilities: Arc<Capabilities>,
626 pub(crate) version: EthVersion,
628 pub(crate) status: Arc<UnifiedStatus>,
630 pub(crate) request_tx: PeerRequestSender<PeerRequest<N>>,
632 pub(crate) timeout: Arc<AtomicU64>,
634 pub(crate) range_info: Option<BlockRangeInfo>,
636 pub(crate) supports_snap: bool,
638}
639
640#[derive(Debug)]
642pub(crate) enum StateAction<N: NetworkPrimitives> {
643 NewBlock {
645 peer_id: PeerId,
647 block: NewBlockMessage<N::NewBlockPayload>,
649 },
650 NewBlockHashes {
651 peer_id: PeerId,
653 hashes: NewBlockHashes,
655 },
656 Connect { remote_addr: SocketAddr, peer_id: PeerId },
658 Disconnect {
660 peer_id: PeerId,
661 reason: Option<DisconnectReason>,
663 },
664 DiscoveredEnrForkId {
666 peer_id: PeerId,
667 addr: PeerAddr,
669 fork_id: ForkId,
671 },
672 DiscoveredNode { peer_id: PeerId, addr: PeerAddr, fork_id: Option<ForkId> },
674 PeerAdded(PeerId),
676 PeerRemoved(PeerId),
678}
679
680#[cfg(test)]
681mod tests {
682 use crate::{
683 discovery::Discovery,
684 fetch::StateFetcher,
685 peers::PeersManager,
686 state::{BlockNumReader, NetworkState, SessionActivation},
687 PeerRequest,
688 };
689 use alloy_consensus::Header;
690 use alloy_primitives::B256;
691 use reth_eth_wire::{BlockBodies, Capabilities, Capability, EthNetworkPrimitives, EthVersion};
692 use reth_ethereum_primitives::BlockBody;
693 use reth_network_api::PeerRequestSender;
694 use reth_network_p2p::{bodies::client::BodiesClient, error::RequestError};
695 use reth_network_peers::PeerId;
696 use reth_storage_api::noop::NoopProvider;
697 use std::{
698 future::poll_fn,
699 sync::{atomic::AtomicU64, Arc},
700 };
701 use tokio::sync::mpsc;
702 use tokio_stream::{wrappers::ReceiverStream, StreamExt};
703
704 fn state() -> NetworkState<EthNetworkPrimitives> {
706 let peers = PeersManager::default();
707 let handle = peers.handle();
708 NetworkState {
709 active_peers: Default::default(),
710 peers_manager: Default::default(),
711 queued_messages: Default::default(),
712 client: BlockNumReader(Box::new(NoopProvider::default())),
713 discovery: Discovery::noop(),
714 state_fetcher: StateFetcher::new(handle, Default::default()),
715 }
716 }
717
718 fn capabilities() -> Arc<Capabilities> {
719 Arc::new(vec![Capability::from(EthVersion::Eth67)].into())
720 }
721
722 #[tokio::test(flavor = "multi_thread")]
725 async fn test_dropped_active_session() {
726 let mut state = state();
727 let client = state.fetch_client();
728
729 let peer_id = PeerId::random();
730 let (tx, session_rx) = mpsc::channel(1);
731 let peer_tx = PeerRequestSender::new(peer_id, tx);
732
733 state.on_session_activated(SessionActivation {
734 peer: peer_id,
735 capabilities: capabilities(),
736 version: EthVersion::Eth67,
737 status: Arc::default(),
738 request_tx: peer_tx,
739 timeout: Arc::new(AtomicU64::new(1)),
740 range_info: None,
741 supports_snap: false,
742 });
743
744 assert!(state.active_peers.contains_key(&peer_id));
745
746 let body = BlockBody { ommers: vec![Header::default()], ..Default::default() };
747
748 let body_response = body.clone();
749
750 tokio::task::spawn(async move {
752 let mut stream = ReceiverStream::new(session_rx);
753 let resp = stream.next().await.unwrap();
754 match resp {
755 PeerRequest::GetBlockBodies { response, .. } => {
756 response.send(Ok(BlockBodies(vec![body_response]))).unwrap();
757 }
758 _ => unreachable!(),
759 }
760
761 let _resp = stream.next().await.unwrap();
763 });
764
765 tokio::task::spawn(async move {
767 loop {
768 poll_fn(|cx| state.poll(cx)).await;
769 }
770 });
771
772 let (peer, bodies) = client.get_block_bodies(vec![B256::random()]).await.unwrap().split();
774 assert_eq!(peer, peer_id);
775 assert_eq!(bodies, vec![body]);
776
777 let resp = client.get_block_bodies(vec![B256::random()]).await;
778 assert!(resp.is_err());
779 assert_eq!(resp.unwrap_err(), RequestError::ConnectionDropped);
780 }
781}