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.is_better(best_peer.1, &requirement) {
189 best_peer = maybe_better;
190 continue
191 }
192
193 if maybe_better.1.timeout() < best_peer.1.timeout() &&
195 !maybe_better.1.last_response_likely_bad
196 {
197 best_peer = maybe_better;
198 }
199 }
200
201 Some(*best_peer.0)
202 }
203
204 fn has_eth71_peer(&self) -> bool {
206 self.peers.values().any(|peer| {
207 !matches!(peer.state, PeerState::Closing) &&
208 peer.capabilities.supports_eth_at_least(&EthVersion::Eth71)
209 })
210 }
211
212 fn poll_action(&mut self) -> PollAction {
214 if self.queued_requests.is_empty() {
216 return PollAction::NoRequests
217 }
218
219 let request = self.queued_requests.pop_front().expect("not empty");
220 let Some(peer_id) = self.next_best_peer(request.best_peer_requirements()) else {
221 if self.should_fail_fast(&request) {
224 request.send_err_response(RequestError::UnsupportedCapability);
225 } else {
226 self.queued_requests.push_back(request);
229 }
230 return PollAction::NoPeersAvailable
231 };
232
233 let request = self.prepare_block_request(peer_id, request);
234
235 PollAction::Ready(FetchAction::BlockRequest { peer_id, request })
236 }
237
238 pub(crate) fn poll(&mut self, cx: &mut Context<'_>) -> Poll<FetchAction> {
240 loop {
242 let no_peers_available = match self.poll_action() {
243 PollAction::Ready(action) => return Poll::Ready(action),
244 PollAction::NoRequests => false,
245 PollAction::NoPeersAvailable => true,
246 };
247
248 loop {
249 match self.download_requests_rx.poll_next_unpin(cx) {
251 Poll::Ready(Some(request)) => {
252 if self.should_fail_fast(&request) {
255 request.send_err_response(RequestError::UnsupportedCapability);
256 continue
257 }
258
259 match request.get_priority() {
260 Priority::High => {
261 let pos = self
264 .queued_requests
265 .iter()
266 .position(|req| req.is_normal_priority())
267 .unwrap_or(0);
268 self.queued_requests.insert(pos, request);
269 }
270 Priority::Normal => {
271 self.queued_requests.push_back(request);
272 }
273 }
274 }
275 Poll::Ready(None) => {
276 unreachable!("channel can't close")
277 }
278 Poll::Pending => break,
279 }
280 }
281
282 if self.queued_requests.is_empty() || no_peers_available {
283 return Poll::Pending
284 }
285 }
286 }
287
288 fn has_snap_peer(&self) -> bool {
290 self.peers
291 .values()
292 .any(|peer| !matches!(peer.state, PeerState::Closing) && peer.supports_snap)
293 }
294
295 fn should_fail_fast(&self, request: &DownloadRequest<N>) -> bool {
298 (request.is_optional_bal() && !self.has_eth71_peer()) ||
299 (request.is_snap() && !self.has_snap_peer())
300 }
301
302 fn prepare_block_request(&mut self, peer_id: PeerId, req: DownloadRequest<N>) -> BlockRequest {
306 if let Some(peer) = self.peers.get_mut(&peer_id) {
308 peer.state = req.peer_state();
309 }
310
311 self.prepare_inflight_block_request(peer_id, req)
312 }
313
314 fn prepare_inflight_block_request(
316 &mut self,
317 peer_id: PeerId,
318 req: DownloadRequest<N>,
319 ) -> BlockRequest {
320 match req {
321 DownloadRequest::GetBlockHeaders { request, response, .. } => {
322 let inflight = Request { request: request.clone(), response };
323 self.inflight_headers_requests.insert(peer_id, inflight);
324 let HeadersRequest { start, limit, direction } = request;
325 BlockRequest::GetBlockHeaders(GetBlockHeaders {
326 start_block: start,
327 limit,
328 skip: 0,
329 direction,
330 })
331 }
332 DownloadRequest::GetBlockBodies { request, response, .. } => {
333 let inflight = Request { request: (), response };
334 self.inflight_bodies_requests.insert(peer_id, inflight);
335 BlockRequest::GetBlockBodies(GetBlockBodies(request))
336 }
337 DownloadRequest::GetBlockAccessLists { request, response, .. } => {
338 let inflight = Request { request: (), response };
339 self.inflight_bals_requests.insert(peer_id, inflight);
340 BlockRequest::GetBlockAccessLists(GetBlockAccessLists(request))
341 }
342 DownloadRequest::GetReceipts { request, response, .. } => {
343 let inflight = Request { request: (), response };
344 self.inflight_receipts_requests.insert(peer_id, inflight);
345 BlockRequest::GetReceipts(GetReceipts(request))
346 }
347 DownloadRequest::GetSnap { request, response, .. } => {
348 let inflight = Request { request: (), response };
349 self.inflight_snap_requests.insert(peer_id, inflight);
350 BlockRequest::GetSnap(request)
351 }
352 }
353 }
354
355 fn followup_request(&mut self, peer_id: PeerId) -> Option<BlockResponseOutcome> {
363 let peer = self.peers.get_mut(&peer_id)?;
364 let req_idx = self.queued_requests.iter().position(|req| {
365 peer.satisfies(&req.best_peer_requirements())
367 })?;
368 let req = self.queued_requests.remove(req_idx).expect("valid request index");
369
370 peer.state = req.peer_state();
371 let req = self.prepare_inflight_block_request(peer_id, req);
372 Some(BlockResponseOutcome::Request(peer_id, req))
373 }
374
375 pub(crate) fn on_block_headers_response(
381 &mut self,
382 peer_id: PeerId,
383 res: RequestResult<Vec<N::BlockHeader>>,
384 ) -> Option<BlockResponseOutcome> {
385 let is_error = res.is_err();
386 let maybe_reputation_change = res.reputation_change_err();
387
388 let resp = self.inflight_headers_requests.remove(&peer_id);
389
390 let is_likely_bad_response =
391 resp.as_ref().is_some_and(|r| res.is_likely_bad_headers_response(&r.request));
392
393 if let Some(resp) = resp {
394 let _ = resp.response.send(res.map(|h| (peer_id, h).into()));
396 }
397
398 if let Some(peer) = self.peers.get_mut(&peer_id) {
399 peer.last_response_likely_bad = is_likely_bad_response;
401
402 if peer.state.on_request_finished() && !is_error && !is_likely_bad_response {
405 return self.followup_request(peer_id)
406 }
407 }
408
409 maybe_reputation_change
412 .map(|reputation_change| BlockResponseOutcome::BadResponse(peer_id, reputation_change))
413 }
414
415 pub(crate) fn on_block_bodies_response(
417 &mut self,
418 peer_id: PeerId,
419 res: RequestResult<Vec<N::BlockBody>>,
420 ) -> Option<BlockResponseOutcome> {
421 let is_likely_bad_response = res.as_ref().map_or(true, |bodies| bodies.is_empty());
422
423 if let Some(resp) = self.inflight_bodies_requests.remove(&peer_id) {
424 let _ = resp.response.send(res.map(|b| (peer_id, b).into()));
425 }
426 if let Some(peer) = self.peers.get_mut(&peer_id) {
427 peer.last_response_likely_bad = is_likely_bad_response;
429
430 if peer.state.on_request_finished() && !is_likely_bad_response {
431 return self.followup_request(peer_id)
432 }
433 }
434 None
435 }
436
437 pub(crate) fn on_block_access_lists_response(
439 &mut self,
440 peer_id: PeerId,
441 res: RequestResult<BlockAccessLists>,
442 ) -> Option<BlockResponseOutcome> {
443 let is_likely_bad_response = res.is_err();
444
445 if let Some(resp) = self.inflight_bals_requests.remove(&peer_id) {
446 let _ = resp.response.send(res.map(|b| (peer_id, b).into()));
447 }
448 if let Some(peer) = self.peers.get_mut(&peer_id) {
449 peer.last_response_likely_bad = is_likely_bad_response;
450
451 if peer.state.on_request_finished() && !is_likely_bad_response {
452 return self.followup_request(peer_id)
453 }
454 }
455 None
456 }
457
458 pub(crate) fn on_receipts_response(
463 &mut self,
464 peer_id: PeerId,
465 res: RequestResult<ReceiptsResponse<N::Receipt>>,
466 ) -> Option<BlockResponseOutcome> {
467 let is_likely_bad_response = res.as_ref().map_or(true, |resp| resp.receipts.is_empty());
468
469 if let Some(resp) = self.inflight_receipts_requests.remove(&peer_id) {
470 let _ = resp.response.send(res.map(|r| (peer_id, r).into()));
471 }
472 if let Some(peer) = self.peers.get_mut(&peer_id) {
473 peer.last_response_likely_bad = is_likely_bad_response;
474
475 if peer.state.on_request_finished() && !is_likely_bad_response {
476 return self.followup_request(peer_id)
477 }
478 }
479 None
480 }
481
482 pub(crate) fn on_snap_response(
484 &mut self,
485 peer_id: PeerId,
486 res: RequestResult<SnapResponse>,
487 ) -> Option<BlockResponseOutcome> {
488 let is_likely_bad_response = res.is_err();
489
490 if let Some(resp) = self.inflight_snap_requests.remove(&peer_id) {
491 let _ = resp.response.send(res.map(|r| (peer_id, r).into()));
492 }
493 if let Some(peer) = self.peers.get_mut(&peer_id) {
494 peer.last_response_likely_bad = is_likely_bad_response;
495
496 if peer.state.on_request_finished() && !is_likely_bad_response {
497 return self.followup_request(peer_id)
498 }
499 }
500 None
501 }
502
503 pub(crate) fn client(&self) -> FetchClient<N> {
505 FetchClient {
506 request_tx: self.download_requests_tx.clone(),
507 peers_handle: self.peers_handle.clone(),
508 num_active_peers: Arc::clone(&self.num_active_peers),
509 }
510 }
511}
512
513enum PollAction {
515 Ready(FetchAction),
516 NoRequests,
517 NoPeersAvailable,
518}
519
520#[derive(Debug)]
522pub(crate) struct NewPeerInfo {
523 pub(crate) peer_id: PeerId,
525 pub(crate) best_hash: B256,
527 pub(crate) best_number: u64,
529 pub(crate) capabilities: Arc<Capabilities>,
531 pub(crate) timeout: Arc<AtomicU64>,
533 pub(crate) range_info: Option<BlockRangeInfo>,
535 pub(crate) supports_snap: bool,
537}
538
539#[derive(Debug)]
541struct Peer {
542 state: PeerState,
544 best_hash: B256,
546 best_number: u64,
548 #[allow(dead_code)]
550 capabilities: Arc<Capabilities>,
551 timeout: Arc<AtomicU64>,
553 last_response_likely_bad: bool,
560 range_info: Option<BlockRangeInfo>,
562 supports_snap: bool,
564}
565
566impl Peer {
567 fn timeout(&self) -> u64 {
568 self.timeout.load(Ordering::Relaxed)
569 }
570
571 fn earliest(&self) -> u64 {
573 self.range_info.as_ref().map_or(0, |info| info.earliest())
574 }
575
576 fn has_full_history(&self) -> bool {
578 self.earliest() == 0
579 }
580
581 fn range(&self) -> Option<RangeInclusive<u64>> {
582 self.range_info.as_ref().map(|info| info.range())
583 }
584
585 fn satisfies(&self, requirement: &BestPeerRequirements) -> bool {
587 match requirement {
588 BestPeerRequirements::EthVersion(ver) => self.capabilities.supports_eth_at_least(ver),
589 BestPeerRequirements::SupportsSnap => self.supports_snap,
590 BestPeerRequirements::None |
591 BestPeerRequirements::FullBlock |
592 BestPeerRequirements::FullBlockRange(_) => true,
593 }
594 }
595
596 fn has_better_range(&self, other: &Self, range: &RangeInclusive<u64>) -> bool {
605 let self_range = self.range();
606 let other_range = other.range();
607
608 match (self_range, other_range) {
609 (Some(self_r), Some(other_r)) => {
610 let self_covers = self_r.contains(range.start()) && self_r.contains(range.end());
612 let other_covers = other_r.contains(range.start()) && other_r.contains(range.end());
613
614 #[expect(clippy::match_same_arms)]
615 match (self_covers, other_covers) {
616 (true, false) => true, (false, true) => false, (true, true) => false, (false, false) => {
620 self_r.start() < other_r.start()
622 }
623 }
624 }
625 (Some(self_r), None) => {
626 self_r.contains(range.start()) && self_r.contains(range.end())
629 }
630 (None, Some(other_r)) => {
631 !(other_r.contains(range.start()) && other_r.contains(range.end()))
634 }
635 (None, None) => false, }
637 }
638
639 fn is_better(&self, other: &Self, requirement: &BestPeerRequirements) -> bool {
641 match requirement {
642 BestPeerRequirements::FullBlockRange(range) => self.has_better_range(other, range),
643 BestPeerRequirements::FullBlock => self.has_full_history() && !other.has_full_history(),
644 BestPeerRequirements::None |
647 BestPeerRequirements::EthVersion(_) |
648 BestPeerRequirements::SupportsSnap => false,
649 }
650 }
651}
652
653#[derive(Debug)]
655enum PeerState {
656 Idle,
658 GetBlockHeaders,
660 GetBlockBodies,
662 GetBlockAccessLists,
664 GetReceipts,
666 GetSnap,
668 Closing,
670}
671
672impl PeerState {
675 const fn is_idle(&self) -> bool {
677 matches!(self, Self::Idle)
678 }
679
680 const fn on_request_finished(&mut self) -> bool {
686 if !matches!(self, Self::Closing) {
687 *self = Self::Idle;
688 return true
689 }
690 false
691 }
692}
693
694#[derive(Debug)]
697struct Request<Req, Resp> {
698 request: Req,
701 response: oneshot::Sender<Resp>,
702}
703
704#[derive(Debug)]
706#[expect(clippy::enum_variant_names)]
707pub(crate) enum DownloadRequest<N: NetworkPrimitives> {
708 GetBlockHeaders {
710 request: HeadersRequest,
711 response: oneshot::Sender<PeerRequestResult<Vec<N::BlockHeader>>>,
712 priority: Priority,
713 },
714 GetBlockBodies {
716 request: Vec<B256>,
717 response: oneshot::Sender<PeerRequestResult<Vec<N::BlockBody>>>,
718 priority: Priority,
719 range_hint: Option<RangeInclusive<u64>>,
720 },
721 GetBlockAccessLists {
723 request: Vec<B256>,
724 response: oneshot::Sender<PeerRequestResult<BlockAccessLists>>,
725 priority: Priority,
726 requirement: BalRequirement,
727 },
728 GetReceipts {
730 request: Vec<B256>,
731 response: oneshot::Sender<PeerRequestResult<ReceiptsResponse<N::Receipt>>>,
732 priority: Priority,
733 },
734 GetSnap {
736 request: SnapProtocolMessage,
737 response: oneshot::Sender<PeerRequestResult<SnapResponse>>,
738 priority: Priority,
739 },
740}
741
742impl<N: NetworkPrimitives> DownloadRequest<N> {
745 const fn peer_state(&self) -> PeerState {
747 match self {
748 Self::GetBlockHeaders { .. } => PeerState::GetBlockHeaders,
749 Self::GetBlockBodies { .. } => PeerState::GetBlockBodies,
750 Self::GetBlockAccessLists { .. } => PeerState::GetBlockAccessLists,
751 Self::GetReceipts { .. } => PeerState::GetReceipts,
752 Self::GetSnap { .. } => PeerState::GetSnap,
753 }
754 }
755
756 const fn get_priority(&self) -> &Priority {
758 match self {
759 Self::GetBlockHeaders { priority, .. } |
760 Self::GetBlockBodies { priority, .. } |
761 Self::GetBlockAccessLists { priority, .. } |
762 Self::GetReceipts { priority, .. } |
763 Self::GetSnap { priority, .. } => priority,
764 }
765 }
766
767 const fn is_normal_priority(&self) -> bool {
769 self.get_priority().is_normal()
770 }
771
772 const fn is_optional_bal(&self) -> bool {
774 matches!(self, Self::GetBlockAccessLists { requirement: BalRequirement::Optional, .. })
775 }
776
777 const fn is_snap(&self) -> bool {
779 matches!(self, Self::GetSnap { .. })
780 }
781
782 fn send_err_response(self, err: RequestError) {
784 let _ = match self {
785 Self::GetBlockHeaders { response, .. } => response.send(Err(err)).ok(),
786 Self::GetBlockBodies { response, .. } => response.send(Err(err)).ok(),
787 Self::GetBlockAccessLists { response, .. } => response.send(Err(err)).ok(),
788 Self::GetReceipts { response, .. } => response.send(Err(err)).ok(),
789 Self::GetSnap { response, .. } => response.send(Err(err)).ok(),
790 };
791 }
792
793 fn best_peer_requirements(&self) -> BestPeerRequirements {
795 match self {
796 Self::GetBlockHeaders { .. } => BestPeerRequirements::None,
797 Self::GetBlockAccessLists { .. } => BestPeerRequirements::EthVersion(EthVersion::Eth71),
798 Self::GetBlockBodies { range_hint, .. } => {
799 if let Some(range) = range_hint {
800 BestPeerRequirements::FullBlockRange(range.clone())
801 } else {
802 BestPeerRequirements::FullBlock
803 }
804 }
805 Self::GetReceipts { .. } => BestPeerRequirements::FullBlock,
806 Self::GetSnap { .. } => BestPeerRequirements::SupportsSnap,
807 }
808 }
809}
810
811pub(crate) enum FetchAction {
813 BlockRequest {
815 peer_id: PeerId,
817 request: BlockRequest,
819 },
820}
821
822#[derive(Debug, PartialEq, Eq)]
826pub(crate) enum BlockResponseOutcome {
827 Request(PeerId, BlockRequest),
829 BadResponse(PeerId, ReputationChangeKind),
831}
832
833enum BestPeerRequirements {
835 None,
837 FullBlockRange(RangeInclusive<u64>),
839 FullBlock,
841 EthVersion(EthVersion),
843 SupportsSnap,
845}
846
847#[cfg(test)]
848mod tests {
849 use super::*;
850 use crate::{peers::PeersManager, PeersConfig};
851 use alloy_consensus::Header;
852 use alloy_primitives::B512;
853 use reth_eth_wire::Capability;
854 use reth_eth_wire_types::snap::{AccountRangeMessage, GetAccountRangeMessage};
855 use std::future::poll_fn;
856
857 #[tokio::test(flavor = "multi_thread")]
858 async fn test_poll_fetcher() {
859 let manager = PeersManager::new(PeersConfig::default());
860 let mut fetcher =
861 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
862
863 poll_fn(move |cx| {
864 assert!(fetcher.poll(cx).is_pending());
865 let (tx, _rx) = oneshot::channel();
866 fetcher.queued_requests.push_back(DownloadRequest::GetBlockBodies {
867 request: vec![],
868 response: tx,
869 priority: Priority::default(),
870 range_hint: None,
871 });
872 assert!(fetcher.poll(cx).is_pending());
873
874 Poll::Ready(())
875 })
876 .await;
877 }
878
879 #[tokio::test]
880 async fn test_peer_rotation() {
881 let manager = PeersManager::new(PeersConfig::default());
882 let mut fetcher =
883 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
884 let peer1 = B512::random();
886 let peer2 = B512::random();
887 let capabilities = Arc::new(Capabilities::from(vec![]));
888 fetcher.new_active_peer(NewPeerInfo {
889 peer_id: peer1,
890 best_hash: B256::random(),
891 best_number: 1,
892 capabilities: Arc::clone(&capabilities),
893 timeout: Arc::new(AtomicU64::new(1)),
894 range_info: None,
895 supports_snap: false,
896 });
897 fetcher.new_active_peer(NewPeerInfo {
898 peer_id: peer2,
899 best_hash: B256::random(),
900 best_number: 2,
901 capabilities: Arc::clone(&capabilities),
902 timeout: Arc::new(AtomicU64::new(1)),
903 range_info: None,
904 supports_snap: false,
905 });
906
907 let first_peer = fetcher.next_best_peer(BestPeerRequirements::None).unwrap();
908 assert!(first_peer == peer1 || first_peer == peer2);
909 fetcher.on_pending_disconnect(&first_peer);
911 let second_peer = fetcher.next_best_peer(BestPeerRequirements::None).unwrap();
913 assert!(first_peer == peer1 || first_peer == peer2);
914 assert_ne!(first_peer, second_peer);
915 fetcher.on_pending_disconnect(&second_peer);
917 assert_eq!(fetcher.next_best_peer(BestPeerRequirements::None), None);
918 }
919
920 #[tokio::test]
921 async fn test_peer_prioritization() {
922 let manager = PeersManager::new(PeersConfig::default());
923 let mut fetcher =
924 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
925 let peer1 = B512::random();
927 let peer2 = B512::random();
928 let peer3 = B512::random();
929
930 let peer2_timeout = Arc::new(AtomicU64::new(300));
931
932 let capabilities = Arc::new(Capabilities::from(vec![]));
933 fetcher.new_active_peer(NewPeerInfo {
934 peer_id: peer1,
935 best_hash: B256::random(),
936 best_number: 1,
937 capabilities: Arc::clone(&capabilities),
938 timeout: Arc::new(AtomicU64::new(30)),
939 range_info: None,
940 supports_snap: false,
941 });
942 fetcher.new_active_peer(NewPeerInfo {
943 peer_id: peer2,
944 best_hash: B256::random(),
945 best_number: 2,
946 capabilities: Arc::clone(&capabilities),
947 timeout: Arc::clone(&peer2_timeout),
948 range_info: None,
949 supports_snap: false,
950 });
951 fetcher.new_active_peer(NewPeerInfo {
952 peer_id: peer3,
953 best_hash: B256::random(),
954 best_number: 3,
955 capabilities: Arc::clone(&capabilities),
956 timeout: Arc::new(AtomicU64::new(50)),
957 range_info: None,
958 supports_snap: false,
959 });
960
961 assert_eq!(fetcher.next_best_peer(BestPeerRequirements::None), Some(peer1));
963 assert_eq!(fetcher.next_best_peer(BestPeerRequirements::None), Some(peer1));
964 peer2_timeout.store(10, Ordering::Relaxed);
966 assert_eq!(fetcher.next_best_peer(BestPeerRequirements::None), Some(peer2));
968 assert_eq!(fetcher.next_best_peer(BestPeerRequirements::None), Some(peer2));
969 }
970
971 #[tokio::test]
972 async fn test_on_block_headers_response() {
973 let manager = PeersManager::new(PeersConfig::default());
974 let mut fetcher =
975 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
976 let peer_id = B512::random();
977
978 assert_eq!(fetcher.on_block_headers_response(peer_id, Ok(vec![Header::default()])), None);
979
980 assert_eq!(
981 fetcher.on_block_headers_response(peer_id, Err(RequestError::Timeout)),
982 Some(BlockResponseOutcome::BadResponse(peer_id, ReputationChangeKind::Timeout))
983 );
984 assert_eq!(
985 fetcher.on_block_headers_response(peer_id, Err(RequestError::BadResponse)),
986 None
987 );
988 assert_eq!(
989 fetcher.on_block_headers_response(peer_id, Err(RequestError::ChannelClosed)),
990 None
991 );
992 assert_eq!(
993 fetcher.on_block_headers_response(peer_id, Err(RequestError::ConnectionDropped)),
994 None
995 );
996 assert_eq!(
997 fetcher.on_block_headers_response(peer_id, Err(RequestError::UnsupportedCapability)),
998 None
999 );
1000 }
1001
1002 #[tokio::test]
1003 async fn test_header_response_outcome() {
1004 let manager = PeersManager::new(PeersConfig::default());
1005 let mut fetcher =
1006 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1007 let peer_id = B512::random();
1008
1009 let request_pair = || {
1010 let (tx, _rx) = oneshot::channel();
1011 let req = Request {
1012 request: HeadersRequest {
1013 start: 0u64.into(),
1014 limit: 1,
1015 direction: Default::default(),
1016 },
1017 response: tx,
1018 };
1019 let header = Header { number: 0, ..Default::default() };
1020 (req, header)
1021 };
1022
1023 fetcher.new_active_peer(NewPeerInfo {
1024 peer_id,
1025 best_hash: Default::default(),
1026 best_number: Default::default(),
1027 capabilities: Arc::new(Capabilities::from(vec![])),
1028 timeout: Default::default(),
1029 range_info: None,
1030 supports_snap: false,
1031 });
1032
1033 let (req, header) = request_pair();
1034 fetcher.inflight_headers_requests.insert(peer_id, req);
1035
1036 let outcome = fetcher.on_block_headers_response(peer_id, Ok(vec![header]));
1037 assert!(outcome.is_none());
1038 assert!(fetcher.peers[&peer_id].state.is_idle());
1039
1040 let outcome =
1041 fetcher.on_block_headers_response(peer_id, Err(RequestError::Timeout)).unwrap();
1042
1043 assert!(EthResponseValidator::reputation_change_err(&Err::<Vec<Header>, _>(
1044 RequestError::Timeout
1045 ))
1046 .is_some());
1047
1048 match outcome {
1049 BlockResponseOutcome::BadResponse(peer, _) => {
1050 assert_eq!(peer, peer_id)
1051 }
1052 BlockResponseOutcome::Request(_, _) => {
1053 unreachable!()
1054 }
1055 };
1056
1057 assert!(fetcher.peers[&peer_id].state.is_idle());
1058 }
1059
1060 #[test]
1061 fn test_peer_is_better_none_requirement() {
1062 let peer1 = Peer {
1063 state: PeerState::Idle,
1064 best_hash: B256::random(),
1065 best_number: 100,
1066 capabilities: Arc::new(Capabilities::new(vec![])),
1067 timeout: Arc::new(AtomicU64::new(10)),
1068 last_response_likely_bad: false,
1069 range_info: Some(BlockRangeInfo::new(0, 100, B256::random())),
1070 supports_snap: false,
1071 };
1072
1073 let peer2 = Peer {
1074 state: PeerState::Idle,
1075 best_hash: B256::random(),
1076 best_number: 50,
1077 capabilities: Arc::new(Capabilities::new(vec![])),
1078 timeout: Arc::new(AtomicU64::new(20)),
1079 last_response_likely_bad: false,
1080 range_info: None,
1081 supports_snap: false,
1082 };
1083
1084 assert!(!peer1.is_better(&peer2, &BestPeerRequirements::None));
1086 assert!(!peer2.is_better(&peer1, &BestPeerRequirements::None));
1087 }
1088
1089 #[test]
1090 fn test_peer_is_better_full_block_requirement() {
1091 let peer_full = Peer {
1093 state: PeerState::Idle,
1094 best_hash: B256::random(),
1095 best_number: 100,
1096 capabilities: Arc::new(Capabilities::new(vec![])),
1097 timeout: Arc::new(AtomicU64::new(10)),
1098 last_response_likely_bad: false,
1099 range_info: Some(BlockRangeInfo::new(0, 100, B256::random())),
1100 supports_snap: false,
1101 };
1102
1103 let peer_partial = Peer {
1105 state: PeerState::Idle,
1106 best_hash: B256::random(),
1107 best_number: 100,
1108 capabilities: Arc::new(Capabilities::new(vec![])),
1109 timeout: Arc::new(AtomicU64::new(10)),
1110 last_response_likely_bad: false,
1111 range_info: Some(BlockRangeInfo::new(50, 100, B256::random())),
1112 supports_snap: false,
1113 };
1114
1115 let peer_no_range = Peer {
1117 state: PeerState::Idle,
1118 best_hash: B256::random(),
1119 best_number: 100,
1120 capabilities: Arc::new(Capabilities::new(vec![])),
1121 timeout: Arc::new(AtomicU64::new(10)),
1122 last_response_likely_bad: false,
1123 range_info: None,
1124 supports_snap: false,
1125 };
1126
1127 assert!(peer_full.is_better(&peer_partial, &BestPeerRequirements::FullBlock));
1129 assert!(!peer_partial.is_better(&peer_full, &BestPeerRequirements::FullBlock));
1130
1131 assert!(peer_no_range.is_better(&peer_partial, &BestPeerRequirements::FullBlock));
1133 assert!(!peer_partial.is_better(&peer_no_range, &BestPeerRequirements::FullBlock));
1134
1135 assert!(!peer_full.is_better(&peer_no_range, &BestPeerRequirements::FullBlock));
1137 assert!(!peer_no_range.is_better(&peer_full, &BestPeerRequirements::FullBlock));
1138 }
1139
1140 #[test]
1141 fn test_peer_is_better_full_block_range_requirement() {
1142 let range = RangeInclusive::new(40, 60);
1143
1144 let peer_covers = Peer {
1146 state: PeerState::Idle,
1147 best_hash: B256::random(),
1148 best_number: 100,
1149 capabilities: Arc::new(Capabilities::new(vec![])),
1150 timeout: Arc::new(AtomicU64::new(10)),
1151 last_response_likely_bad: false,
1152 range_info: Some(BlockRangeInfo::new(0, 100, B256::random())),
1153 supports_snap: false,
1154 };
1155
1156 let peer_no_cover = Peer {
1158 state: PeerState::Idle,
1159 best_hash: B256::random(),
1160 best_number: 100,
1161 capabilities: Arc::new(Capabilities::new(vec![])),
1162 timeout: Arc::new(AtomicU64::new(10)),
1163 last_response_likely_bad: false,
1164 range_info: Some(BlockRangeInfo::new(70, 100, B256::random())),
1165 supports_snap: false,
1166 };
1167
1168 assert!(peer_covers
1170 .is_better(&peer_no_cover, &BestPeerRequirements::FullBlockRange(range.clone())));
1171 assert!(
1172 !peer_no_cover.is_better(&peer_covers, &BestPeerRequirements::FullBlockRange(range))
1173 );
1174 }
1175
1176 #[test]
1177 fn test_peer_is_better_both_cover_range() {
1178 let range = RangeInclusive::new(30, 50);
1179
1180 let peer_full = Peer {
1182 state: PeerState::Idle,
1183 best_hash: B256::random(),
1184 best_number: 100,
1185 capabilities: Arc::new(Capabilities::new(vec![])),
1186 timeout: Arc::new(AtomicU64::new(10)),
1187 last_response_likely_bad: false,
1188 range_info: Some(BlockRangeInfo::new(0, 50, B256::random())),
1189 supports_snap: false,
1190 };
1191
1192 let peer_partial = Peer {
1194 state: PeerState::Idle,
1195 best_hash: B256::random(),
1196 best_number: 100,
1197 capabilities: Arc::new(Capabilities::new(vec![])),
1198 timeout: Arc::new(AtomicU64::new(10)),
1199 last_response_likely_bad: false,
1200 range_info: Some(BlockRangeInfo::new(30, 50, B256::random())),
1201 supports_snap: false,
1202 };
1203
1204 assert!(!peer_full
1206 .is_better(&peer_partial, &BestPeerRequirements::FullBlockRange(range.clone())));
1207 assert!(!peer_partial.is_better(&peer_full, &BestPeerRequirements::FullBlockRange(range)));
1208 }
1209
1210 #[test]
1211 fn test_peer_is_better_lower_start() {
1212 let range = RangeInclusive::new(30, 60);
1213
1214 let peer_full = Peer {
1216 state: PeerState::Idle,
1217 best_hash: B256::random(),
1218 best_number: 100,
1219 capabilities: Arc::new(Capabilities::new(vec![])),
1220 timeout: Arc::new(AtomicU64::new(10)),
1221 last_response_likely_bad: false,
1222 range_info: Some(BlockRangeInfo::new(0, 50, B256::random())),
1223 supports_snap: false,
1224 };
1225
1226 let peer_partial = Peer {
1228 state: PeerState::Idle,
1229 best_hash: B256::random(),
1230 best_number: 100,
1231 capabilities: Arc::new(Capabilities::new(vec![])),
1232 timeout: Arc::new(AtomicU64::new(10)),
1233 last_response_likely_bad: false,
1234 range_info: Some(BlockRangeInfo::new(30, 50, B256::random())),
1235 supports_snap: false,
1236 };
1237
1238 assert!(peer_full
1240 .is_better(&peer_partial, &BestPeerRequirements::FullBlockRange(range.clone())));
1241 assert!(!peer_partial.is_better(&peer_full, &BestPeerRequirements::FullBlockRange(range)));
1242 }
1243
1244 #[test]
1245 fn test_peer_is_better_neither_covers_range() {
1246 let range = RangeInclusive::new(40, 60);
1247
1248 let peer_full = Peer {
1250 state: PeerState::Idle,
1251 best_hash: B256::random(),
1252 best_number: 30,
1253 capabilities: Arc::new(Capabilities::new(vec![])),
1254 timeout: Arc::new(AtomicU64::new(10)),
1255 last_response_likely_bad: false,
1256 range_info: Some(BlockRangeInfo::new(0, 30, B256::random())),
1257 supports_snap: false,
1258 };
1259
1260 let peer_partial = Peer {
1262 state: PeerState::Idle,
1263 best_hash: B256::random(),
1264 best_number: 30,
1265 capabilities: Arc::new(Capabilities::new(vec![])),
1266 timeout: Arc::new(AtomicU64::new(10)),
1267 last_response_likely_bad: false,
1268 range_info: Some(BlockRangeInfo::new(10, 30, B256::random())),
1269 supports_snap: false,
1270 };
1271
1272 assert!(peer_full
1274 .is_better(&peer_partial, &BestPeerRequirements::FullBlockRange(range.clone())));
1275 assert!(!peer_partial.is_better(&peer_full, &BestPeerRequirements::FullBlockRange(range)));
1276 }
1277
1278 #[test]
1279 fn test_peer_is_better_no_range_info() {
1280 let range = RangeInclusive::new(40, 60);
1281
1282 let peer_with_range = Peer {
1284 state: PeerState::Idle,
1285 best_hash: B256::random(),
1286 best_number: 100,
1287 capabilities: Arc::new(Capabilities::new(vec![])),
1288 timeout: Arc::new(AtomicU64::new(10)),
1289 last_response_likely_bad: false,
1290 range_info: Some(BlockRangeInfo::new(30, 100, B256::random())),
1291 supports_snap: false,
1292 };
1293
1294 let peer_no_range = Peer {
1296 state: PeerState::Idle,
1297 best_hash: B256::random(),
1298 best_number: 100,
1299 capabilities: Arc::new(Capabilities::new(vec![])),
1300 timeout: Arc::new(AtomicU64::new(10)),
1301 last_response_likely_bad: false,
1302 range_info: None,
1303 supports_snap: false,
1304 };
1305
1306 assert!(!peer_no_range
1308 .is_better(&peer_with_range, &BestPeerRequirements::FullBlockRange(range.clone())));
1309
1310 assert!(
1312 peer_with_range.is_better(&peer_no_range, &BestPeerRequirements::FullBlockRange(range))
1313 );
1314 }
1315
1316 #[test]
1317 fn test_peer_is_better_one_peer_no_range_covers() {
1318 let range = RangeInclusive::new(40, 60);
1319
1320 let peer_with_range_covers = Peer {
1322 state: PeerState::Idle,
1323 best_hash: B256::random(),
1324 best_number: 100,
1325 capabilities: Arc::new(Capabilities::new(vec![])),
1326 timeout: Arc::new(AtomicU64::new(10)),
1327 last_response_likely_bad: false,
1328 range_info: Some(BlockRangeInfo::new(30, 100, B256::random())),
1329 supports_snap: false,
1330 };
1331
1332 let peer_no_range = Peer {
1334 state: PeerState::Idle,
1335 best_hash: B256::random(),
1336 best_number: 100,
1337 capabilities: Arc::new(Capabilities::new(vec![])),
1338 timeout: Arc::new(AtomicU64::new(10)),
1339 last_response_likely_bad: false,
1340 range_info: None,
1341 supports_snap: false,
1342 };
1343
1344 assert!(peer_with_range_covers
1346 .is_better(&peer_no_range, &BestPeerRequirements::FullBlockRange(range.clone())));
1347
1348 assert!(!peer_no_range
1350 .is_better(&peer_with_range_covers, &BestPeerRequirements::FullBlockRange(range)));
1351 }
1352
1353 #[test]
1354 fn test_peer_is_better_one_peer_no_range_doesnt_cover() {
1355 let range = RangeInclusive::new(40, 60);
1356
1357 let peer_with_range_no_cover = 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: Some(BlockRangeInfo::new(70, 100, B256::random())),
1366 supports_snap: false,
1367 };
1368
1369 let peer_no_range = Peer {
1371 state: PeerState::Idle,
1372 best_hash: B256::random(),
1373 best_number: 100,
1374 capabilities: Arc::new(Capabilities::new(vec![])),
1375 timeout: Arc::new(AtomicU64::new(10)),
1376 last_response_likely_bad: false,
1377 range_info: None,
1378 supports_snap: false,
1379 };
1380
1381 assert!(!peer_with_range_no_cover
1383 .is_better(&peer_no_range, &BestPeerRequirements::FullBlockRange(range.clone())));
1384
1385 assert!(peer_no_range
1387 .is_better(&peer_with_range_no_cover, &BestPeerRequirements::FullBlockRange(range)));
1388 }
1389
1390 #[test]
1391 fn test_peer_is_better_edge_cases() {
1392 let range = RangeInclusive::new(50, 100);
1394
1395 let peer_exact = 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: Some(BlockRangeInfo::new(50, 100, B256::random())),
1404 supports_snap: false,
1405 };
1406
1407 let peer_short_start = Peer {
1409 state: PeerState::Idle,
1410 best_hash: B256::random(),
1411 best_number: 100,
1412 capabilities: Arc::new(Capabilities::new(vec![])),
1413 timeout: Arc::new(AtomicU64::new(10)),
1414 last_response_likely_bad: false,
1415 range_info: Some(BlockRangeInfo::new(51, 100, B256::random())),
1416 supports_snap: false,
1417 };
1418
1419 let peer_short_end = Peer {
1421 state: PeerState::Idle,
1422 best_hash: B256::random(),
1423 best_number: 100,
1424 capabilities: Arc::new(Capabilities::new(vec![])),
1425 timeout: Arc::new(AtomicU64::new(10)),
1426 last_response_likely_bad: false,
1427 range_info: Some(BlockRangeInfo::new(50, 99, B256::random())),
1428 supports_snap: false,
1429 };
1430
1431 assert!(peer_exact
1433 .is_better(&peer_short_start, &BestPeerRequirements::FullBlockRange(range.clone())));
1434 assert!(peer_exact
1435 .is_better(&peer_short_end, &BestPeerRequirements::FullBlockRange(range.clone())));
1436
1437 assert!(!peer_short_start
1439 .is_better(&peer_exact, &BestPeerRequirements::FullBlockRange(range.clone())));
1440 assert!(
1441 !peer_short_end.is_better(&peer_exact, &BestPeerRequirements::FullBlockRange(range))
1442 );
1443 }
1444
1445 fn fetcher_with_peer() -> (StateFetcher<EthNetworkPrimitives>, PeerId) {
1447 let manager = PeersManager::new(PeersConfig::default());
1448 let mut fetcher =
1449 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1450 let peer_id = B512::random();
1451
1452 fetcher.new_active_peer(NewPeerInfo {
1453 peer_id,
1454 best_hash: Default::default(),
1455 best_number: Default::default(),
1456 capabilities: Arc::new(Capabilities::from(vec![])),
1457 timeout: Default::default(),
1458 range_info: None,
1459 supports_snap: false,
1460 });
1461 (fetcher, peer_id)
1462 }
1463
1464 fn insert_inflight_receipts(
1467 fetcher: &mut StateFetcher<EthNetworkPrimitives>,
1468 peer_id: PeerId,
1469 ) -> oneshot::Receiver<PeerRequestResult<ReceiptsResponse<reth_ethereum_primitives::Receipt>>>
1470 {
1471 let (tx, rx) = oneshot::channel();
1472 fetcher.inflight_receipts_requests.insert(peer_id, Request { request: (), response: tx });
1473 fetcher.peers.get_mut(&peer_id).unwrap().state = PeerState::GetReceipts;
1474 rx
1475 }
1476
1477 #[tokio::test]
1480 async fn test_poll_dispatches_receipts_to_peer() {
1481 let (mut fetcher, peer_id) = fetcher_with_peer();
1482
1483 poll_fn(move |cx| {
1484 let (tx, _rx) = oneshot::channel();
1485 fetcher.queued_requests.push_back(DownloadRequest::GetReceipts {
1486 request: vec![B256::ZERO],
1487 response: tx,
1488 priority: Priority::default(),
1489 });
1490
1491 let Poll::Ready(FetchAction::BlockRequest { peer_id: dispatched_peer, request }) =
1492 fetcher.poll(cx)
1493 else {
1494 panic!("expected Ready(BlockRequest)");
1495 };
1496 assert_eq!(dispatched_peer, peer_id);
1497 assert!(matches!(request, BlockRequest::GetReceipts(_)));
1498
1499 assert!(matches!(fetcher.peers[&peer_id].state, PeerState::GetReceipts));
1501 assert!(fetcher.inflight_receipts_requests.contains_key(&peer_id));
1503
1504 Poll::Ready(())
1505 })
1506 .await;
1507 }
1508
1509 #[tokio::test]
1512 async fn test_receipts_complete_response_resolves_and_idles_peer() {
1513 let (mut fetcher, peer_id) = fetcher_with_peer();
1514
1515 let rx = insert_inflight_receipts(&mut fetcher, peer_id);
1516
1517 let resp = ReceiptsResponse::new(vec![vec![]]);
1518 let outcome = fetcher.on_receipts_response(peer_id, Ok(resp));
1519
1520 assert!(outcome.is_none());
1522 assert!(fetcher.peers[&peer_id].state.is_idle());
1524 assert!(!fetcher.inflight_receipts_requests.contains_key(&peer_id));
1526
1527 let result = rx.await.unwrap().unwrap();
1529 assert_eq!(result.1.receipts.len(), 1);
1530 }
1531
1532 #[tokio::test]
1533 async fn test_receipts_empty_response_marks_peer_bad() {
1534 let (mut fetcher, peer_id) = fetcher_with_peer();
1535 let _rx = insert_inflight_receipts(&mut fetcher, peer_id);
1536
1537 let resp = ReceiptsResponse::new(vec![]);
1538 let _ = fetcher.on_receipts_response(peer_id, Ok(resp));
1539
1540 assert!(fetcher.peers[&peer_id].last_response_likely_bad);
1541 }
1542
1543 #[tokio::test]
1544 async fn test_receipts_error_forwards_and_marks_peer_bad() {
1545 let (mut fetcher, peer_id) = fetcher_with_peer();
1546 let rx = insert_inflight_receipts(&mut fetcher, peer_id);
1547
1548 let _ = fetcher.on_receipts_response(peer_id, Err(RequestError::Timeout));
1549
1550 assert!(fetcher.peers[&peer_id].last_response_likely_bad);
1551 let result = rx.await.unwrap();
1553 assert_eq!(result.unwrap_err(), RequestError::Timeout);
1554 }
1555
1556 #[tokio::test]
1557 async fn test_session_closed_cancels_inflight_receipts() {
1558 let (mut fetcher, peer_id) = fetcher_with_peer();
1559 let rx = insert_inflight_receipts(&mut fetcher, peer_id);
1560
1561 fetcher.on_session_closed(&peer_id);
1562
1563 assert!(!fetcher.peers.contains_key(&peer_id));
1564 assert!(!fetcher.inflight_receipts_requests.contains_key(&peer_id));
1565
1566 let result = rx.await.unwrap();
1567 assert_eq!(result.unwrap_err(), RequestError::ConnectionDropped);
1568 }
1569
1570 #[tokio::test]
1571 async fn test_receipts_response_triggers_followup() {
1572 let (mut fetcher, peer_id) = fetcher_with_peer();
1573
1574 let (followup_tx, _followup_rx) = oneshot::channel();
1576 fetcher.queued_requests.push_back(DownloadRequest::GetBlockBodies {
1577 request: vec![B256::random()],
1578 response: followup_tx,
1579 priority: Priority::default(),
1580 range_hint: None,
1581 });
1582
1583 let _rx = insert_inflight_receipts(&mut fetcher, peer_id);
1584
1585 let resp = ReceiptsResponse::new(vec![vec![]]);
1586 let outcome = fetcher.on_receipts_response(peer_id, Ok(resp));
1587
1588 assert!(matches!(outcome, Some(BlockResponseOutcome::Request(pid, _)) if pid == peer_id));
1589 }
1590
1591 #[tokio::test]
1592 async fn test_followup_skips_request_peer_cannot_serve() {
1593 let (mut fetcher, peer_id) = fetcher_with_peer();
1594
1595 let peer_71 = B512::random();
1596 let caps_71 = Arc::new(Capabilities::from(vec![Capability::new("eth".into(), 71)]));
1597 fetcher.new_active_peer(NewPeerInfo {
1598 peer_id: peer_71,
1599 best_hash: B256::random(),
1600 best_number: 100,
1601 capabilities: caps_71,
1602 timeout: Arc::new(AtomicU64::new(10)),
1603 range_info: None,
1604 supports_snap: false,
1605 });
1606 fetcher.peers.get_mut(&peer_71).expect("peer exists").state = PeerState::GetBlockHeaders;
1607
1608 let (followup_tx, _followup_rx) = oneshot::channel();
1609 fetcher.queued_requests.push_back(DownloadRequest::GetBlockAccessLists {
1610 request: vec![B256::random()],
1611 response: followup_tx,
1612 priority: Priority::Normal,
1613 requirement: BalRequirement::Optional,
1614 });
1615
1616 let _rx = insert_inflight_receipts(&mut fetcher, peer_id);
1617
1618 let resp = ReceiptsResponse::new(vec![vec![]]);
1619 assert!(fetcher.on_receipts_response(peer_id, Ok(resp)).is_none());
1620 assert!(fetcher.peers[&peer_id].state.is_idle());
1621 assert!(!fetcher.inflight_bals_requests.contains_key(&peer_id));
1622 assert!(matches!(
1623 fetcher.queued_requests.front(),
1624 Some(DownloadRequest::GetBlockAccessLists {
1625 requirement: BalRequirement::Optional,
1626 ..
1627 })
1628 ));
1629 }
1630
1631 #[tokio::test]
1632 async fn test_followup_uses_first_satisfiable_request() {
1633 let (mut fetcher, peer_id) = fetcher_with_peer();
1634
1635 let peer_71 = B512::random();
1636 let caps_71 = Arc::new(Capabilities::from(vec![Capability::new("eth".into(), 71)]));
1637 fetcher.new_active_peer(NewPeerInfo {
1638 peer_id: peer_71,
1639 best_hash: B256::random(),
1640 best_number: 100,
1641 capabilities: caps_71,
1642 timeout: Arc::new(AtomicU64::new(10)),
1643 range_info: None,
1644 supports_snap: false,
1645 });
1646 fetcher.peers.get_mut(&peer_71).expect("peer exists").state = PeerState::GetBlockHeaders;
1647
1648 let (bal_tx, _bal_rx) = oneshot::channel();
1649 fetcher.queued_requests.push_back(DownloadRequest::GetBlockAccessLists {
1650 request: vec![B256::random()],
1651 response: bal_tx,
1652 priority: Priority::Normal,
1653 requirement: BalRequirement::Optional,
1654 });
1655
1656 let (bodies_tx, _bodies_rx) = oneshot::channel();
1657 fetcher.queued_requests.push_back(DownloadRequest::GetBlockBodies {
1658 request: vec![B256::random()],
1659 response: bodies_tx,
1660 priority: Priority::Normal,
1661 range_hint: None,
1662 });
1663
1664 let _rx = insert_inflight_receipts(&mut fetcher, peer_id);
1665
1666 let resp = ReceiptsResponse::new(vec![vec![]]);
1667 let outcome = fetcher.on_receipts_response(peer_id, Ok(resp));
1668
1669 assert!(matches!(
1670 outcome,
1671 Some(BlockResponseOutcome::Request(pid, BlockRequest::GetBlockBodies(_))) if pid == peer_id
1672 ));
1673 assert!(fetcher.inflight_bodies_requests.contains_key(&peer_id));
1674 assert!(matches!(fetcher.peers[&peer_id].state, PeerState::GetBlockBodies));
1675 assert_eq!(fetcher.queued_requests.len(), 1);
1676 assert!(matches!(
1677 fetcher.queued_requests.front(),
1678 Some(DownloadRequest::GetBlockAccessLists {
1679 requirement: BalRequirement::Optional,
1680 ..
1681 })
1682 ));
1683 }
1684
1685 #[tokio::test]
1686 async fn test_prepare_block_request_creates_inflight_receipts() {
1687 let (mut fetcher, peer_id) = fetcher_with_peer();
1688 let hashes = vec![B256::with_last_byte(1), B256::with_last_byte(2)];
1689
1690 let (tx, _rx) = oneshot::channel();
1691 let req = DownloadRequest::GetReceipts {
1692 request: hashes.clone(),
1693 response: tx,
1694 priority: Priority::default(),
1695 };
1696
1697 let block_request = fetcher.prepare_block_request(peer_id, req);
1698
1699 match block_request {
1701 BlockRequest::GetReceipts(ref get) => {
1702 assert_eq!(get.0, hashes);
1703 }
1704 other => panic!("expected GetReceipts, got {other:?}"),
1705 }
1706
1707 assert!(matches!(fetcher.peers[&peer_id].state, PeerState::GetReceipts));
1709
1710 assert!(fetcher.inflight_receipts_requests.contains_key(&peer_id));
1712 }
1713 #[tokio::test]
1714 async fn test_next_best_peer_eth71_no_support() {
1715 let manager = PeersManager::new(PeersConfig::default());
1716 let mut fetcher =
1717 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1718
1719 let peer = B512::random();
1720
1721 let capabilities = Arc::new(Capabilities::new(vec![]));
1723
1724 fetcher.new_active_peer(NewPeerInfo {
1725 peer_id: peer,
1726 best_hash: B256::random(),
1727 best_number: 100,
1728 capabilities,
1729 timeout: Arc::new(AtomicU64::new(10)),
1730 range_info: None,
1731 supports_snap: false,
1732 });
1733
1734 assert_eq!(
1736 fetcher.next_best_peer(BestPeerRequirements::EthVersion(EthVersion::Eth71)),
1737 None
1738 );
1739 }
1740
1741 #[tokio::test]
1742 async fn test_next_best_peer_eth71_supported() {
1743 let manager = PeersManager::new(PeersConfig::default());
1744 let mut fetcher =
1745 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1746
1747 let peer = B512::random();
1748
1749 let capabilities = Arc::new(Capabilities::from(vec![Capability::new("eth".into(), 71)]));
1751
1752 fetcher.new_active_peer(NewPeerInfo {
1753 peer_id: peer,
1754 best_hash: B256::random(),
1755 best_number: 100,
1756 capabilities,
1757 timeout: Arc::new(AtomicU64::new(10)),
1758 range_info: None,
1759 supports_snap: false,
1760 });
1761
1762 assert_eq!(
1763 fetcher.next_best_peer(BestPeerRequirements::EthVersion(EthVersion::Eth71)),
1764 Some(peer)
1765 );
1766 }
1767
1768 #[tokio::test]
1769 async fn test_next_best_peer_eth71_filters_correctly() {
1770 let manager = PeersManager::new(PeersConfig::default());
1771 let mut fetcher =
1772 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1773
1774 let peer_no_71 = B512::random();
1775 let peer_with_71 = B512::random();
1776
1777 let caps_old = Arc::new(Capabilities::new(vec![]));
1779
1780 let caps_71 = Arc::new(Capabilities::from(vec![Capability::new("eth".into(), 71)]));
1782
1783 fetcher.new_active_peer(NewPeerInfo {
1784 peer_id: peer_no_71,
1785 best_hash: B256::random(),
1786 best_number: 100,
1787 capabilities: caps_old,
1788 timeout: Arc::new(AtomicU64::new(5)),
1789 range_info: None,
1790 supports_snap: false,
1791 });
1792
1793 fetcher.new_active_peer(NewPeerInfo {
1794 peer_id: peer_with_71,
1795 best_hash: B256::random(),
1796 best_number: 100,
1797 capabilities: caps_71,
1798 timeout: Arc::new(AtomicU64::new(50)),
1799 range_info: None,
1800 supports_snap: false,
1801 });
1802
1803 assert_eq!(
1806 fetcher.next_best_peer(BestPeerRequirements::EthVersion(EthVersion::Eth71)),
1807 Some(peer_with_71)
1808 );
1809 }
1810
1811 #[tokio::test]
1812 async fn test_wakes_when_eth71_peer_connects() {
1813 use futures::task::noop_waker;
1814 use std::task::{Context, Poll};
1815
1816 let manager = PeersManager::new(PeersConfig::default());
1817 let mut fetcher =
1818 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1819
1820 let (tx, _rx) = oneshot::channel();
1822 fetcher.queued_requests.push_back(DownloadRequest::GetBlockAccessLists {
1823 request: vec![],
1824 response: tx,
1825 priority: Priority::Normal,
1826 requirement: BalRequirement::Mandatory,
1827 });
1828
1829 let waker = noop_waker();
1830 let mut cx = Context::from_waker(&waker);
1831
1832 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
1834
1835 let peer_old = B512::random();
1837 let caps_old = Arc::new(Capabilities::new(vec![]));
1838
1839 fetcher.new_active_peer(NewPeerInfo {
1840 peer_id: peer_old,
1841 best_hash: B256::random(),
1842 best_number: 100,
1843 capabilities: caps_old,
1844 timeout: Arc::new(AtomicU64::new(10)),
1845 range_info: None,
1846 supports_snap: false,
1847 });
1848
1849 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
1851
1852 let peer_71 = B512::random();
1854 let caps_71 = Arc::new(Capabilities::from(vec![Capability::new("eth".into(), 71)]));
1855
1856 fetcher.new_active_peer(NewPeerInfo {
1857 peer_id: peer_71,
1858 best_hash: B256::random(),
1859 best_number: 100,
1860 capabilities: caps_71,
1861 timeout: Arc::new(AtomicU64::new(10)),
1862 range_info: None,
1863 supports_snap: false,
1864 });
1865
1866 if let Poll::Ready(FetchAction::BlockRequest { peer_id, .. }) = fetcher.poll(&mut cx) {
1868 assert_eq!(peer_id, peer_71);
1869 }
1870 }
1871
1872 #[tokio::test]
1873 async fn test_optional_bal_request_rejected_without_eth71_peer() {
1874 use futures::task::noop_waker;
1875 use std::task::{Context, Poll};
1876
1877 let manager = PeersManager::new(PeersConfig::default());
1878 let mut fetcher =
1879 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1880
1881 let peer_old = B512::random();
1882 let caps_old = Arc::new(Capabilities::new(vec![]));
1883 fetcher.new_active_peer(NewPeerInfo {
1884 peer_id: peer_old,
1885 best_hash: B256::random(),
1886 best_number: 100,
1887 capabilities: caps_old,
1888 timeout: Arc::new(AtomicU64::new(10)),
1889 range_info: None,
1890 supports_snap: false,
1891 });
1892
1893 let (tx, rx) = oneshot::channel();
1894 fetcher
1895 .download_requests_tx
1896 .send(DownloadRequest::GetBlockAccessLists {
1897 request: vec![],
1898 response: tx,
1899 priority: Priority::Normal,
1900 requirement: BalRequirement::Optional,
1901 })
1902 .unwrap();
1903
1904 let waker = noop_waker();
1905 let mut cx = Context::from_waker(&waker);
1906
1907 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
1908 assert!(fetcher.queued_requests.is_empty());
1909 assert_eq!(rx.await.unwrap().unwrap_err(), RequestError::UnsupportedCapability);
1910 }
1911
1912 #[tokio::test]
1913 async fn test_optional_bal_request_waits_for_busy_eth71_peer() {
1914 use futures::task::noop_waker;
1915 use std::task::{Context, Poll};
1916
1917 let manager = PeersManager::new(PeersConfig::default());
1918 let mut fetcher =
1919 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1920
1921 let peer_71 = B512::random();
1922 let caps_71 = Arc::new(Capabilities::from(vec![Capability::new("eth".into(), 71)]));
1923 fetcher.new_active_peer(NewPeerInfo {
1924 peer_id: peer_71,
1925 best_hash: B256::random(),
1926 best_number: 100,
1927 capabilities: caps_71,
1928 timeout: Arc::new(AtomicU64::new(10)),
1929 range_info: None,
1930 supports_snap: false,
1931 });
1932 fetcher.peers.get_mut(&peer_71).expect("peer exists").state = PeerState::GetBlockHeaders;
1933
1934 let (tx, _rx) = oneshot::channel();
1935 fetcher
1936 .download_requests_tx
1937 .send(DownloadRequest::GetBlockAccessLists {
1938 request: vec![],
1939 response: tx,
1940 priority: Priority::Normal,
1941 requirement: BalRequirement::Optional,
1942 })
1943 .unwrap();
1944
1945 let waker = noop_waker();
1946 let mut cx = Context::from_waker(&waker);
1947
1948 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
1949 assert_eq!(fetcher.queued_requests.len(), 1);
1950 }
1951
1952 #[tokio::test]
1953 async fn test_queued_optional_bal_request_rejected_after_eth71_disconnect() {
1954 use futures::task::noop_waker;
1955 use std::task::{Context, Poll};
1956
1957 let manager = PeersManager::new(PeersConfig::default());
1958 let mut fetcher =
1959 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
1960
1961 let peer_old = B512::random();
1962 let caps_old = Arc::new(Capabilities::new(vec![]));
1963 fetcher.new_active_peer(NewPeerInfo {
1964 peer_id: peer_old,
1965 best_hash: B256::random(),
1966 best_number: 100,
1967 capabilities: caps_old,
1968 timeout: Arc::new(AtomicU64::new(10)),
1969 range_info: None,
1970 supports_snap: false,
1971 });
1972
1973 let peer_71 = B512::random();
1974 let caps_71 = Arc::new(Capabilities::from(vec![Capability::new("eth".into(), 71)]));
1975 fetcher.new_active_peer(NewPeerInfo {
1976 peer_id: peer_71,
1977 best_hash: B256::random(),
1978 best_number: 100,
1979 capabilities: caps_71,
1980 timeout: Arc::new(AtomicU64::new(10)),
1981 range_info: None,
1982 supports_snap: false,
1983 });
1984 fetcher.peers.get_mut(&peer_71).expect("peer exists").state = PeerState::GetBlockHeaders;
1985
1986 let (tx, rx) = oneshot::channel();
1987 fetcher
1988 .download_requests_tx
1989 .send(DownloadRequest::GetBlockAccessLists {
1990 request: vec![],
1991 response: tx,
1992 priority: Priority::Normal,
1993 requirement: BalRequirement::Optional,
1994 })
1995 .unwrap();
1996
1997 let waker = noop_waker();
1998 let mut cx = Context::from_waker(&waker);
1999
2000 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
2001 assert_eq!(fetcher.queued_requests.len(), 1);
2002
2003 fetcher.on_session_closed(&peer_71);
2004
2005 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
2006 assert!(fetcher.queued_requests.is_empty());
2007 assert_eq!(rx.await.unwrap().unwrap_err(), RequestError::UnsupportedCapability);
2008 }
2009
2010 #[tokio::test]
2011 async fn test_next_best_peer_snap_no_support() {
2012 let manager = PeersManager::new(PeersConfig::default());
2013 let mut fetcher =
2014 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
2015
2016 let peer = B512::random();
2017 fetcher.new_active_peer(NewPeerInfo {
2018 peer_id: peer,
2019 best_hash: B256::random(),
2020 best_number: 100,
2021 capabilities: Arc::new(Capabilities::new(vec![])),
2022 timeout: Arc::new(AtomicU64::new(10)),
2023 range_info: None,
2024 supports_snap: false,
2025 });
2026
2027 assert_eq!(fetcher.next_best_peer(BestPeerRequirements::SupportsSnap), None);
2028 }
2029
2030 #[tokio::test]
2031 async fn test_next_best_peer_snap_supported() {
2032 let manager = PeersManager::new(PeersConfig::default());
2033 let mut fetcher =
2034 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
2035
2036 let peer = B512::random();
2037 fetcher.new_active_peer(NewPeerInfo {
2038 peer_id: peer,
2039 best_hash: B256::random(),
2040 best_number: 100,
2041 capabilities: Arc::new(Capabilities::new(vec![])),
2042 timeout: Arc::new(AtomicU64::new(10)),
2043 range_info: None,
2044 supports_snap: true,
2045 });
2046
2047 assert_eq!(fetcher.next_best_peer(BestPeerRequirements::SupportsSnap), Some(peer));
2048 }
2049
2050 #[tokio::test]
2051 async fn test_next_best_peer_snap_filters_correctly() {
2052 let manager = PeersManager::new(PeersConfig::default());
2053 let mut fetcher =
2054 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
2055
2056 let peer_no_snap = B512::random();
2057 let peer_with_snap = B512::random();
2058
2059 fetcher.new_active_peer(NewPeerInfo {
2060 peer_id: peer_no_snap,
2061 best_hash: B256::random(),
2062 best_number: 100,
2063 capabilities: Arc::new(Capabilities::new(vec![])),
2064 timeout: Arc::new(AtomicU64::new(5)),
2065 range_info: None,
2066 supports_snap: false,
2067 });
2068 fetcher.new_active_peer(NewPeerInfo {
2069 peer_id: peer_with_snap,
2070 best_hash: B256::random(),
2071 best_number: 100,
2072 capabilities: Arc::new(Capabilities::new(vec![])),
2073 timeout: Arc::new(AtomicU64::new(50)),
2074 range_info: None,
2075 supports_snap: true,
2076 });
2077
2078 assert_eq!(
2080 fetcher.next_best_peer(BestPeerRequirements::SupportsSnap),
2081 Some(peer_with_snap)
2082 );
2083 }
2084
2085 #[tokio::test]
2086 async fn test_snap_request_rejected_without_snap_peer() {
2087 use futures::task::noop_waker;
2088 use std::task::{Context, Poll};
2089
2090 let manager = PeersManager::new(PeersConfig::default());
2091 let mut fetcher =
2092 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
2093
2094 fetcher.new_active_peer(NewPeerInfo {
2096 peer_id: B512::random(),
2097 best_hash: B256::random(),
2098 best_number: 100,
2099 capabilities: Arc::new(Capabilities::new(vec![])),
2100 timeout: Arc::new(AtomicU64::new(10)),
2101 range_info: None,
2102 supports_snap: false,
2103 });
2104
2105 let (tx, rx) = oneshot::channel();
2106 fetcher
2107 .download_requests_tx
2108 .send(DownloadRequest::GetSnap {
2109 request: SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
2110 request_id: 0,
2111 root_hash: B256::ZERO,
2112 starting_hash: B256::ZERO,
2113 limit_hash: B256::ZERO,
2114 response_bytes: 0,
2115 }),
2116 response: tx,
2117 priority: Priority::Normal,
2118 })
2119 .unwrap();
2120
2121 let waker = noop_waker();
2122 let mut cx = Context::from_waker(&waker);
2123
2124 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
2125 assert!(fetcher.queued_requests.is_empty());
2126 assert_eq!(rx.await.unwrap().unwrap_err(), RequestError::UnsupportedCapability);
2127 }
2128
2129 #[tokio::test]
2130 async fn test_snap_response_triggers_followup() {
2131 let manager = PeersManager::new(PeersConfig::default());
2132 let mut fetcher =
2133 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
2134
2135 let peer_id = B512::random();
2136 fetcher.new_active_peer(NewPeerInfo {
2137 peer_id,
2138 best_hash: B256::random(),
2139 best_number: 100,
2140 capabilities: Arc::new(Capabilities::new(vec![])),
2141 timeout: Arc::new(AtomicU64::new(10)),
2142 range_info: None,
2143 supports_snap: true,
2144 });
2145
2146 let (followup_tx, _followup_rx) = oneshot::channel();
2148 fetcher.queued_requests.push_back(DownloadRequest::GetSnap {
2149 request: SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
2150 request_id: 0,
2151 root_hash: B256::ZERO,
2152 starting_hash: B256::ZERO,
2153 limit_hash: B256::ZERO,
2154 response_bytes: 0,
2155 }),
2156 response: followup_tx,
2157 priority: Priority::Normal,
2158 });
2159
2160 let (tx, mut rx) = oneshot::channel();
2161 fetcher.inflight_snap_requests.insert(peer_id, Request { request: (), response: tx });
2162 fetcher.peers.get_mut(&peer_id).unwrap().state = PeerState::GetSnap;
2163
2164 let resp = SnapResponse::AccountRange(AccountRangeMessage {
2165 request_id: 1,
2166 accounts: vec![],
2167 proof: vec![],
2168 });
2169 let outcome = fetcher.on_snap_response(peer_id, Ok(resp));
2170
2171 assert!(matches!(outcome, Some(BlockResponseOutcome::Request(pid, _)) if pid == peer_id));
2172 assert!(rx.try_recv().is_ok());
2173 }
2174
2175 #[tokio::test]
2176 async fn test_queued_snap_request_rejected_after_last_peer_disconnects() {
2177 use futures::task::noop_waker;
2178 use std::task::{Context, Poll};
2179
2180 let manager = PeersManager::new(PeersConfig::default());
2181 let mut fetcher =
2182 StateFetcher::<EthNetworkPrimitives>::new(manager.handle(), Default::default());
2183
2184 let peer = B512::random();
2186 fetcher.new_active_peer(NewPeerInfo {
2187 peer_id: peer,
2188 best_hash: B256::random(),
2189 best_number: 100,
2190 capabilities: Arc::new(Capabilities::new(vec![])),
2191 timeout: Arc::new(AtomicU64::new(10)),
2192 range_info: None,
2193 supports_snap: true,
2194 });
2195 fetcher.peers.get_mut(&peer).expect("peer exists").state = PeerState::GetBlockHeaders;
2196
2197 let (tx, rx) = oneshot::channel();
2198 fetcher
2199 .download_requests_tx
2200 .send(DownloadRequest::GetSnap {
2201 request: SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
2202 request_id: 0,
2203 root_hash: B256::ZERO,
2204 starting_hash: B256::ZERO,
2205 limit_hash: B256::ZERO,
2206 response_bytes: 0,
2207 }),
2208 response: tx,
2209 priority: Priority::Normal,
2210 })
2211 .unwrap();
2212
2213 let waker = noop_waker();
2214 let mut cx = Context::from_waker(&waker);
2215
2216 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
2217 assert_eq!(fetcher.queued_requests.len(), 1);
2218
2219 fetcher.on_session_closed(&peer);
2222
2223 assert!(matches!(fetcher.poll(&mut cx), Poll::Pending));
2224 assert!(fetcher.queued_requests.is_empty());
2225 assert_eq!(rx.await.unwrap().unwrap_err(), RequestError::UnsupportedCapability);
2226 }
2227}