1mod client;
4
5pub use client::FetchClient;
6
7use crate::{message::BlockRequest, session::BlockRangeInfo};
8use alloy_primitives::B256;
9use futures::StreamExt;
10use reth_eth_wire::{
11 snap::SnapProtocolMessage, BlockAccessLists, Capabilities, EthNetworkPrimitives, EthVersion,
12 GetBlockAccessLists, GetBlockBodies, GetBlockHeaders, GetReceipts, NetworkPrimitives,
13};
14use reth_network_api::test_utils::PeersHandle;
15use reth_network_p2p::{
16 block_access_lists::client::BalRequirement,
17 error::{EthResponseValidator, PeerRequestResult, RequestError, RequestResult},
18 headers::client::HeadersRequest,
19 priority::Priority,
20 receipts::client::ReceiptsResponse,
21 snap::client::SnapResponse,
22};
23use reth_network_peers::PeerId;
24use reth_network_types::ReputationChangeKind;
25use std::{
26 collections::{HashMap, VecDeque},
27 ops::RangeInclusive,
28 sync::{
29 atomic::{AtomicU64, AtomicUsize, Ordering},
30 Arc,
31 },
32 task::{Context, Poll},
33};
34use tokio::sync::{mpsc, mpsc::UnboundedSender, oneshot};
35use tokio_stream::wrappers::UnboundedReceiverStream;
36
37type InflightHeadersRequest<H> = Request<HeadersRequest, PeerRequestResult<Vec<H>>>;
38type InflightBodiesRequest<B> = Request<(), PeerRequestResult<Vec<B>>>;
39type InflightReceiptsRequest<R> = Request<(), PeerRequestResult<ReceiptsResponse<R>>>;
40type InflightBlockAccessListsRequest = Request<(), PeerRequestResult<BlockAccessLists>>;
41type InflightSnapRequest = Request<(), PeerRequestResult<SnapResponse>>;
42
43#[derive(Debug)]
50pub struct StateFetcher<N: NetworkPrimitives = EthNetworkPrimitives> {
51 inflight_headers_requests: HashMap<PeerId, InflightHeadersRequest<N::BlockHeader>>,
53 inflight_bodies_requests: HashMap<PeerId, InflightBodiesRequest<N::BlockBody>>,
55 inflight_bals_requests: HashMap<PeerId, InflightBlockAccessListsRequest>,
57 inflight_receipts_requests: HashMap<PeerId, InflightReceiptsRequest<N::Receipt>>,
59 inflight_snap_requests: HashMap<PeerId, InflightSnapRequest>,
61 peers: HashMap<PeerId, Peer>,
63 peers_handle: PeersHandle,
65 num_active_peers: Arc<AtomicUsize>,
67 queued_requests: VecDeque<DownloadRequest<N>>,
69 download_requests_rx: UnboundedReceiverStream<DownloadRequest<N>>,
71 download_requests_tx: UnboundedSender<DownloadRequest<N>>,
73}
74
75impl<N: NetworkPrimitives> StateFetcher<N> {
78 pub(crate) fn new(peers_handle: PeersHandle, num_active_peers: Arc<AtomicUsize>) -> Self {
79 let (download_requests_tx, download_requests_rx) = mpsc::unbounded_channel();
80 Self {
81 inflight_headers_requests: Default::default(),
82 inflight_bodies_requests: Default::default(),
83 inflight_bals_requests: Default::default(),
84 inflight_receipts_requests: Default::default(),
85 inflight_snap_requests: Default::default(),
86 peers: Default::default(),
87 peers_handle,
88 num_active_peers,
89 queued_requests: Default::default(),
90 download_requests_rx: UnboundedReceiverStream::new(download_requests_rx),
91 download_requests_tx,
92 }
93 }
94
95 pub(crate) fn new_active_peer(&mut self, peer: NewPeerInfo) {
97 let NewPeerInfo {
98 peer_id,
99 best_hash,
100 best_number,
101 capabilities,
102 timeout,
103 range_info,
104 supports_snap,
105 } = peer;
106 self.peers.insert(
107 peer_id,
108 Peer {
109 state: PeerState::Idle,
110 best_hash,
111 best_number,
112 capabilities,
113 timeout,
114 last_response_likely_bad: false,
115 range_info,
116 supports_snap,
117 },
118 );
119 }
120
121 pub(crate) fn on_session_closed(&mut self, peer: &PeerId) {
128 self.peers.remove(peer);
129 if let Some(req) = self.inflight_headers_requests.remove(peer) {
130 let _ = req.response.send(Err(RequestError::ConnectionDropped));
131 }
132 if let Some(req) = self.inflight_bodies_requests.remove(peer) {
133 let _ = req.response.send(Err(RequestError::ConnectionDropped));
134 }
135 if let Some(req) = self.inflight_bals_requests.remove(peer) {
136 let _ = req.response.send(Err(RequestError::ConnectionDropped));
137 }
138 if let Some(req) = self.inflight_receipts_requests.remove(peer) {
139 let _ = req.response.send(Err(RequestError::ConnectionDropped));
140 }
141 if let Some(req) = self.inflight_snap_requests.remove(peer) {
142 let _ = req.response.send(Err(RequestError::ConnectionDropped));
143 }
144 }
145
146 pub(crate) fn update_peer_block(&mut self, peer_id: &PeerId, hash: B256, number: u64) -> bool {
150 if let Some(peer) = self.peers.get_mut(peer_id) &&
151 number > peer.best_number
152 {
153 peer.best_hash = hash;
154 peer.best_number = number;
155 return true
156 }
157 false
158 }
159
160 pub(crate) fn on_pending_disconnect(&mut self, peer_id: &PeerId) {
162 if let Some(peer) = self.peers.get_mut(peer_id) {
163 peer.state = PeerState::Closing;
164 }
165 }
166
167 fn next_best_peer(&self, requirement: BestPeerRequirements) -> Option<PeerId> {
172 let mut idle = self
174 .peers
175 .iter()
176 .filter(|(_, peer)| peer.state.is_idle() && peer.satisfies(&requirement));
177
178 let mut best_peer = idle.next()?;
179
180 for maybe_better in idle {
181 if best_peer.1.last_response_likely_bad && !maybe_better.1.last_response_likely_bad {
183 best_peer = maybe_better;
184 continue
185 }
186
187 if maybe_better.1.last_response_likely_bad && !best_peer.1.last_response_likely_bad {
189 continue
190 }
191
192 if maybe_better.1.is_better(best_peer.1, &requirement) {
194 best_peer = maybe_better;
195 continue
196 }
197
198 if maybe_better.1.timeout() < best_peer.1.timeout() &&
200 !maybe_better.1.last_response_likely_bad
201 {
202 best_peer = maybe_better;
203 }
204 }
205
206 Some(*best_peer.0)
207 }
208
209 fn has_eth71_peer(&self) -> bool {
211 self.peers.values().any(|peer| {
212 !matches!(peer.state, PeerState::Closing) &&
213 peer.capabilities.supports_eth_at_least(&EthVersion::Eth71)
214 })
215 }
216
217 fn poll_action(&mut self) -> PollAction {
219 if self.queued_requests.is_empty() {
221 return PollAction::NoRequests
222 }
223
224 let request = self.queued_requests.pop_front().expect("not empty");
225 let Some(peer_id) = self.next_best_peer(request.best_peer_requirements()) else {
226 if self.should_fail_fast(&request) {
229 request.send_err_response(RequestError::UnsupportedCapability);
230 } else {
231 self.queued_requests.push_back(request);
234 }
235 return PollAction::NoPeersAvailable
236 };
237
238 let request = self.prepare_block_request(peer_id, request);
239
240 PollAction::Ready(FetchAction::BlockRequest { peer_id, request })
241 }
242
243 pub(crate) fn poll(&mut self, cx: &mut Context<'_>) -> Poll<FetchAction> {
245 loop {
247 let no_peers_available = match self.poll_action() {
248 PollAction::Ready(action) => return Poll::Ready(action),
249 PollAction::NoRequests => false,
250 PollAction::NoPeersAvailable => true,
251 };
252
253 loop {
254 match self.download_requests_rx.poll_next_unpin(cx) {
256 Poll::Ready(Some(request)) => {
257 if self.should_fail_fast(&request) {
260 request.send_err_response(RequestError::UnsupportedCapability);
261 continue
262 }
263
264 match request.get_priority() {
265 Priority::High => {
266 let pos = self
269 .queued_requests
270 .iter()
271 .position(|req| req.is_normal_priority())
272 .unwrap_or(0);
273 self.queued_requests.insert(pos, request);
274 }
275 Priority::Normal => {
276 self.queued_requests.push_back(request);
277 }
278 }
279 }
280 Poll::Ready(None) => {
281 unreachable!("channel can't close")
282 }
283 Poll::Pending => break,
284 }
285 }
286
287 if self.queued_requests.is_empty() || no_peers_available {
288 return Poll::Pending
289 }
290 }
291 }
292
293 fn has_snap_peer(&self) -> bool {
295 self.peers
296 .values()
297 .any(|peer| !matches!(peer.state, PeerState::Closing) && peer.supports_snap)
298 }
299
300 fn should_fail_fast(&self, request: &DownloadRequest<N>) -> bool {
303 (request.is_optional_bal() && !self.has_eth71_peer()) ||
304 (request.is_snap() && !self.has_snap_peer())
305 }
306
307 fn prepare_block_request(&mut self, peer_id: PeerId, req: DownloadRequest<N>) -> BlockRequest {
311 if let Some(peer) = self.peers.get_mut(&peer_id) {
313 peer.state = req.peer_state();
314 }
315
316 self.prepare_inflight_block_request(peer_id, req)
317 }
318
319 fn prepare_inflight_block_request(
321 &mut self,
322 peer_id: PeerId,
323 req: DownloadRequest<N>,
324 ) -> BlockRequest {
325 match req {
326 DownloadRequest::GetBlockHeaders { request, response, .. } => {
327 let inflight = Request { request: request.clone(), response };
328 self.inflight_headers_requests.insert(peer_id, inflight);
329 let HeadersRequest { start, limit, direction } = request;
330 BlockRequest::GetBlockHeaders(GetBlockHeaders {
331 start_block: start,
332 limit,
333 skip: 0,
334 direction,
335 })
336 }
337 DownloadRequest::GetBlockBodies { request, response, .. } => {
338 let inflight = Request { request: (), response };
339 self.inflight_bodies_requests.insert(peer_id, inflight);
340 BlockRequest::GetBlockBodies(GetBlockBodies(request))
341 }
342 DownloadRequest::GetBlockAccessLists { request, response, .. } => {
343 let inflight = Request { request: (), response };
344 self.inflight_bals_requests.insert(peer_id, inflight);
345 BlockRequest::GetBlockAccessLists(GetBlockAccessLists(request))
346 }
347 DownloadRequest::GetReceipts { request, response, .. } => {
348 let inflight = Request { request: (), response };
349 self.inflight_receipts_requests.insert(peer_id, inflight);
350 BlockRequest::GetReceipts(GetReceipts(request))
351 }
352 DownloadRequest::GetSnap { request, response, .. } => {
353 let inflight = Request { request: (), response };
354 self.inflight_snap_requests.insert(peer_id, inflight);
355 BlockRequest::GetSnap(Box::new(request))
356 }
357 }
358 }
359
360 fn followup_request(&mut self, peer_id: PeerId) -> Option<BlockResponseOutcome> {
368 let peer = self.peers.get_mut(&peer_id)?;
369 let req_idx = self.queued_requests.iter().position(|req| {
370 peer.satisfies(&req.best_peer_requirements())
372 })?;
373 let req = self.queued_requests.remove(req_idx).expect("valid request index");
374
375 peer.state = req.peer_state();
376 let req = self.prepare_inflight_block_request(peer_id, req);
377 Some(BlockResponseOutcome::Request(peer_id, req))
378 }
379
380 pub(crate) fn on_block_headers_response(
386 &mut self,
387 peer_id: PeerId,
388 res: RequestResult<Vec<N::BlockHeader>>,
389 ) -> Option<BlockResponseOutcome> {
390 let is_error = res.is_err();
391 let maybe_reputation_change = res.reputation_change_err();
392
393 let resp = self.inflight_headers_requests.remove(&peer_id);
394
395 let is_likely_bad_response =
396 resp.as_ref().is_some_and(|r| res.is_likely_bad_headers_response(&r.request));
397
398 if let Some(resp) = resp {
399 let _ = resp.response.send(res.map(|h| (peer_id, h).into()));
401 }
402
403 if let Some(peer) = self.peers.get_mut(&peer_id) {
404 peer.last_response_likely_bad = is_likely_bad_response;
406
407 if peer.state.on_request_finished() && !is_error && !is_likely_bad_response {
410 return self.followup_request(peer_id)
411 }
412 }
413
414 maybe_reputation_change
417 .map(|reputation_change| BlockResponseOutcome::BadResponse(peer_id, reputation_change))
418 }
419
420 pub(crate) fn on_block_bodies_response(
422 &mut self,
423 peer_id: PeerId,
424 res: RequestResult<Vec<N::BlockBody>>,
425 ) -> Option<BlockResponseOutcome> {
426 let is_likely_bad_response = res.as_ref().map_or(true, |bodies| bodies.is_empty());
427
428 if let Some(resp) = self.inflight_bodies_requests.remove(&peer_id) {
429 let _ = resp.response.send(res.map(|b| (peer_id, b).into()));
430 }
431 if let Some(peer) = self.peers.get_mut(&peer_id) {
432 peer.last_response_likely_bad = is_likely_bad_response;
434
435 if peer.state.on_request_finished() && !is_likely_bad_response {
436 return self.followup_request(peer_id)
437 }
438 }
439 None
440 }
441
442 pub(crate) fn on_block_access_lists_response(
444 &mut self,
445 peer_id: PeerId,
446 res: RequestResult<BlockAccessLists>,
447 ) -> Option<BlockResponseOutcome> {
448 let is_likely_bad_response = res.is_err();
449
450 if let Some(resp) = self.inflight_bals_requests.remove(&peer_id) {
451 let _ = resp.response.send(res.map(|b| (peer_id, b).into()));
452 }
453 if let Some(peer) = self.peers.get_mut(&peer_id) {
454 peer.last_response_likely_bad = is_likely_bad_response;
455
456 if peer.state.on_request_finished() && !is_likely_bad_response {
457 return self.followup_request(peer_id)
458 }
459 }
460 None
461 }
462
463 pub(crate) fn on_receipts_response(
468 &mut self,
469 peer_id: PeerId,
470 res: RequestResult<ReceiptsResponse<N::Receipt>>,
471 ) -> Option<BlockResponseOutcome> {
472 let is_likely_bad_response = res.as_ref().map_or(true, |resp| resp.receipts.is_empty());
473
474 if let Some(resp) = self.inflight_receipts_requests.remove(&peer_id) {
475 let _ = resp.response.send(res.map(|r| (peer_id, r).into()));
476 }
477 if let Some(peer) = self.peers.get_mut(&peer_id) {
478 peer.last_response_likely_bad = is_likely_bad_response;
479
480 if peer.state.on_request_finished() && !is_likely_bad_response {
481 return self.followup_request(peer_id)
482 }
483 }
484 None
485 }
486
487 pub(crate) fn on_snap_response(
489 &mut self,
490 peer_id: PeerId,
491 res: RequestResult<SnapResponse>,
492 ) -> Option<BlockResponseOutcome> {
493 let is_likely_bad_response = match &res {
496 Ok(SnapResponse::AccountRange(response)) => {
497 response.accounts.is_empty() && response.proof.is_empty()
498 }
499 Ok(SnapResponse::StorageRanges(response)) => {
500 response.slots.is_empty() && response.proof.is_empty()
501 }
502 Ok(SnapResponse::ByteCodes(response)) => response.codes.is_empty(),
503 Ok(SnapResponse::BlockAccessLists(response)) => {
504 response.block_access_lists.0.is_empty() ||
505 response.block_access_lists.0.iter().any(Option::is_none)
506 }
507 Err(_) => true,
508 };
509
510 if let Some(resp) = self.inflight_snap_requests.remove(&peer_id) {
511 let _ = resp.response.send(res.map(|r| (peer_id, r).into()));
512 }
513 if let Some(peer) = self.peers.get_mut(&peer_id) {
514 peer.last_response_likely_bad = is_likely_bad_response;
515
516 if peer.state.on_request_finished() && !is_likely_bad_response {
517 return self.followup_request(peer_id)
518 }
519 }
520 None
521 }
522
523 pub(crate) fn client(&self) -> FetchClient<N> {
525 FetchClient {
526 request_tx: self.download_requests_tx.clone(),
527 peers_handle: self.peers_handle.clone(),
528 num_active_peers: Arc::clone(&self.num_active_peers),
529 }
530 }
531}
532
533enum PollAction {
535 Ready(FetchAction),
536 NoRequests,
537 NoPeersAvailable,
538}
539
540#[derive(Debug)]
542pub(crate) struct NewPeerInfo {
543 pub(crate) peer_id: PeerId,
545 pub(crate) best_hash: B256,
547 pub(crate) best_number: u64,
549 pub(crate) capabilities: Arc<Capabilities>,
551 pub(crate) timeout: Arc<AtomicU64>,
553 pub(crate) range_info: Option<BlockRangeInfo>,
555 pub(crate) supports_snap: bool,
557}
558
559#[derive(Debug)]
561struct Peer {
562 state: PeerState,
564 best_hash: B256,
566 best_number: u64,
568 #[allow(dead_code)]
570 capabilities: Arc<Capabilities>,
571 timeout: Arc<AtomicU64>,
573 last_response_likely_bad: bool,
580 range_info: Option<BlockRangeInfo>,
582 supports_snap: bool,
584}
585
586impl Peer {
587 fn timeout(&self) -> u64 {
588 self.timeout.load(Ordering::Relaxed)
589 }
590
591 fn earliest(&self) -> u64 {
593 self.range_info.as_ref().map_or(0, |info| info.earliest())
594 }
595
596 fn has_full_history(&self) -> bool {
598 self.earliest() == 0
599 }
600
601 fn range(&self) -> Option<RangeInclusive<u64>> {
602 self.range_info.as_ref().map(|info| info.range())
603 }
604
605 fn satisfies(&self, requirement: &BestPeerRequirements) -> bool {
607 match requirement {
608 BestPeerRequirements::EthVersion(ver) => self.capabilities.supports_eth_at_least(ver),
609 BestPeerRequirements::SupportsSnap => self.supports_snap,
610 BestPeerRequirements::None |
611 BestPeerRequirements::FullBlock |
612 BestPeerRequirements::FullBlockRange(_) => true,
613 }
614 }
615
616 fn has_better_range(&self, other: &Self, range: &RangeInclusive<u64>) -> bool {
625 let self_range = self.range();
626 let other_range = other.range();
627
628 match (self_range, other_range) {
629 (Some(self_r), Some(other_r)) => {
630 let self_covers = self_r.contains(range.start()) && self_r.contains(range.end());
632 let other_covers = other_r.contains(range.start()) && other_r.contains(range.end());
633
634 #[expect(clippy::match_same_arms)]
635 match (self_covers, other_covers) {
636 (true, false) => true, (false, true) => false, (true, true) => false, (false, false) => {
640 self_r.start() < other_r.start()
642 }
643 }
644 }
645 (Some(self_r), None) => {
646 self_r.contains(range.start()) && self_r.contains(range.end())
649 }
650 (None, Some(other_r)) => {
651 !(other_r.contains(range.start()) && other_r.contains(range.end()))
654 }
655 (None, None) => false, }
657 }
658
659 fn is_better(&self, other: &Self, requirement: &BestPeerRequirements) -> bool {
661 match requirement {
662 BestPeerRequirements::FullBlockRange(range) => self.has_better_range(other, range),
663 BestPeerRequirements::FullBlock => self.has_full_history() && !other.has_full_history(),
664 BestPeerRequirements::None |
667 BestPeerRequirements::EthVersion(_) |
668 BestPeerRequirements::SupportsSnap => false,
669 }
670 }
671}
672
673#[derive(Debug)]
675enum PeerState {
676 Idle,
678 GetBlockHeaders,
680 GetBlockBodies,
682 GetBlockAccessLists,
684 GetReceipts,
686 GetSnap,
688 Closing,
690}
691
692impl PeerState {
695 const fn is_idle(&self) -> bool {
697 matches!(self, Self::Idle)
698 }
699
700 const fn on_request_finished(&mut self) -> bool {
706 if !matches!(self, Self::Closing) {
707 *self = Self::Idle;
708 return true
709 }
710 false
711 }
712}
713
714#[derive(Debug)]
717struct Request<Req, Resp> {
718 request: Req,
721 response: oneshot::Sender<Resp>,
722}
723
724#[derive(Debug)]
726#[expect(clippy::enum_variant_names)]
727pub(crate) enum DownloadRequest<N: NetworkPrimitives> {
728 GetBlockHeaders {
730 request: HeadersRequest,
731 response: oneshot::Sender<PeerRequestResult<Vec<N::BlockHeader>>>,
732 priority: Priority,
733 },
734 GetBlockBodies {
736 request: Vec<B256>,
737 response: oneshot::Sender<PeerRequestResult<Vec<N::BlockBody>>>,
738 priority: Priority,
739 range_hint: Option<RangeInclusive<u64>>,
740 },
741 GetBlockAccessLists {
743 request: Vec<B256>,
744 response: oneshot::Sender<PeerRequestResult<BlockAccessLists>>,
745 priority: Priority,
746 requirement: BalRequirement,
747 },
748 GetReceipts {
750 request: Vec<B256>,
751 response: oneshot::Sender<PeerRequestResult<ReceiptsResponse<N::Receipt>>>,
752 priority: Priority,
753 },
754 GetSnap {
756 request: SnapProtocolMessage,
757 response: oneshot::Sender<PeerRequestResult<SnapResponse>>,
758 priority: Priority,
759 },
760}
761
762impl<N: NetworkPrimitives> DownloadRequest<N> {
765 const fn peer_state(&self) -> PeerState {
767 match self {
768 Self::GetBlockHeaders { .. } => PeerState::GetBlockHeaders,
769 Self::GetBlockBodies { .. } => PeerState::GetBlockBodies,
770 Self::GetBlockAccessLists { .. } => PeerState::GetBlockAccessLists,
771 Self::GetReceipts { .. } => PeerState::GetReceipts,
772 Self::GetSnap { .. } => PeerState::GetSnap,
773 }
774 }
775
776 const fn get_priority(&self) -> &Priority {
778 match self {
779 Self::GetBlockHeaders { priority, .. } |
780 Self::GetBlockBodies { priority, .. } |
781 Self::GetBlockAccessLists { priority, .. } |
782 Self::GetReceipts { priority, .. } |
783 Self::GetSnap { priority, .. } => priority,
784 }
785 }
786
787 const fn is_normal_priority(&self) -> bool {
789 self.get_priority().is_normal()
790 }
791
792 const fn is_optional_bal(&self) -> bool {
794 matches!(self, Self::GetBlockAccessLists { requirement: BalRequirement::Optional, .. })
795 }
796
797 const fn is_snap(&self) -> bool {
799 matches!(self, Self::GetSnap { .. })
800 }
801
802 fn send_err_response(self, err: RequestError) {
804 let _ = match self {
805 Self::GetBlockHeaders { response, .. } => response.send(Err(err)).ok(),
806 Self::GetBlockBodies { response, .. } => response.send(Err(err)).ok(),
807 Self::GetBlockAccessLists { response, .. } => response.send(Err(err)).ok(),
808 Self::GetReceipts { response, .. } => response.send(Err(err)).ok(),
809 Self::GetSnap { response, .. } => response.send(Err(err)).ok(),
810 };
811 }
812
813 fn best_peer_requirements(&self) -> BestPeerRequirements {
815 match self {
816 Self::GetBlockHeaders { .. } => BestPeerRequirements::None,
817 Self::GetBlockAccessLists { .. } => BestPeerRequirements::EthVersion(EthVersion::Eth71),
818 Self::GetBlockBodies { range_hint, .. } => {
819 if let Some(range) = range_hint {
820 BestPeerRequirements::FullBlockRange(range.clone())
821 } else {
822 BestPeerRequirements::FullBlock
823 }
824 }
825 Self::GetReceipts { .. } => BestPeerRequirements::FullBlock,
826 Self::GetSnap { .. } => BestPeerRequirements::SupportsSnap,
827 }
828 }
829}
830
831pub(crate) enum FetchAction {
833 BlockRequest {
835 peer_id: PeerId,
837 request: BlockRequest,
839 },
840}
841
842#[derive(Debug, PartialEq, Eq)]
846pub(crate) enum BlockResponseOutcome {
847 Request(PeerId, BlockRequest),
849 BadResponse(PeerId, ReputationChangeKind),
851}
852
853enum BestPeerRequirements {
855 None,
857 FullBlockRange(RangeInclusive<u64>),
859 FullBlock,
861 EthVersion(EthVersion),
863 SupportsSnap,
865}
866
867#[cfg(test)]
868mod tests {
869 use super::*;
870 use crate::{peers::PeersManager, PeersConfig};
871 use alloy_consensus::Header;
872 use alloy_primitives::{Bytes, B512};
873 use futures::task::noop_waker;
874 use reth_eth_wire::Capability;
875 use reth_eth_wire_types::snap::{
876 AccountRangeMessage, BlockAccessListsMessage, ByteCodesMessage, GetAccountRangeMessage,
877 StorageRangesMessage,
878 };
879 use std::{
880 future::poll_fn,
881 task::{Context, Poll},
882 };
883 use test_case::test_case;
884
885 #[tokio::test(flavor = "multi_thread")]
886 async fn test_poll_fetcher() {
887 let manager = PeersManager::new(PeersConfig::default());
888 let mut fetcher =
889 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
890
891 poll_fn(move |cx| {
892 assert!(fetcher.poll(cx).is_pending());
893 let (tx, _rx) = oneshot::channel();
894 fetcher.queued_requests.push_back(DownloadRequest::GetBlockBodies {
895 request: vec![],
896 response: tx,
897 priority: Priority::default(),
898 range_hint: None,
899 });
900 assert!(fetcher.poll(cx).is_pending());
901
902 Poll::Ready(())
903 })
904 .await;
905 }
906
907 #[tokio::test]
908 async fn test_peer_rotation() {
909 let manager = PeersManager::new(PeersConfig::default());
910 let mut fetcher =
911 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
912 let peer1 = B512::random();
914 let peer2 = B512::random();
915 let capabilities = Arc::new(Capabilities::from(vec![]));
916 fetcher.new_active_peer(NewPeerInfo {
917 peer_id: peer1,
918 best_hash: B256::random(),
919 best_number: 1,
920 capabilities: Arc::clone(&capabilities),
921 timeout: Arc::new(AtomicU64::new(1)),
922 range_info: None,
923 supports_snap: false,
924 });
925 fetcher.new_active_peer(NewPeerInfo {
926 peer_id: peer2,
927 best_hash: B256::random(),
928 best_number: 2,
929 capabilities: Arc::clone(&capabilities),
930 timeout: Arc::new(AtomicU64::new(1)),
931 range_info: None,
932 supports_snap: false,
933 });
934
935 let first_peer = fetcher.next_best_peer(BestPeerRequirements::None).unwrap();
936 assert!(first_peer == peer1 || first_peer == peer2);
937 fetcher.on_pending_disconnect(&first_peer);
939 let second_peer = fetcher.next_best_peer(BestPeerRequirements::None).unwrap();
941 assert!(first_peer == peer1 || first_peer == peer2);
942 assert_ne!(first_peer, second_peer);
943 fetcher.on_pending_disconnect(&second_peer);
945 assert_eq!(fetcher.next_best_peer(BestPeerRequirements::None), None);
946 }
947
948 #[tokio::test]
949 async fn test_peer_prioritization() {
950 let manager = PeersManager::new(PeersConfig::default());
951 let mut fetcher =
952 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
953 let peer1 = B512::random();
955 let peer2 = B512::random();
956 let peer3 = B512::random();
957
958 let peer2_timeout = Arc::new(AtomicU64::new(300));
959
960 let capabilities = Arc::new(Capabilities::from(vec![]));
961 fetcher.new_active_peer(NewPeerInfo {
962 peer_id: peer1,
963 best_hash: B256::random(),
964 best_number: 1,
965 capabilities: Arc::clone(&capabilities),
966 timeout: Arc::new(AtomicU64::new(30)),
967 range_info: None,
968 supports_snap: false,
969 });
970 fetcher.new_active_peer(NewPeerInfo {
971 peer_id: peer2,
972 best_hash: B256::random(),
973 best_number: 2,
974 capabilities: Arc::clone(&capabilities),
975 timeout: Arc::clone(&peer2_timeout),
976 range_info: None,
977 supports_snap: false,
978 });
979 fetcher.new_active_peer(NewPeerInfo {
980 peer_id: peer3,
981 best_hash: B256::random(),
982 best_number: 3,
983 capabilities: Arc::clone(&capabilities),
984 timeout: Arc::new(AtomicU64::new(50)),
985 range_info: None,
986 supports_snap: false,
987 });
988
989 assert_eq!(fetcher.next_best_peer(BestPeerRequirements::None), Some(peer1));
991 assert_eq!(fetcher.next_best_peer(BestPeerRequirements::None), Some(peer1));
992 peer2_timeout.store(10, Ordering::Relaxed);
994 assert_eq!(fetcher.next_best_peer(BestPeerRequirements::None), Some(peer2));
996 assert_eq!(fetcher.next_best_peer(BestPeerRequirements::None), Some(peer2));
997 }
998
999 #[tokio::test]
1000 async fn test_on_block_headers_response() {
1001 let manager = PeersManager::new(PeersConfig::default());
1002 let mut fetcher =
1003 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1004 let peer_id = B512::random();
1005
1006 assert_eq!(fetcher.on_block_headers_response(peer_id, Ok(vec![Header::default()])), None);
1007
1008 assert_eq!(
1009 fetcher.on_block_headers_response(peer_id, Err(RequestError::Timeout)),
1010 Some(BlockResponseOutcome::BadResponse(peer_id, ReputationChangeKind::Timeout))
1011 );
1012 assert_eq!(
1013 fetcher.on_block_headers_response(peer_id, Err(RequestError::BadResponse)),
1014 None
1015 );
1016 assert_eq!(
1017 fetcher.on_block_headers_response(peer_id, Err(RequestError::ChannelClosed)),
1018 None
1019 );
1020 assert_eq!(
1021 fetcher.on_block_headers_response(peer_id, Err(RequestError::ConnectionDropped)),
1022 None
1023 );
1024 assert_eq!(
1025 fetcher.on_block_headers_response(peer_id, Err(RequestError::UnsupportedCapability)),
1026 None
1027 );
1028 }
1029
1030 #[tokio::test]
1031 async fn test_header_response_outcome() {
1032 let manager = PeersManager::new(PeersConfig::default());
1033 let mut fetcher =
1034 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1035 let peer_id = B512::random();
1036
1037 let request_pair = || {
1038 let (tx, _rx) = oneshot::channel();
1039 let req = Request {
1040 request: HeadersRequest {
1041 start: 0u64.into(),
1042 limit: 1,
1043 direction: Default::default(),
1044 },
1045 response: tx,
1046 };
1047 let header = Header { number: 0, ..Default::default() };
1048 (req, header)
1049 };
1050
1051 fetcher.new_active_peer(NewPeerInfo {
1052 peer_id,
1053 best_hash: Default::default(),
1054 best_number: Default::default(),
1055 capabilities: Arc::new(Capabilities::from(vec![])),
1056 timeout: Default::default(),
1057 range_info: None,
1058 supports_snap: false,
1059 });
1060
1061 let (req, header) = request_pair();
1062 fetcher.inflight_headers_requests.insert(peer_id, req);
1063
1064 let outcome = fetcher.on_block_headers_response(peer_id, Ok(vec![header]));
1065 assert!(outcome.is_none());
1066 assert!(fetcher.peers[&peer_id].state.is_idle());
1067
1068 let outcome =
1069 fetcher.on_block_headers_response(peer_id, Err(RequestError::Timeout)).unwrap();
1070
1071 assert!(EthResponseValidator::reputation_change_err(&Err::<Vec<Header>, _>(
1072 RequestError::Timeout
1073 ))
1074 .is_some());
1075
1076 match outcome {
1077 BlockResponseOutcome::BadResponse(peer, _) => {
1078 assert_eq!(peer, peer_id)
1079 }
1080 BlockResponseOutcome::Request(_, _) => {
1081 unreachable!()
1082 }
1083 };
1084
1085 assert!(fetcher.peers[&peer_id].state.is_idle());
1086 }
1087
1088 #[tokio::test]
1089 async fn test_partial_header_response_triggers_followup() {
1090 let (mut fetcher, peer_id) = fetcher_with_peer();
1091
1092 let (followup_tx, _followup_rx) = oneshot::channel();
1093 fetcher.queued_requests.push_back(DownloadRequest::GetBlockHeaders {
1094 request: HeadersRequest::falling(1u64.into(), 1),
1095 response: followup_tx,
1096 priority: Priority::High,
1097 });
1098
1099 let (response_tx, mut response_rx) = oneshot::channel();
1100 fetcher.inflight_headers_requests.insert(
1101 peer_id,
1102 Request { request: HeadersRequest::falling(2u64.into(), 2), response: response_tx },
1103 );
1104 fetcher.peers.get_mut(&peer_id).unwrap().state = PeerState::GetBlockHeaders;
1105
1106 let header = Header { number: 2, ..Default::default() };
1107 let outcome = fetcher.on_block_headers_response(peer_id, Ok(vec![header]));
1108
1109 let Some(BlockResponseOutcome::Request(
1110 dispatched_peer,
1111 BlockRequest::GetBlockHeaders(request),
1112 )) = outcome
1113 else {
1114 panic!("expected a header followup request")
1115 };
1116 assert_eq!(dispatched_peer, peer_id);
1117 assert_eq!(request.start_block, 1u64.into());
1118 assert_eq!(request.limit, 1);
1119 assert!(!fetcher.peers[&peer_id].last_response_likely_bad);
1120 assert!(response_rx.try_recv().is_ok());
1121 }
1122
1123 #[test]
1124 fn test_peer_is_better_none_requirement() {
1125 let peer1 = Peer {
1126 state: PeerState::Idle,
1127 best_hash: B256::random(),
1128 best_number: 100,
1129 capabilities: Arc::new(Capabilities::new(vec![])),
1130 timeout: Arc::new(AtomicU64::new(10)),
1131 last_response_likely_bad: false,
1132 range_info: Some(BlockRangeInfo::new(0, 100, B256::random())),
1133 supports_snap: false,
1134 };
1135
1136 let peer2 = Peer {
1137 state: PeerState::Idle,
1138 best_hash: B256::random(),
1139 best_number: 50,
1140 capabilities: Arc::new(Capabilities::new(vec![])),
1141 timeout: Arc::new(AtomicU64::new(20)),
1142 last_response_likely_bad: false,
1143 range_info: None,
1144 supports_snap: false,
1145 };
1146
1147 assert!(!peer1.is_better(&peer2, &BestPeerRequirements::None));
1149 assert!(!peer2.is_better(&peer1, &BestPeerRequirements::None));
1150 }
1151
1152 #[test]
1153 fn test_peer_is_better_full_block_requirement() {
1154 let peer_full = Peer {
1156 state: PeerState::Idle,
1157 best_hash: B256::random(),
1158 best_number: 100,
1159 capabilities: Arc::new(Capabilities::new(vec![])),
1160 timeout: Arc::new(AtomicU64::new(10)),
1161 last_response_likely_bad: false,
1162 range_info: Some(BlockRangeInfo::new(0, 100, B256::random())),
1163 supports_snap: false,
1164 };
1165
1166 let peer_partial = Peer {
1168 state: PeerState::Idle,
1169 best_hash: B256::random(),
1170 best_number: 100,
1171 capabilities: Arc::new(Capabilities::new(vec![])),
1172 timeout: Arc::new(AtomicU64::new(10)),
1173 last_response_likely_bad: false,
1174 range_info: Some(BlockRangeInfo::new(50, 100, B256::random())),
1175 supports_snap: false,
1176 };
1177
1178 let peer_no_range = Peer {
1180 state: PeerState::Idle,
1181 best_hash: B256::random(),
1182 best_number: 100,
1183 capabilities: Arc::new(Capabilities::new(vec![])),
1184 timeout: Arc::new(AtomicU64::new(10)),
1185 last_response_likely_bad: false,
1186 range_info: None,
1187 supports_snap: false,
1188 };
1189
1190 assert!(peer_full.is_better(&peer_partial, &BestPeerRequirements::FullBlock));
1192 assert!(!peer_partial.is_better(&peer_full, &BestPeerRequirements::FullBlock));
1193
1194 assert!(peer_no_range.is_better(&peer_partial, &BestPeerRequirements::FullBlock));
1196 assert!(!peer_partial.is_better(&peer_no_range, &BestPeerRequirements::FullBlock));
1197
1198 assert!(!peer_full.is_better(&peer_no_range, &BestPeerRequirements::FullBlock));
1200 assert!(!peer_no_range.is_better(&peer_full, &BestPeerRequirements::FullBlock));
1201 }
1202
1203 #[test]
1204 fn test_peer_is_better_full_block_range_requirement() {
1205 let range = RangeInclusive::new(40, 60);
1206
1207 let peer_covers = Peer {
1209 state: PeerState::Idle,
1210 best_hash: B256::random(),
1211 best_number: 100,
1212 capabilities: Arc::new(Capabilities::new(vec![])),
1213 timeout: Arc::new(AtomicU64::new(10)),
1214 last_response_likely_bad: false,
1215 range_info: Some(BlockRangeInfo::new(0, 100, B256::random())),
1216 supports_snap: false,
1217 };
1218
1219 let peer_no_cover = Peer {
1221 state: PeerState::Idle,
1222 best_hash: B256::random(),
1223 best_number: 100,
1224 capabilities: Arc::new(Capabilities::new(vec![])),
1225 timeout: Arc::new(AtomicU64::new(10)),
1226 last_response_likely_bad: false,
1227 range_info: Some(BlockRangeInfo::new(70, 100, B256::random())),
1228 supports_snap: false,
1229 };
1230
1231 assert!(peer_covers
1233 .is_better(&peer_no_cover, &BestPeerRequirements::FullBlockRange(range.clone())));
1234 assert!(
1235 !peer_no_cover.is_better(&peer_covers, &BestPeerRequirements::FullBlockRange(range))
1236 );
1237 }
1238
1239 #[test]
1240 fn test_peer_is_better_both_cover_range() {
1241 let range = RangeInclusive::new(30, 50);
1242
1243 let peer_full = Peer {
1245 state: PeerState::Idle,
1246 best_hash: B256::random(),
1247 best_number: 100,
1248 capabilities: Arc::new(Capabilities::new(vec![])),
1249 timeout: Arc::new(AtomicU64::new(10)),
1250 last_response_likely_bad: false,
1251 range_info: Some(BlockRangeInfo::new(0, 50, B256::random())),
1252 supports_snap: false,
1253 };
1254
1255 let peer_partial = Peer {
1257 state: PeerState::Idle,
1258 best_hash: B256::random(),
1259 best_number: 100,
1260 capabilities: Arc::new(Capabilities::new(vec![])),
1261 timeout: Arc::new(AtomicU64::new(10)),
1262 last_response_likely_bad: false,
1263 range_info: Some(BlockRangeInfo::new(30, 50, B256::random())),
1264 supports_snap: false,
1265 };
1266
1267 assert!(!peer_full
1269 .is_better(&peer_partial, &BestPeerRequirements::FullBlockRange(range.clone())));
1270 assert!(!peer_partial.is_better(&peer_full, &BestPeerRequirements::FullBlockRange(range)));
1271 }
1272
1273 #[test]
1274 fn test_peer_is_better_lower_start() {
1275 let range = RangeInclusive::new(30, 60);
1276
1277 let peer_full = Peer {
1279 state: PeerState::Idle,
1280 best_hash: B256::random(),
1281 best_number: 100,
1282 capabilities: Arc::new(Capabilities::new(vec![])),
1283 timeout: Arc::new(AtomicU64::new(10)),
1284 last_response_likely_bad: false,
1285 range_info: Some(BlockRangeInfo::new(0, 50, B256::random())),
1286 supports_snap: false,
1287 };
1288
1289 let peer_partial = Peer {
1291 state: PeerState::Idle,
1292 best_hash: B256::random(),
1293 best_number: 100,
1294 capabilities: Arc::new(Capabilities::new(vec![])),
1295 timeout: Arc::new(AtomicU64::new(10)),
1296 last_response_likely_bad: false,
1297 range_info: Some(BlockRangeInfo::new(30, 50, B256::random())),
1298 supports_snap: false,
1299 };
1300
1301 assert!(peer_full
1303 .is_better(&peer_partial, &BestPeerRequirements::FullBlockRange(range.clone())));
1304 assert!(!peer_partial.is_better(&peer_full, &BestPeerRequirements::FullBlockRange(range)));
1305 }
1306
1307 #[test]
1308 fn test_peer_is_better_neither_covers_range() {
1309 let range = RangeInclusive::new(40, 60);
1310
1311 let peer_full = Peer {
1313 state: PeerState::Idle,
1314 best_hash: B256::random(),
1315 best_number: 30,
1316 capabilities: Arc::new(Capabilities::new(vec![])),
1317 timeout: Arc::new(AtomicU64::new(10)),
1318 last_response_likely_bad: false,
1319 range_info: Some(BlockRangeInfo::new(0, 30, B256::random())),
1320 supports_snap: false,
1321 };
1322
1323 let peer_partial = Peer {
1325 state: PeerState::Idle,
1326 best_hash: B256::random(),
1327 best_number: 30,
1328 capabilities: Arc::new(Capabilities::new(vec![])),
1329 timeout: Arc::new(AtomicU64::new(10)),
1330 last_response_likely_bad: false,
1331 range_info: Some(BlockRangeInfo::new(10, 30, B256::random())),
1332 supports_snap: false,
1333 };
1334
1335 assert!(peer_full
1337 .is_better(&peer_partial, &BestPeerRequirements::FullBlockRange(range.clone())));
1338 assert!(!peer_partial.is_better(&peer_full, &BestPeerRequirements::FullBlockRange(range)));
1339 }
1340
1341 #[test]
1342 fn test_peer_is_better_no_range_info() {
1343 let range = RangeInclusive::new(40, 60);
1344
1345 let peer_with_range = Peer {
1347 state: PeerState::Idle,
1348 best_hash: B256::random(),
1349 best_number: 100,
1350 capabilities: Arc::new(Capabilities::new(vec![])),
1351 timeout: Arc::new(AtomicU64::new(10)),
1352 last_response_likely_bad: false,
1353 range_info: Some(BlockRangeInfo::new(30, 100, B256::random())),
1354 supports_snap: false,
1355 };
1356
1357 let peer_no_range = Peer {
1359 state: PeerState::Idle,
1360 best_hash: B256::random(),
1361 best_number: 100,
1362 capabilities: Arc::new(Capabilities::new(vec![])),
1363 timeout: Arc::new(AtomicU64::new(10)),
1364 last_response_likely_bad: false,
1365 range_info: None,
1366 supports_snap: false,
1367 };
1368
1369 assert!(!peer_no_range
1371 .is_better(&peer_with_range, &BestPeerRequirements::FullBlockRange(range.clone())));
1372
1373 assert!(
1375 peer_with_range.is_better(&peer_no_range, &BestPeerRequirements::FullBlockRange(range))
1376 );
1377 }
1378
1379 #[test]
1380 fn test_peer_is_better_one_peer_no_range_covers() {
1381 let range = RangeInclusive::new(40, 60);
1382
1383 let peer_with_range_covers = Peer {
1385 state: PeerState::Idle,
1386 best_hash: B256::random(),
1387 best_number: 100,
1388 capabilities: Arc::new(Capabilities::new(vec![])),
1389 timeout: Arc::new(AtomicU64::new(10)),
1390 last_response_likely_bad: false,
1391 range_info: Some(BlockRangeInfo::new(30, 100, B256::random())),
1392 supports_snap: false,
1393 };
1394
1395 let peer_no_range = Peer {
1397 state: PeerState::Idle,
1398 best_hash: B256::random(),
1399 best_number: 100,
1400 capabilities: Arc::new(Capabilities::new(vec![])),
1401 timeout: Arc::new(AtomicU64::new(10)),
1402 last_response_likely_bad: false,
1403 range_info: None,
1404 supports_snap: false,
1405 };
1406
1407 assert!(peer_with_range_covers
1409 .is_better(&peer_no_range, &BestPeerRequirements::FullBlockRange(range.clone())));
1410
1411 assert!(!peer_no_range
1413 .is_better(&peer_with_range_covers, &BestPeerRequirements::FullBlockRange(range)));
1414 }
1415
1416 #[test]
1417 fn test_peer_is_better_one_peer_no_range_doesnt_cover() {
1418 let range = RangeInclusive::new(40, 60);
1419
1420 let peer_with_range_no_cover = Peer {
1422 state: PeerState::Idle,
1423 best_hash: B256::random(),
1424 best_number: 100,
1425 capabilities: Arc::new(Capabilities::new(vec![])),
1426 timeout: Arc::new(AtomicU64::new(10)),
1427 last_response_likely_bad: false,
1428 range_info: Some(BlockRangeInfo::new(70, 100, B256::random())),
1429 supports_snap: false,
1430 };
1431
1432 let peer_no_range = Peer {
1434 state: PeerState::Idle,
1435 best_hash: B256::random(),
1436 best_number: 100,
1437 capabilities: Arc::new(Capabilities::new(vec![])),
1438 timeout: Arc::new(AtomicU64::new(10)),
1439 last_response_likely_bad: false,
1440 range_info: None,
1441 supports_snap: false,
1442 };
1443
1444 assert!(!peer_with_range_no_cover
1446 .is_better(&peer_no_range, &BestPeerRequirements::FullBlockRange(range.clone())));
1447
1448 assert!(peer_no_range
1450 .is_better(&peer_with_range_no_cover, &BestPeerRequirements::FullBlockRange(range)));
1451 }
1452
1453 #[test]
1454 fn test_peer_is_better_edge_cases() {
1455 let range = RangeInclusive::new(50, 100);
1457
1458 let peer_exact = Peer {
1460 state: PeerState::Idle,
1461 best_hash: B256::random(),
1462 best_number: 100,
1463 capabilities: Arc::new(Capabilities::new(vec![])),
1464 timeout: Arc::new(AtomicU64::new(10)),
1465 last_response_likely_bad: false,
1466 range_info: Some(BlockRangeInfo::new(50, 100, B256::random())),
1467 supports_snap: false,
1468 };
1469
1470 let peer_short_start = Peer {
1472 state: PeerState::Idle,
1473 best_hash: B256::random(),
1474 best_number: 100,
1475 capabilities: Arc::new(Capabilities::new(vec![])),
1476 timeout: Arc::new(AtomicU64::new(10)),
1477 last_response_likely_bad: false,
1478 range_info: Some(BlockRangeInfo::new(51, 100, B256::random())),
1479 supports_snap: false,
1480 };
1481
1482 let peer_short_end = Peer {
1484 state: PeerState::Idle,
1485 best_hash: B256::random(),
1486 best_number: 100,
1487 capabilities: Arc::new(Capabilities::new(vec![])),
1488 timeout: Arc::new(AtomicU64::new(10)),
1489 last_response_likely_bad: false,
1490 range_info: Some(BlockRangeInfo::new(50, 99, B256::random())),
1491 supports_snap: false,
1492 };
1493
1494 assert!(peer_exact
1496 .is_better(&peer_short_start, &BestPeerRequirements::FullBlockRange(range.clone())));
1497 assert!(peer_exact
1498 .is_better(&peer_short_end, &BestPeerRequirements::FullBlockRange(range.clone())));
1499
1500 assert!(!peer_short_start
1502 .is_better(&peer_exact, &BestPeerRequirements::FullBlockRange(range.clone())));
1503 assert!(
1504 !peer_short_end.is_better(&peer_exact, &BestPeerRequirements::FullBlockRange(range))
1505 );
1506 }
1507
1508 fn fetcher_with_peer() -> (StateFetcher<EthNetworkPrimitives>, PeerId) {
1510 let manager = PeersManager::new(PeersConfig::default());
1511 let mut fetcher =
1512 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1513 let peer_id = B512::random();
1514
1515 fetcher.new_active_peer(NewPeerInfo {
1516 peer_id,
1517 best_hash: Default::default(),
1518 best_number: Default::default(),
1519 capabilities: Arc::new(Capabilities::from(vec![])),
1520 timeout: Default::default(),
1521 range_info: None,
1522 supports_snap: false,
1523 });
1524 (fetcher, peer_id)
1525 }
1526
1527 fn insert_inflight_receipts(
1530 fetcher: &mut StateFetcher<EthNetworkPrimitives>,
1531 peer_id: PeerId,
1532 ) -> oneshot::Receiver<PeerRequestResult<ReceiptsResponse<reth_ethereum_primitives::Receipt>>>
1533 {
1534 let (tx, rx) = oneshot::channel();
1535 fetcher.inflight_receipts_requests.insert(peer_id, Request { request: (), response: tx });
1536 fetcher.peers.get_mut(&peer_id).unwrap().state = PeerState::GetReceipts;
1537 rx
1538 }
1539
1540 #[tokio::test]
1543 async fn test_poll_dispatches_receipts_to_peer() {
1544 let (mut fetcher, peer_id) = fetcher_with_peer();
1545
1546 poll_fn(move |cx| {
1547 let (tx, _rx) = oneshot::channel();
1548 fetcher.queued_requests.push_back(DownloadRequest::GetReceipts {
1549 request: vec![B256::ZERO],
1550 response: tx,
1551 priority: Priority::default(),
1552 });
1553
1554 let Poll::Ready(FetchAction::BlockRequest { peer_id: dispatched_peer, request }) =
1555 fetcher.poll(cx)
1556 else {
1557 panic!("expected Ready(BlockRequest)");
1558 };
1559 assert_eq!(dispatched_peer, peer_id);
1560 assert!(matches!(request, BlockRequest::GetReceipts(_)));
1561
1562 assert!(matches!(fetcher.peers[&peer_id].state, PeerState::GetReceipts));
1564 assert!(fetcher.inflight_receipts_requests.contains_key(&peer_id));
1566
1567 Poll::Ready(())
1568 })
1569 .await;
1570 }
1571
1572 #[tokio::test]
1575 async fn test_receipts_complete_response_resolves_and_idles_peer() {
1576 let (mut fetcher, peer_id) = fetcher_with_peer();
1577
1578 let rx = insert_inflight_receipts(&mut fetcher, peer_id);
1579
1580 let resp = ReceiptsResponse::new(vec![vec![]]);
1581 let outcome = fetcher.on_receipts_response(peer_id, Ok(resp));
1582
1583 assert!(outcome.is_none());
1585 assert!(fetcher.peers[&peer_id].state.is_idle());
1587 assert!(!fetcher.inflight_receipts_requests.contains_key(&peer_id));
1589
1590 let result = rx.await.unwrap().unwrap();
1592 assert_eq!(result.1.receipts.len(), 1);
1593 }
1594
1595 #[tokio::test]
1596 async fn test_receipts_empty_response_marks_peer_bad() {
1597 let (mut fetcher, peer_id) = fetcher_with_peer();
1598 let _rx = insert_inflight_receipts(&mut fetcher, peer_id);
1599
1600 let resp = ReceiptsResponse::new(vec![]);
1601 let _ = fetcher.on_receipts_response(peer_id, Ok(resp));
1602
1603 assert!(fetcher.peers[&peer_id].last_response_likely_bad);
1604 }
1605
1606 #[tokio::test]
1607 async fn test_receipts_error_forwards_and_marks_peer_bad() {
1608 let (mut fetcher, peer_id) = fetcher_with_peer();
1609 let rx = insert_inflight_receipts(&mut fetcher, peer_id);
1610
1611 let _ = fetcher.on_receipts_response(peer_id, Err(RequestError::Timeout));
1612
1613 assert!(fetcher.peers[&peer_id].last_response_likely_bad);
1614 let result = rx.await.unwrap();
1616 assert_eq!(result.unwrap_err(), RequestError::Timeout);
1617 }
1618
1619 #[tokio::test]
1620 async fn test_session_closed_cancels_inflight_receipts() {
1621 let (mut fetcher, peer_id) = fetcher_with_peer();
1622 let rx = insert_inflight_receipts(&mut fetcher, peer_id);
1623
1624 fetcher.on_session_closed(&peer_id);
1625
1626 assert!(!fetcher.peers.contains_key(&peer_id));
1627 assert!(!fetcher.inflight_receipts_requests.contains_key(&peer_id));
1628
1629 let result = rx.await.unwrap();
1630 assert_eq!(result.unwrap_err(), RequestError::ConnectionDropped);
1631 }
1632
1633 #[tokio::test]
1634 async fn test_receipts_response_triggers_followup() {
1635 let (mut fetcher, peer_id) = fetcher_with_peer();
1636
1637 let (followup_tx, _followup_rx) = oneshot::channel();
1639 fetcher.queued_requests.push_back(DownloadRequest::GetBlockBodies {
1640 request: vec![B256::random()],
1641 response: followup_tx,
1642 priority: Priority::default(),
1643 range_hint: None,
1644 });
1645
1646 let _rx = insert_inflight_receipts(&mut fetcher, peer_id);
1647
1648 let resp = ReceiptsResponse::new(vec![vec![]]);
1649 let outcome = fetcher.on_receipts_response(peer_id, Ok(resp));
1650
1651 assert!(matches!(outcome, Some(BlockResponseOutcome::Request(pid, _)) if pid == peer_id));
1652 }
1653
1654 #[tokio::test]
1655 async fn test_followup_skips_request_peer_cannot_serve() {
1656 let (mut fetcher, peer_id) = fetcher_with_peer();
1657
1658 let peer_71 = B512::random();
1659 let caps_71 = Arc::new(Capabilities::from(vec![Capability::new("eth".into(), 71)]));
1660 fetcher.new_active_peer(NewPeerInfo {
1661 peer_id: peer_71,
1662 best_hash: B256::random(),
1663 best_number: 100,
1664 capabilities: caps_71,
1665 timeout: Arc::new(AtomicU64::new(10)),
1666 range_info: None,
1667 supports_snap: false,
1668 });
1669 fetcher.peers.get_mut(&peer_71).expect("peer exists").state = PeerState::GetBlockHeaders;
1670
1671 let (followup_tx, _followup_rx) = oneshot::channel();
1672 fetcher.queued_requests.push_back(DownloadRequest::GetBlockAccessLists {
1673 request: vec![B256::random()],
1674 response: followup_tx,
1675 priority: Priority::Normal,
1676 requirement: BalRequirement::Optional,
1677 });
1678
1679 let _rx = insert_inflight_receipts(&mut fetcher, peer_id);
1680
1681 let resp = ReceiptsResponse::new(vec![vec![]]);
1682 assert!(fetcher.on_receipts_response(peer_id, Ok(resp)).is_none());
1683 assert!(fetcher.peers[&peer_id].state.is_idle());
1684 assert!(!fetcher.inflight_bals_requests.contains_key(&peer_id));
1685 assert!(matches!(
1686 fetcher.queued_requests.front(),
1687 Some(DownloadRequest::GetBlockAccessLists {
1688 requirement: BalRequirement::Optional,
1689 ..
1690 })
1691 ));
1692 }
1693
1694 #[tokio::test]
1695 async fn test_followup_uses_first_satisfiable_request() {
1696 let (mut fetcher, peer_id) = fetcher_with_peer();
1697
1698 let peer_71 = B512::random();
1699 let caps_71 = Arc::new(Capabilities::from(vec![Capability::new("eth".into(), 71)]));
1700 fetcher.new_active_peer(NewPeerInfo {
1701 peer_id: peer_71,
1702 best_hash: B256::random(),
1703 best_number: 100,
1704 capabilities: caps_71,
1705 timeout: Arc::new(AtomicU64::new(10)),
1706 range_info: None,
1707 supports_snap: false,
1708 });
1709 fetcher.peers.get_mut(&peer_71).expect("peer exists").state = PeerState::GetBlockHeaders;
1710
1711 let (bal_tx, _bal_rx) = oneshot::channel();
1712 fetcher.queued_requests.push_back(DownloadRequest::GetBlockAccessLists {
1713 request: vec![B256::random()],
1714 response: bal_tx,
1715 priority: Priority::Normal,
1716 requirement: BalRequirement::Optional,
1717 });
1718
1719 let (bodies_tx, _bodies_rx) = oneshot::channel();
1720 fetcher.queued_requests.push_back(DownloadRequest::GetBlockBodies {
1721 request: vec![B256::random()],
1722 response: bodies_tx,
1723 priority: Priority::Normal,
1724 range_hint: None,
1725 });
1726
1727 let _rx = insert_inflight_receipts(&mut fetcher, peer_id);
1728
1729 let resp = ReceiptsResponse::new(vec![vec![]]);
1730 let outcome = fetcher.on_receipts_response(peer_id, Ok(resp));
1731
1732 assert!(matches!(
1733 outcome,
1734 Some(BlockResponseOutcome::Request(pid, BlockRequest::GetBlockBodies(_))) if pid == peer_id
1735 ));
1736 assert!(fetcher.inflight_bodies_requests.contains_key(&peer_id));
1737 assert!(matches!(fetcher.peers[&peer_id].state, PeerState::GetBlockBodies));
1738 assert_eq!(fetcher.queued_requests.len(), 1);
1739 assert!(matches!(
1740 fetcher.queued_requests.front(),
1741 Some(DownloadRequest::GetBlockAccessLists {
1742 requirement: BalRequirement::Optional,
1743 ..
1744 })
1745 ));
1746 }
1747
1748 #[tokio::test]
1749 async fn test_prepare_block_request_creates_inflight_receipts() {
1750 let (mut fetcher, peer_id) = fetcher_with_peer();
1751 let hashes = vec![B256::with_last_byte(1), B256::with_last_byte(2)];
1752
1753 let (tx, _rx) = oneshot::channel();
1754 let req = DownloadRequest::GetReceipts {
1755 request: hashes.clone(),
1756 response: tx,
1757 priority: Priority::default(),
1758 };
1759
1760 let block_request = fetcher.prepare_block_request(peer_id, req);
1761
1762 match block_request {
1764 BlockRequest::GetReceipts(ref get) => {
1765 assert_eq!(get.0, hashes);
1766 }
1767 other => panic!("expected GetReceipts, got {other:?}"),
1768 }
1769
1770 assert!(matches!(fetcher.peers[&peer_id].state, PeerState::GetReceipts));
1772
1773 assert!(fetcher.inflight_receipts_requests.contains_key(&peer_id));
1775 }
1776 #[tokio::test]
1777 async fn test_next_best_peer_eth71_no_support() {
1778 let manager = PeersManager::new(PeersConfig::default());
1779 let mut fetcher =
1780 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1781
1782 let peer = B512::random();
1783
1784 let capabilities = Arc::new(Capabilities::new(vec![]));
1786
1787 fetcher.new_active_peer(NewPeerInfo {
1788 peer_id: peer,
1789 best_hash: B256::random(),
1790 best_number: 100,
1791 capabilities,
1792 timeout: Arc::new(AtomicU64::new(10)),
1793 range_info: None,
1794 supports_snap: false,
1795 });
1796
1797 assert_eq!(
1799 fetcher.next_best_peer(BestPeerRequirements::EthVersion(EthVersion::Eth71)),
1800 None
1801 );
1802 }
1803
1804 #[tokio::test]
1805 async fn test_next_best_peer_eth71_supported() {
1806 let manager = PeersManager::new(PeersConfig::default());
1807 let mut fetcher =
1808 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1809
1810 let peer = B512::random();
1811
1812 let capabilities = Arc::new(Capabilities::from(vec![Capability::new("eth".into(), 71)]));
1814
1815 fetcher.new_active_peer(NewPeerInfo {
1816 peer_id: peer,
1817 best_hash: B256::random(),
1818 best_number: 100,
1819 capabilities,
1820 timeout: Arc::new(AtomicU64::new(10)),
1821 range_info: None,
1822 supports_snap: false,
1823 });
1824
1825 assert_eq!(
1826 fetcher.next_best_peer(BestPeerRequirements::EthVersion(EthVersion::Eth71)),
1827 Some(peer)
1828 );
1829 }
1830
1831 #[tokio::test]
1832 async fn test_next_best_peer_eth71_filters_correctly() {
1833 let manager = PeersManager::new(PeersConfig::default());
1834 let mut fetcher =
1835 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1836
1837 let peer_no_71 = B512::random();
1838 let peer_with_71 = B512::random();
1839
1840 let caps_old = Arc::new(Capabilities::new(vec![]));
1842
1843 let caps_71 = Arc::new(Capabilities::from(vec![Capability::new("eth".into(), 71)]));
1845
1846 fetcher.new_active_peer(NewPeerInfo {
1847 peer_id: peer_no_71,
1848 best_hash: B256::random(),
1849 best_number: 100,
1850 capabilities: caps_old,
1851 timeout: Arc::new(AtomicU64::new(5)),
1852 range_info: None,
1853 supports_snap: false,
1854 });
1855
1856 fetcher.new_active_peer(NewPeerInfo {
1857 peer_id: peer_with_71,
1858 best_hash: B256::random(),
1859 best_number: 100,
1860 capabilities: caps_71,
1861 timeout: Arc::new(AtomicU64::new(50)),
1862 range_info: None,
1863 supports_snap: false,
1864 });
1865
1866 assert_eq!(
1869 fetcher.next_best_peer(BestPeerRequirements::EthVersion(EthVersion::Eth71)),
1870 Some(peer_with_71)
1871 );
1872 }
1873
1874 #[tokio::test]
1875 async fn test_wakes_when_eth71_peer_connects() {
1876 let manager = PeersManager::new(PeersConfig::default());
1877 let mut fetcher =
1878 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1879
1880 let (tx, _rx) = oneshot::channel();
1882 fetcher.queued_requests.push_back(DownloadRequest::GetBlockAccessLists {
1883 request: vec![],
1884 response: tx,
1885 priority: Priority::Normal,
1886 requirement: BalRequirement::Mandatory,
1887 });
1888
1889 let waker = noop_waker();
1890 let mut cx = Context::from_waker(&waker);
1891
1892 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
1894
1895 let peer_old = B512::random();
1897 let caps_old = Arc::new(Capabilities::new(vec![]));
1898
1899 fetcher.new_active_peer(NewPeerInfo {
1900 peer_id: peer_old,
1901 best_hash: B256::random(),
1902 best_number: 100,
1903 capabilities: caps_old,
1904 timeout: Arc::new(AtomicU64::new(10)),
1905 range_info: None,
1906 supports_snap: false,
1907 });
1908
1909 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
1911
1912 let peer_71 = B512::random();
1914 let caps_71 = Arc::new(Capabilities::from(vec![Capability::new("eth".into(), 71)]));
1915
1916 fetcher.new_active_peer(NewPeerInfo {
1917 peer_id: peer_71,
1918 best_hash: B256::random(),
1919 best_number: 100,
1920 capabilities: caps_71,
1921 timeout: Arc::new(AtomicU64::new(10)),
1922 range_info: None,
1923 supports_snap: false,
1924 });
1925
1926 if let Poll::Ready(FetchAction::BlockRequest { peer_id, .. }) = fetcher.poll(&mut cx) {
1928 assert_eq!(peer_id, peer_71);
1929 }
1930 }
1931
1932 #[tokio::test]
1933 async fn test_optional_bal_request_rejected_without_eth71_peer() {
1934 let manager = PeersManager::new(PeersConfig::default());
1935 let mut fetcher =
1936 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1937
1938 let peer_old = B512::random();
1939 let caps_old = Arc::new(Capabilities::new(vec![]));
1940 fetcher.new_active_peer(NewPeerInfo {
1941 peer_id: peer_old,
1942 best_hash: B256::random(),
1943 best_number: 100,
1944 capabilities: caps_old,
1945 timeout: Arc::new(AtomicU64::new(10)),
1946 range_info: None,
1947 supports_snap: false,
1948 });
1949
1950 let (tx, rx) = oneshot::channel();
1951 fetcher
1952 .download_requests_tx
1953 .send(DownloadRequest::GetBlockAccessLists {
1954 request: vec![],
1955 response: tx,
1956 priority: Priority::Normal,
1957 requirement: BalRequirement::Optional,
1958 })
1959 .unwrap();
1960
1961 let waker = noop_waker();
1962 let mut cx = Context::from_waker(&waker);
1963
1964 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
1965 assert!(fetcher.queued_requests.is_empty());
1966 assert_eq!(rx.await.unwrap().unwrap_err(), RequestError::UnsupportedCapability);
1967 }
1968
1969 #[tokio::test]
1970 async fn test_optional_bal_request_waits_for_busy_eth71_peer() {
1971 let manager = PeersManager::new(PeersConfig::default());
1972 let mut fetcher =
1973 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1974
1975 let peer_71 = B512::random();
1976 let caps_71 = Arc::new(Capabilities::from(vec![Capability::new("eth".into(), 71)]));
1977 fetcher.new_active_peer(NewPeerInfo {
1978 peer_id: peer_71,
1979 best_hash: B256::random(),
1980 best_number: 100,
1981 capabilities: caps_71,
1982 timeout: Arc::new(AtomicU64::new(10)),
1983 range_info: None,
1984 supports_snap: false,
1985 });
1986 fetcher.peers.get_mut(&peer_71).expect("peer exists").state = PeerState::GetBlockHeaders;
1987
1988 let (tx, _rx) = oneshot::channel();
1989 fetcher
1990 .download_requests_tx
1991 .send(DownloadRequest::GetBlockAccessLists {
1992 request: vec![],
1993 response: tx,
1994 priority: Priority::Normal,
1995 requirement: BalRequirement::Optional,
1996 })
1997 .unwrap();
1998
1999 let waker = noop_waker();
2000 let mut cx = Context::from_waker(&waker);
2001
2002 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
2003 assert_eq!(fetcher.queued_requests.len(), 1);
2004 }
2005
2006 #[tokio::test]
2007 async fn test_queued_optional_bal_request_rejected_after_eth71_disconnect() {
2008 let manager = PeersManager::new(PeersConfig::default());
2009 let mut fetcher =
2010 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
2011
2012 let peer_old = B512::random();
2013 let caps_old = Arc::new(Capabilities::new(vec![]));
2014 fetcher.new_active_peer(NewPeerInfo {
2015 peer_id: peer_old,
2016 best_hash: B256::random(),
2017 best_number: 100,
2018 capabilities: caps_old,
2019 timeout: Arc::new(AtomicU64::new(10)),
2020 range_info: None,
2021 supports_snap: false,
2022 });
2023
2024 let peer_71 = B512::random();
2025 let caps_71 = Arc::new(Capabilities::from(vec![Capability::new("eth".into(), 71)]));
2026 fetcher.new_active_peer(NewPeerInfo {
2027 peer_id: peer_71,
2028 best_hash: B256::random(),
2029 best_number: 100,
2030 capabilities: caps_71,
2031 timeout: Arc::new(AtomicU64::new(10)),
2032 range_info: None,
2033 supports_snap: false,
2034 });
2035 fetcher.peers.get_mut(&peer_71).expect("peer exists").state = PeerState::GetBlockHeaders;
2036
2037 let (tx, rx) = oneshot::channel();
2038 fetcher
2039 .download_requests_tx
2040 .send(DownloadRequest::GetBlockAccessLists {
2041 request: vec![],
2042 response: tx,
2043 priority: Priority::Normal,
2044 requirement: BalRequirement::Optional,
2045 })
2046 .unwrap();
2047
2048 let waker = noop_waker();
2049 let mut cx = Context::from_waker(&waker);
2050
2051 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
2052 assert_eq!(fetcher.queued_requests.len(), 1);
2053
2054 fetcher.on_session_closed(&peer_71);
2055
2056 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
2057 assert!(fetcher.queued_requests.is_empty());
2058 assert_eq!(rx.await.unwrap().unwrap_err(), RequestError::UnsupportedCapability);
2059 }
2060
2061 #[tokio::test]
2062 async fn test_next_best_peer_snap_no_support() {
2063 let manager = PeersManager::new(PeersConfig::default());
2064 let mut fetcher =
2065 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
2066
2067 let peer = B512::random();
2068 fetcher.new_active_peer(NewPeerInfo {
2069 peer_id: peer,
2070 best_hash: B256::random(),
2071 best_number: 100,
2072 capabilities: Arc::new(Capabilities::new(vec![])),
2073 timeout: Arc::new(AtomicU64::new(10)),
2074 range_info: None,
2075 supports_snap: false,
2076 });
2077
2078 assert_eq!(fetcher.next_best_peer(BestPeerRequirements::SupportsSnap), None);
2079 }
2080
2081 #[tokio::test]
2082 async fn test_next_best_peer_snap_filters_correctly() {
2083 let manager = PeersManager::new(PeersConfig::default());
2084 let mut fetcher =
2085 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
2086
2087 let peer_no_snap = B512::random();
2088 let peer_with_snap = B512::random();
2089
2090 fetcher.new_active_peer(NewPeerInfo {
2091 peer_id: peer_no_snap,
2092 best_hash: B256::random(),
2093 best_number: 100,
2094 capabilities: Arc::new(Capabilities::new(vec![])),
2095 timeout: Arc::new(AtomicU64::new(5)),
2096 range_info: None,
2097 supports_snap: false,
2098 });
2099 fetcher.new_active_peer(NewPeerInfo {
2100 peer_id: peer_with_snap,
2101 best_hash: B256::random(),
2102 best_number: 100,
2103 capabilities: Arc::new(Capabilities::new(vec![])),
2104 timeout: Arc::new(AtomicU64::new(50)),
2105 range_info: None,
2106 supports_snap: true,
2107 });
2108
2109 assert_eq!(
2111 fetcher.next_best_peer(BestPeerRequirements::SupportsSnap),
2112 Some(peer_with_snap)
2113 );
2114 }
2115
2116 #[tokio::test]
2117 async fn test_snap_request_rejected_without_snap_peer() {
2118 let manager = PeersManager::new(PeersConfig::default());
2119 let mut fetcher =
2120 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
2121
2122 fetcher.new_active_peer(NewPeerInfo {
2124 peer_id: B512::random(),
2125 best_hash: B256::random(),
2126 best_number: 100,
2127 capabilities: Arc::new(Capabilities::new(vec![])),
2128 timeout: Arc::new(AtomicU64::new(10)),
2129 range_info: None,
2130 supports_snap: false,
2131 });
2132
2133 let (tx, rx) = oneshot::channel();
2134 fetcher
2135 .download_requests_tx
2136 .send(DownloadRequest::GetSnap {
2137 request: SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
2138 request_id: 0,
2139 root_hash: B256::ZERO,
2140 starting_hash: B256::ZERO,
2141 limit_hash: B256::ZERO,
2142 response_bytes: 0,
2143 }),
2144 response: tx,
2145 priority: Priority::Normal,
2146 })
2147 .unwrap();
2148
2149 let waker = noop_waker();
2150 let mut cx = Context::from_waker(&waker);
2151
2152 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
2153 assert!(fetcher.queued_requests.is_empty());
2154 assert_eq!(rx.await.unwrap().unwrap_err(), RequestError::UnsupportedCapability);
2155 }
2156
2157 #[test_case(SnapResponse::AccountRange(AccountRangeMessage {
2158 request_id: 1, accounts: vec![], proof: vec![Bytes::from_static(&[0xc0])],
2159 }); "account proof")]
2160 #[test_case(SnapResponse::StorageRanges(StorageRangesMessage {
2161 request_id: 1, slots: vec![], proof: vec![Bytes::from_static(&[0xc0])],
2162 }); "storage proof")]
2163 #[test_case(SnapResponse::ByteCodes(ByteCodesMessage {
2164 request_id: 1, codes: vec![Bytes::new()],
2165 }); "empty bytecode")]
2166 #[test_case(SnapResponse::BlockAccessLists(BlockAccessListsMessage {
2167 request_id: 1,
2168 block_access_lists: BlockAccessLists(vec![Some(Bytes::from_static(&[alloy_rlp::EMPTY_LIST_CODE]))]),
2169 }); "present empty BAL")]
2170 #[tokio::test]
2171 async fn test_snap_response_triggers_followup(resp: SnapResponse) {
2172 let manager = PeersManager::new(PeersConfig::default());
2173 let mut fetcher =
2174 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
2175
2176 let peer_id = B512::random();
2177 fetcher.new_active_peer(NewPeerInfo {
2178 peer_id,
2179 best_hash: B256::random(),
2180 best_number: 100,
2181 capabilities: Arc::new(Capabilities::new(vec![])),
2182 timeout: Arc::new(AtomicU64::new(10)),
2183 range_info: None,
2184 supports_snap: true,
2185 });
2186
2187 let (followup_tx, _followup_rx) = oneshot::channel();
2189 fetcher.queued_requests.push_back(DownloadRequest::GetSnap {
2190 request: SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
2191 request_id: 0,
2192 root_hash: B256::ZERO,
2193 starting_hash: B256::ZERO,
2194 limit_hash: B256::ZERO,
2195 response_bytes: 0,
2196 }),
2197 response: followup_tx,
2198 priority: Priority::Normal,
2199 });
2200
2201 let (tx, mut rx) = oneshot::channel();
2202 fetcher.inflight_snap_requests.insert(peer_id, Request { request: (), response: tx });
2203 fetcher.peers.get_mut(&peer_id).unwrap().state = PeerState::GetSnap;
2204
2205 let outcome = fetcher.on_snap_response(peer_id, Ok(resp));
2206
2207 assert!(matches!(outcome, Some(BlockResponseOutcome::Request(pid, _)) if pid == peer_id));
2208 assert!(rx.try_recv().is_ok());
2209 }
2210
2211 #[tokio::test]
2212 async fn test_queued_snap_request_rejected_after_last_peer_disconnects() {
2213 let manager = PeersManager::new(PeersConfig::default());
2214 let mut fetcher =
2215 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
2216
2217 let peer = B512::random();
2219 fetcher.new_active_peer(NewPeerInfo {
2220 peer_id: peer,
2221 best_hash: B256::random(),
2222 best_number: 100,
2223 capabilities: Arc::new(Capabilities::new(vec![])),
2224 timeout: Arc::new(AtomicU64::new(10)),
2225 range_info: None,
2226 supports_snap: true,
2227 });
2228 fetcher.peers.get_mut(&peer).expect("peer exists").state = PeerState::GetBlockHeaders;
2229
2230 let (tx, rx) = oneshot::channel();
2231 fetcher
2232 .download_requests_tx
2233 .send(DownloadRequest::GetSnap {
2234 request: SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
2235 request_id: 0,
2236 root_hash: B256::ZERO,
2237 starting_hash: B256::ZERO,
2238 limit_hash: B256::ZERO,
2239 response_bytes: 0,
2240 }),
2241 response: tx,
2242 priority: Priority::Normal,
2243 })
2244 .unwrap();
2245
2246 let waker = noop_waker();
2247 let mut cx = Context::from_waker(&waker);
2248
2249 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
2250 assert_eq!(fetcher.queued_requests.len(), 1);
2251
2252 fetcher.on_session_closed(&peer);
2255
2256 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
2257 assert!(fetcher.queued_requests.is_empty());
2258 assert_eq!(rx.await.unwrap().unwrap_err(), RequestError::UnsupportedCapability);
2259 }
2260
2261 #[tokio::test]
2262 async fn unavailable_snap_responses_yield_to_another_peer() {
2263 let responses = [
2264 SnapResponse::BlockAccessLists(BlockAccessListsMessage {
2265 request_id: 1,
2266 block_access_lists: BlockAccessLists(vec![None, None]),
2267 }),
2268 SnapResponse::AccountRange(AccountRangeMessage {
2269 request_id: 1,
2270 accounts: vec![],
2271 proof: vec![],
2272 }),
2273 SnapResponse::StorageRanges(StorageRangesMessage {
2274 request_id: 1,
2275 slots: vec![],
2276 proof: vec![],
2277 }),
2278 SnapResponse::ByteCodes(ByteCodesMessage { request_id: 1, codes: vec![] }),
2279 SnapResponse::BlockAccessLists(BlockAccessListsMessage {
2280 request_id: 1,
2281 block_access_lists: BlockAccessLists(vec![]),
2282 }),
2283 SnapResponse::BlockAccessLists(BlockAccessListsMessage {
2284 request_id: 1,
2285 block_access_lists: BlockAccessLists(vec![
2286 None,
2287 Some(Bytes::from_static(&[alloy_rlp::EMPTY_LIST_CODE])),
2288 ]),
2289 }),
2290 ];
2291 for response in responses {
2292 let manager = PeersManager::new(PeersConfig::default());
2293 let mut fetcher =
2294 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
2295 let fast = B512::random();
2296 let other = B512::random();
2297 for (peer_id, timeout) in [(fast, 10), (other, 50)] {
2298 fetcher.new_active_peer(NewPeerInfo {
2299 peer_id,
2300 best_hash: B256::random(),
2301 best_number: 100,
2302 capabilities: Arc::new(Capabilities::new(vec![])),
2303 timeout: Arc::new(AtomicU64::new(timeout)),
2304 range_info: None,
2305 supports_snap: true,
2306 });
2307 }
2308 assert_eq!(fetcher.next_best_peer(BestPeerRequirements::SupportsSnap), Some(fast));
2309 let (tx, rx) = oneshot::channel();
2310 fetcher.inflight_snap_requests.insert(fast, Request { request: (), response: tx });
2311 fetcher.peers.get_mut(&fast).unwrap().state = PeerState::GetSnap;
2312 let (followup_tx, _followup_rx) = oneshot::channel();
2313 fetcher.queued_requests.push_back(DownloadRequest::GetSnap {
2314 request: SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
2315 request_id: 2,
2316 root_hash: B256::random(),
2317 starting_hash: B256::ZERO,
2318 limit_hash: B256::repeat_byte(0xff),
2319 response_bytes: 1024,
2320 }),
2321 response: followup_tx,
2322 priority: Priority::Normal,
2323 });
2324
2325 assert_eq!(fetcher.on_snap_response(fast, Ok(response.clone())), None);
2326 assert_eq!(rx.await.unwrap().unwrap().1, response);
2327 assert_eq!(
2328 fetcher.next_best_peer(BestPeerRequirements::SupportsSnap),
2329 Some(other),
2330 "an unavailable response must not monopolize requests: {response:?}"
2331 );
2332 assert!(matches!(
2333 fetcher.poll_action(),
2334 PollAction::Ready(FetchAction::BlockRequest { peer_id, .. }) if peer_id == other
2335 ));
2336 }
2337 }
2338}