1use super::task::TaskDownloader;
4use crate::metrics::HeaderDownloaderMetrics;
5use alloy_consensus::BlockHeader;
6use alloy_eips::BlockHashOrNumber;
7use alloy_primitives::{BlockNumber, Sealable, B256};
8use futures::{stream::Stream, FutureExt};
9use futures_util::{stream::FuturesUnordered, StreamExt};
10use rayon::prelude::*;
11use reth_config::config::HeadersConfig;
12use reth_consensus::HeaderValidator;
13use reth_network_p2p::{
14 error::{DownloadError, DownloadResult, PeerRequestResult},
15 headers::{
16 client::{HeadersClient, HeadersRequest},
17 downloader::{validate_header_download, HeaderDownloader, SyncTarget},
18 error::{HeadersDownloaderError, HeadersDownloaderResult},
19 },
20 priority::Priority,
21};
22use reth_network_peers::PeerId;
23use reth_primitives_traits::{GotExpected, SealedHeader};
24use reth_tasks::Runtime;
25use std::{
26 cmp::{Ordering, Reverse},
27 collections::{binary_heap::PeekMut, BinaryHeap},
28 future::Future,
29 pin::Pin,
30 sync::Arc,
31 task::{ready, Context, Poll},
32};
33use thiserror::Error;
34use tracing::{debug, error, trace};
35
36const REQUESTS_PER_PEER_MULTIPLIER: usize = 5;
40
41#[derive(Error, Debug)]
43enum ReverseHeadersDownloaderError<H: Sealable> {
44 #[error(transparent)]
45 Downloader(#[from] HeadersDownloaderError<H>),
46 #[error(transparent)]
47 Response(#[from] Box<HeadersResponseError>),
48}
49
50impl<H: Sealable> From<HeadersResponseError> for ReverseHeadersDownloaderError<H> {
51 fn from(value: HeadersResponseError) -> Self {
52 Self::Response(Box::new(value))
53 }
54}
55
56#[must_use = "Stream does nothing unless polled"]
68#[derive(Debug)]
69pub struct ReverseHeadersDownloader<H: HeadersClient> {
70 consensus: Arc<dyn HeaderValidator<H::Header>>,
72 client: Arc<H>,
74 local_head: Option<SealedHeader<H::Header>>,
76 sync_target: Option<SyncTargetBlock>,
78 next_request_block_number: u64,
80 lowest_validated_header: Option<SealedHeader<H::Header>>,
82 next_chain_tip_block_number: u64,
84 request_limit: u64,
86 min_concurrent_requests: usize,
88 max_concurrent_requests: usize,
90 stream_batch_size: usize,
92 max_buffered_responses: usize,
94 sync_target_request: Option<HeadersRequestFuture<H::Output>>,
99 in_progress_queue: FuturesUnordered<HeadersRequestFuture<H::Output>>,
101 buffered_responses: BinaryHeap<OrderedHeadersResponse<H::Header>>,
103 queued_validated_headers: Vec<SealedHeader<H::Header>>,
107 metrics: HeaderDownloaderMetrics,
109}
110
111impl<H> ReverseHeadersDownloader<H>
114where
115 H: HeadersClient<Header: reth_primitives_traits::BlockHeader> + 'static,
116{
117 pub fn builder() -> ReverseHeadersDownloaderBuilder {
119 ReverseHeadersDownloaderBuilder::default()
120 }
121
122 #[inline]
124 fn local_block_number(&self) -> Option<BlockNumber> {
125 self.local_head.as_ref().map(|h| h.number())
126 }
127
128 #[inline]
134 fn existing_local_block_number(&self) -> BlockNumber {
135 self.local_head.as_ref().expect("is initialized").number()
136 }
137
138 #[inline]
144 fn existing_sync_target(&self) -> SyncTargetBlock {
145 self.sync_target.as_ref().expect("is initialized").clone()
146 }
147
148 #[inline]
153 fn concurrent_request_limit(&self) -> usize {
154 let num_peers = self.client.num_connected_peers();
155
156 let dynamic_target = num_peers * REQUESTS_PER_PEER_MULTIPLIER;
159 let max_dynamic = dynamic_target.max(self.min_concurrent_requests);
160
161 if num_peers < self.min_concurrent_requests {
163 return max_dynamic
164 }
165
166 max_dynamic.min(self.max_concurrent_requests)
167 }
168
169 fn next_request(&mut self) -> Option<HeadersRequest> {
175 if let Some(local_head) = self.local_block_number() &&
176 self.next_request_block_number > local_head
177 {
178 let request =
179 calc_next_request(local_head, self.next_request_block_number, self.request_limit);
180 self.next_request_block_number -= request.limit;
183
184 return Some(request)
185 }
186
187 None
188 }
189
190 fn lowest_validated_header(&self) -> Option<&SealedHeader<H::Header>> {
200 self.queued_validated_headers.last().or(self.lowest_validated_header.as_ref())
201 }
202
203 fn reset(&mut self) {
207 debug!(target: "downloaders::headers", "Resetting headers downloader");
208 self.next_request_block_number = 0;
209 self.next_chain_tip_block_number = 0;
210 self.sync_target.take();
211 }
212
213 fn validate_sync_target(
215 &self,
216 header: &SealedHeader<H::Header>,
217 request: HeadersRequest,
218 peer_id: PeerId,
219 ) -> Result<(), Box<HeadersResponseError>> {
220 match self.existing_sync_target() {
221 SyncTargetBlock::Hash(hash) | SyncTargetBlock::HashAndNumber { hash, .. }
222 if header.hash() != hash =>
223 {
224 Err(Box::new(HeadersResponseError {
225 request,
226 peer_id: Some(peer_id),
227 error: DownloadError::InvalidTip(
228 GotExpected { got: header.hash(), expected: hash }.into(),
229 ),
230 }))
231 }
232 SyncTargetBlock::Number(number) if header.number() != number => {
233 Err(Box::new(HeadersResponseError {
234 request,
235 peer_id: Some(peer_id),
236 error: DownloadError::InvalidTipNumber(GotExpected {
237 got: header.number(),
238 expected: number,
239 }),
240 }))
241 }
242 _ => Ok(()),
243 }
244 }
245
246 fn process_next_headers(
254 &mut self,
255 request: HeadersRequest,
256 headers: Vec<H::Header>,
257 peer_id: PeerId,
258 ) -> Result<(), ReverseHeadersDownloaderError<H::Header>> {
259 let mut validated = Vec::with_capacity(headers.len());
260
261 let sealed_headers =
262 headers.into_par_iter().map(SealedHeader::seal_slow).collect::<Vec<_>>();
263 for parent in sealed_headers {
264 if let Some(validated_header) =
266 validated.last().or_else(|| self.lowest_validated_header())
267 {
268 if let Err(error) = self.validate(validated_header, &parent) {
269 trace!(target: "downloaders::headers", %error ,"Failed to validate header");
270 return Err(
271 HeadersResponseError { request, peer_id: Some(peer_id), error }.into()
272 )
273 }
274 } else {
275 self.validate_sync_target(&parent, request.clone(), peer_id)?;
276 }
277
278 validated.push(parent);
279 }
280
281 if let Some(last_header) = validated.last() {
283 self.validate_attachment(last_header, request, peer_id)?;
284 }
285
286 self.next_chain_tip_block_number =
288 validated.last().expect("exists").number().saturating_sub(1);
289 self.queued_validated_headers.extend(validated);
290
291 Ok(())
292 }
293
294 fn validate_attachment(
298 &mut self,
299 header: &SealedHeader<H::Header>,
300 request: HeadersRequest,
301 peer_id: PeerId,
302 ) -> Result<(), ReverseHeadersDownloaderError<H::Header>> {
303 let Some(head) =
304 self.local_head.as_ref().filter(|head| header.number() == head.number() + 1)
305 else {
306 return Ok(())
307 };
308 if let Err(error) = self.consensus.validate_header(header) {
310 trace!(target: "downloaders::headers", %error, "Failed to validate header");
311 return Err(HeadersResponseError {
312 request,
313 peer_id: Some(peer_id),
314 error: DownloadError::HeaderValidation {
315 hash: head.hash(),
316 number: head.number(),
317 error: Box::new(error),
318 },
319 }
320 .into())
321 }
322
323 if let Err(error) = self.consensus.validate_header_against_parent(header, head) {
330 let local_head = head.clone();
331 error!(target: "downloaders::headers", %error, number = header.number(), hash = ?header.hash(), "Header cannot be attached to known canonical chain");
333
334 self.reset();
339
340 return Err(HeadersDownloaderError::DetachedHead {
341 local_head: Box::new(local_head),
342 header: Box::new(header.clone()),
343 error: Box::new(error),
344 }
345 .into())
346 }
347 Ok(())
348 }
349
350 fn on_block_number_update(&mut self, target_block_number: u64, next_block: u64) {
361 if let Some(old_target) =
363 self.sync_target.as_mut().and_then(|t| t.replace_number(target_block_number))
364 {
365 if target_block_number > old_target {
366 self.next_request_block_number = next_block;
369 self.next_chain_tip_block_number = next_block;
370 self.clear();
371 } else {
372 let skip = self
374 .queued_validated_headers
375 .iter()
376 .take_while(|last| last.number() > target_block_number)
377 .count();
378 self.queued_validated_headers.drain(..skip);
380 }
381 } else {
382 self.next_request_block_number = next_block;
384 self.next_chain_tip_block_number = next_block;
385 }
386 }
387
388 fn on_sync_target_outcome(
390 &mut self,
391 response: HeadersRequestOutcome<H::Header>,
392 ) -> Result<(), ReverseHeadersDownloaderError<H::Header>> {
393 let sync_target = self.existing_sync_target();
394 let HeadersRequestOutcome { request, outcome } = response;
395 match outcome {
396 Ok(res) => {
397 let (peer_id, mut headers) = res.split();
398
399 self.metrics.total_downloaded.increment(headers.len() as u64);
401
402 headers.sort_unstable_by_key(|h| Reverse(h.number()));
404
405 if headers.is_empty() {
406 return Err(HeadersResponseError {
407 request,
408 peer_id: Some(peer_id),
409 error: DownloadError::EmptyResponse,
410 }
411 .into())
412 }
413
414 let header = headers.swap_remove(0);
415 let target = SealedHeader::seal_slow(header);
416
417 match sync_target {
418 SyncTargetBlock::Hash(hash) | SyncTargetBlock::HashAndNumber { hash, .. } => {
419 if target.hash() != hash {
420 return Err(HeadersResponseError {
421 request,
422 peer_id: Some(peer_id),
423 error: DownloadError::InvalidTip(
424 GotExpected { got: target.hash(), expected: hash }.into(),
425 ),
426 }
427 .into())
428 }
429 }
430 SyncTargetBlock::Number(number) => {
431 if target.number() != number {
432 return Err(HeadersResponseError {
433 request,
434 peer_id: Some(peer_id),
435 error: DownloadError::InvalidTipNumber(GotExpected {
436 got: target.number(),
437 expected: number,
438 }),
439 }
440 .into())
441 }
442 }
443 }
444
445 trace!(target: "downloaders::headers", head=?self.local_block_number(), hash=?target.hash(), number=%target.number(), "Received sync target");
446
447 self.validate_attachment(&target, request, peer_id)?;
449
450 let parent_block_number = target.number().saturating_sub(1);
452 self.on_block_number_update(target.number(), parent_block_number);
453
454 self.queued_validated_headers.push(target);
455
456 self.try_validate_buffered()
458 .map(Err::<(), ReverseHeadersDownloaderError<H::Header>>)
459 .transpose()?;
460
461 Ok(())
462 }
463 Err(err) => {
464 Err(HeadersResponseError { request, peer_id: None, error: err.into() }.into())
465 }
466 }
467 }
468
469 fn on_headers_outcome(
471 &mut self,
472 response: HeadersRequestOutcome<H::Header>,
473 ) -> Result<(), ReverseHeadersDownloaderError<H::Header>> {
474 let requested_block_number = response.block_number();
475 let HeadersRequestOutcome { request, outcome } = response;
476
477 match outcome {
478 Ok(res) => {
479 let (peer_id, mut headers) = res.split();
480
481 self.metrics.total_downloaded.increment(headers.len() as u64);
483
484 trace!(target: "downloaders::headers", len=%headers.len(), "Received headers response");
485
486 if headers.is_empty() {
487 return Err(HeadersResponseError {
488 request,
489 peer_id: Some(peer_id),
490 error: DownloadError::EmptyResponse,
491 }
492 .into())
493 }
494
495 let received_headers = headers.len() as u64;
496 if received_headers > request.limit {
497 return Err(HeadersResponseError {
498 peer_id: Some(peer_id),
499 error: DownloadError::HeadersResponseTooLong(GotExpected {
500 got: received_headers,
501 expected: request.limit,
502 }),
503 request,
504 }
505 .into())
506 }
507 let missing_headers = request.limit - received_headers;
508
509 headers.sort_unstable_by_key(|h| Reverse(h.number()));
511
512 let highest = &headers[0];
514
515 trace!(target: "downloaders::headers", requested_block_number, highest=?highest.number(), "Validating non-empty headers response");
516
517 if highest.number() != requested_block_number {
518 return Err(HeadersResponseError {
519 request,
520 peer_id: Some(peer_id),
521 error: DownloadError::HeadersResponseStartBlockMismatch(GotExpected {
522 got: highest.number(),
523 expected: requested_block_number,
524 }),
525 }
526 .into())
527 }
528
529 if highest.number() == self.next_chain_tip_block_number {
531 self.process_next_headers(request, headers, peer_id)?;
533 self.requeue_missing_headers(
537 requested_block_number,
538 received_headers,
539 missing_headers,
540 );
541 self.try_validate_buffered()
543 .map(Err::<(), ReverseHeadersDownloaderError<H::Header>>)
544 .transpose()?;
545 } else if highest.number() > self.existing_local_block_number() {
546 self.metrics.buffered_responses.increment(1.);
547 self.buffered_responses.push(OrderedHeadersResponse {
549 headers,
550 request,
551 peer_id,
552 });
553 self.requeue_missing_headers(
554 requested_block_number,
555 received_headers,
556 missing_headers,
557 );
558 }
559
560 Ok(())
561 }
562 Err(err) => {
565 trace!(target: "downloaders::headers", %err, "Response error");
566 Err(HeadersResponseError { request, peer_id: None, error: err.into() }.into())
567 }
568 }
569 }
570
571 fn penalize_peer(&self, peer_id: Option<PeerId>, error: &DownloadError) {
572 if let Some(peer_id) = peer_id {
574 trace!(target: "downloaders::headers", ?peer_id, %error, "Penalizing peer");
575 self.client.report_bad_message(peer_id);
576 }
577 }
578
579 fn on_headers_error(&self, err: Box<HeadersResponseError>) {
583 let HeadersResponseError { request, peer_id, error } = *err;
584
585 self.penalize_peer(peer_id, &error);
586
587 self.metrics.increment_errors(&error);
589
590 self.submit_request(request, Priority::High);
592 }
593
594 fn try_validate_buffered(&mut self) -> Option<ReverseHeadersDownloaderError<H::Header>> {
598 loop {
599 let next_response = self.buffered_responses.peek_mut()?;
601 let next_block_number = next_response.block_number();
602 match next_block_number.cmp(&self.next_chain_tip_block_number) {
603 Ordering::Less => return None,
604 Ordering::Equal => {
605 let OrderedHeadersResponse { headers, request, peer_id } =
606 PeekMut::pop(next_response);
607 self.metrics.buffered_responses.decrement(1.);
608
609 if let Err(err) = self.process_next_headers(request, headers, peer_id) {
610 return Some(err)
611 }
612 }
613 Ordering::Greater => {
614 self.metrics.buffered_responses.decrement(1.);
615 PeekMut::pop(next_response);
616 }
617 }
618 }
619 }
620
621 const fn get_sync_target_request(&self, start: BlockHashOrNumber) -> HeadersRequest {
623 HeadersRequest::falling(start, 1)
624 }
625
626 fn submit_request(&self, request: HeadersRequest, priority: Priority) {
628 trace!(target: "downloaders::headers", ?request, "Submitting headers request");
629 self.in_progress_queue.push(self.request_fut(request, priority));
630 self.metrics.in_flight_requests.increment(1.);
631 }
632
633 fn requeue_missing_headers(
639 &self,
640 requested_block_number: u64,
641 received_headers: u64,
642 missing_headers: u64,
643 ) {
644 if missing_headers > 0 {
645 self.metrics.partial_responses.increment(1);
646 self.submit_request(
647 HeadersRequest::falling(
648 (requested_block_number - received_headers).into(),
649 missing_headers,
650 ),
651 Priority::High,
652 );
653 }
654 }
655
656 fn request_fut(
657 &self,
658 request: HeadersRequest,
659 priority: Priority,
660 ) -> HeadersRequestFuture<H::Output> {
661 let client = Arc::clone(&self.client);
662 HeadersRequestFuture {
663 request: Some(request.clone()),
664 fut: client.get_headers_with_priority(request, priority),
665 }
666 }
667
668 fn validate(
670 &self,
671 header: &SealedHeader<H::Header>,
672 parent: &SealedHeader<H::Header>,
673 ) -> DownloadResult<()> {
674 validate_header_download(&self.consensus, header, parent)
675 }
676
677 fn clear(&mut self) {
679 self.lowest_validated_header.take();
680 self.queued_validated_headers = Vec::new();
681 self.buffered_responses = BinaryHeap::new();
682 self.in_progress_queue.clear();
683
684 self.metrics.in_flight_requests.set(0.);
685 self.metrics.buffered_responses.set(0.);
686 }
687
688 fn split_next_batch(&mut self) -> Vec<SealedHeader<H::Header>> {
690 let batch_size = self.stream_batch_size.min(self.queued_validated_headers.len());
691 let mut rem = self.queued_validated_headers.split_off(batch_size);
692 std::mem::swap(&mut rem, &mut self.queued_validated_headers);
693 rem.shrink_to_fit();
709 rem
710 }
711}
712
713impl<H> ReverseHeadersDownloader<H>
714where
715 H: HeadersClient,
716 Self: HeaderDownloader + 'static,
717{
718 pub fn into_task_with(
720 self,
721 runtime: &Runtime,
722 ) -> TaskDownloader<<Self as HeaderDownloader>::Header> {
723 TaskDownloader::spawn_with(self, runtime)
724 }
725}
726
727impl<H> HeaderDownloader for ReverseHeadersDownloader<H>
728where
729 H: HeadersClient<Header: reth_primitives_traits::BlockHeader> + 'static,
730{
731 type Header = H::Header;
732
733 fn update_local_head(&mut self, head: SealedHeader<H::Header>) {
734 while self
736 .queued_validated_headers
737 .last()
738 .is_some_and(|last| last.number() <= head.number())
739 {
740 self.queued_validated_headers.pop();
742 }
743 trace!(
744 target: "downloaders::headers",
745 head=?head.num_hash(),
746 "Updating local head"
747 );
748 self.local_head = Some(head);
750 }
751
752 fn update_sync_target(&mut self, target: SyncTarget) {
754 let current_tip = self.sync_target.as_ref().and_then(|t| t.hash());
755 trace!(
756 target: "downloaders::headers",
757 sync_target=?target,
758 current_tip=?current_tip,
759 "Updating sync target"
760 );
761 match target {
762 SyncTarget::Tip(tip) => {
763 if Some(tip) != current_tip {
764 trace!(target: "downloaders::headers", current=?current_tip, new=?tip, "Update sync target");
765 let new_sync_target = SyncTargetBlock::from_hash(tip);
766
767 if let Some(target_number) = self
770 .queued_validated_headers
771 .first()
772 .filter(|h| h.hash() == tip)
773 .map(|h| h.number())
774 {
775 self.sync_target = Some(new_sync_target.with_number(target_number));
776 return
777 }
778
779 trace!(target: "downloaders::headers", new=?target, "Request new sync target");
780 self.metrics.out_of_order_requests.increment(1);
781 self.sync_target = Some(new_sync_target);
782 self.sync_target_request = Some(
783 self.request_fut(self.get_sync_target_request(tip.into()), Priority::High),
784 );
785 }
786 }
787 SyncTarget::Gap(existing) => {
788 let target = existing.parent;
789 if Some(target) != current_tip {
790 self.sync_target_request.take();
792 let parent_block_number = existing.block.number.saturating_sub(1);
795
796 trace!(target: "downloaders::headers", current=?current_tip, new=?target, %parent_block_number, "Updated sync target");
797
798 self.sync_target = match self.sync_target.take() {
800 Some(sync_target) => Some(sync_target.with_hash(target)),
801 None => Some(SyncTargetBlock::from_hash(target)),
802 };
803 self.on_block_number_update(parent_block_number, parent_block_number);
804 }
805 }
806 SyncTarget::TipNum(num) => {
807 let current_tip_num = self.sync_target.as_ref().and_then(|t| t.number());
808 if Some(num) != current_tip_num {
809 trace!(target: "downloaders::headers", %num, "Updating sync target based on num");
810 self.sync_target = Some(SyncTargetBlock::from_number(num));
812 self.sync_target_request = Some(
813 self.request_fut(self.get_sync_target_request(num.into()), Priority::High),
814 );
815 }
816 }
817 }
818 }
819
820 fn set_batch_size(&mut self, batch_size: usize) {
821 self.stream_batch_size = batch_size;
822 }
823}
824
825impl<H> Stream for ReverseHeadersDownloader<H>
826where
827 H: HeadersClient<Header: reth_primitives_traits::BlockHeader> + 'static,
828{
829 type Item = HeadersDownloaderResult<Vec<SealedHeader<H::Header>>, H::Header>;
830
831 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
832 let this = self.get_mut();
833
834 if this.local_head.is_none() || this.sync_target.is_none() {
837 trace!(
838 target: "downloaders::headers",
839 head=?this.local_block_number(),
840 sync_target=?this.sync_target,
841 "The downloader sync boundaries have not been set"
842 );
843 return Poll::Pending
844 }
845
846 while let Some(mut req) = this.sync_target_request.take() {
849 match req.poll_unpin(cx) {
850 Poll::Ready(outcome) => {
851 match this.on_sync_target_outcome(outcome) {
852 Ok(()) => break,
853 Err(ReverseHeadersDownloaderError::Response(error)) => {
854 trace!(target: "downloaders::headers", %error, "invalid sync target response");
855 if error.is_channel_closed() {
856 return Poll::Ready(None)
858 }
859
860 this.penalize_peer(error.peer_id, &error.error);
861 this.metrics.increment_errors(&error.error);
862 this.sync_target_request =
863 Some(this.request_fut(error.request, Priority::High));
864 }
865 Err(ReverseHeadersDownloaderError::Downloader(error)) => {
866 this.clear();
867 return Poll::Ready(Some(Err(error)))
868 }
869 };
870 }
871 Poll::Pending => {
872 this.sync_target_request = Some(req);
873 return Poll::Pending
874 }
875 }
876 }
877
878 this.buffered_responses.shrink_to_fit();
880
881 loop {
890 while let Poll::Ready(Some(outcome)) = this.in_progress_queue.poll_next_unpin(cx) {
892 this.metrics.in_flight_requests.decrement(1.);
893 match this.on_headers_outcome(outcome) {
895 Ok(()) => (),
896 Err(ReverseHeadersDownloaderError::Response(error)) => {
897 if error.is_channel_closed() {
898 return Poll::Ready(None)
900 }
901 this.on_headers_error(error);
902 }
903 Err(ReverseHeadersDownloaderError::Downloader(error)) => {
904 this.clear();
905 return Poll::Ready(Some(Err(error)))
906 }
907 };
908 }
909
910 this.buffered_responses.shrink_to_fit();
912
913 let mut progress = false;
915
916 let concurrent_request_limit = this.concurrent_request_limit();
917 while this.in_progress_queue.len() < concurrent_request_limit &&
919 this.buffered_responses.len() < this.max_buffered_responses
920 {
921 if let Some(request) = this.next_request() {
922 trace!(
923 target: "downloaders::headers",
924 "Requesting headers {request:?}"
925 );
926 progress = true;
927 this.submit_request(request, Priority::Normal);
928 } else {
929 break
931 }
932 }
933
934 if this.queued_validated_headers.len() >= this.stream_batch_size {
936 let next_batch = this.split_next_batch();
937
938 if this.queued_validated_headers.is_empty() {
941 this.lowest_validated_header = next_batch.last().cloned();
942 }
943
944 trace!(target: "downloaders::headers", batch=%next_batch.len(), "Returning validated batch");
945
946 this.metrics.total_flushed.increment(next_batch.len() as u64);
947 return Poll::Ready(Some(Ok(next_batch)))
948 }
949
950 if !progress {
951 break
952 }
953 }
954
955 if this.in_progress_queue.is_empty() {
957 let next_batch = this.split_next_batch();
958 if next_batch.is_empty() {
959 this.clear();
960 return Poll::Ready(None)
961 }
962 this.metrics.total_flushed.increment(next_batch.len() as u64);
963 return Poll::Ready(Some(Ok(next_batch)))
964 }
965
966 Poll::Pending
967 }
968}
969
970#[derive(Debug)]
972struct HeadersRequestFuture<F> {
973 request: Option<HeadersRequest>,
974 fut: F,
975}
976
977impl<F, H> Future for HeadersRequestFuture<F>
978where
979 F: Future<Output = PeerRequestResult<Vec<H>>> + Sync + Send + Unpin,
980{
981 type Output = HeadersRequestOutcome<H>;
982
983 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
984 let this = self.get_mut();
985 let outcome = ready!(this.fut.poll_unpin(cx));
986 let request = this.request.take().unwrap();
987
988 Poll::Ready(HeadersRequestOutcome { request, outcome })
989 }
990}
991
992struct HeadersRequestOutcome<H> {
994 request: HeadersRequest,
995 outcome: PeerRequestResult<Vec<H>>,
996}
997
998impl<H> HeadersRequestOutcome<H> {
1001 const fn block_number(&self) -> u64 {
1002 self.request.start.as_number().expect("is number")
1003 }
1004}
1005
1006#[derive(Debug)]
1008struct OrderedHeadersResponse<H> {
1009 headers: Vec<H>,
1010 request: HeadersRequest,
1011 peer_id: PeerId,
1012}
1013
1014impl<H> OrderedHeadersResponse<H> {
1017 const fn block_number(&self) -> u64 {
1018 self.request.start.as_number().expect("is number")
1019 }
1020}
1021
1022impl<H> PartialEq for OrderedHeadersResponse<H> {
1023 fn eq(&self, other: &Self) -> bool {
1024 self.block_number() == other.block_number()
1025 }
1026}
1027
1028impl<H> Eq for OrderedHeadersResponse<H> {}
1029
1030impl<H> PartialOrd for OrderedHeadersResponse<H> {
1031 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1032 Some(self.cmp(other))
1033 }
1034}
1035
1036impl<H> Ord for OrderedHeadersResponse<H> {
1037 fn cmp(&self, other: &Self) -> Ordering {
1038 self.block_number().cmp(&other.block_number())
1039 }
1040}
1041
1042#[derive(Debug, Error)]
1044#[error("error requesting headers from peer {peer_id:?}: {error}; request: {request:?}")]
1045struct HeadersResponseError {
1046 request: HeadersRequest,
1047 peer_id: Option<PeerId>,
1048 #[source]
1049 error: DownloadError,
1050}
1051
1052impl HeadersResponseError {
1053 const fn is_channel_closed(&self) -> bool {
1055 if let DownloadError::RequestError(ref err) = self.error {
1056 return err.is_channel_closed()
1057 }
1058 false
1059 }
1060}
1061
1062#[derive(Clone, Debug)]
1065pub enum SyncTargetBlock {
1066 Hash(B256),
1068 Number(u64),
1070 HashAndNumber {
1072 hash: B256,
1074 number: u64,
1076 },
1077}
1078
1079impl SyncTargetBlock {
1080 const fn from_hash(hash: B256) -> Self {
1082 Self::Hash(hash)
1083 }
1084
1085 const fn from_number(num: u64) -> Self {
1087 Self::Number(num)
1088 }
1089
1090 const fn with_hash(self, hash: B256) -> Self {
1092 match self {
1093 Self::Hash(_) => Self::Hash(hash),
1094 Self::Number(number) | Self::HashAndNumber { number, .. } => {
1095 Self::HashAndNumber { hash, number }
1096 }
1097 }
1098 }
1099
1100 const fn with_number(self, number: u64) -> Self {
1102 match self {
1103 Self::Hash(hash) | Self::HashAndNumber { hash, .. } => {
1104 Self::HashAndNumber { hash, number }
1105 }
1106 Self::Number(_) => Self::Number(number),
1107 }
1108 }
1109
1110 const fn replace_number(&mut self, number: u64) -> Option<u64> {
1115 match self {
1116 Self::Hash(hash) => {
1117 *self = Self::HashAndNumber { hash: *hash, number };
1118 None
1119 }
1120 Self::Number(old_number) => {
1121 let res = Some(*old_number);
1122 *self = Self::Number(number);
1123 res
1124 }
1125 Self::HashAndNumber { number: old_number, hash } => {
1126 let res = Some(*old_number);
1127 *self = Self::HashAndNumber { hash: *hash, number };
1128 res
1129 }
1130 }
1131 }
1132
1133 const fn hash(&self) -> Option<B256> {
1135 match self {
1136 Self::Hash(hash) | Self::HashAndNumber { hash, .. } => Some(*hash),
1137 Self::Number(_) => None,
1138 }
1139 }
1140
1141 const fn number(&self) -> Option<u64> {
1143 match self {
1144 Self::Hash(_) => None,
1145 Self::Number(number) | Self::HashAndNumber { number, .. } => Some(*number),
1146 }
1147 }
1148}
1149
1150#[derive(Debug)]
1153pub struct ReverseHeadersDownloaderBuilder {
1154 request_limit: u64,
1156 stream_batch_size: usize,
1158 min_concurrent_requests: usize,
1160 max_concurrent_requests: usize,
1162 max_buffered_responses: usize,
1164}
1165
1166impl ReverseHeadersDownloaderBuilder {
1167 pub fn new(config: HeadersConfig) -> Self {
1170 Self::default()
1171 .request_limit(config.downloader_request_limit)
1172 .min_concurrent_requests(config.downloader_min_concurrent_requests)
1173 .max_concurrent_requests(config.downloader_max_concurrent_requests)
1174 .max_buffered_responses(config.downloader_max_buffered_responses)
1175 .stream_batch_size(config.commit_threshold as usize)
1176 }
1177}
1178
1179impl Default for ReverseHeadersDownloaderBuilder {
1180 fn default() -> Self {
1181 Self {
1182 stream_batch_size: 10_000,
1183 request_limit: 1_000,
1186 max_concurrent_requests: 100,
1187 min_concurrent_requests: 5,
1188 max_buffered_responses: 100,
1189 }
1190 }
1191}
1192
1193impl ReverseHeadersDownloaderBuilder {
1194 pub const fn request_limit(mut self, limit: u64) -> Self {
1199 self.request_limit = limit;
1200 self
1201 }
1202
1203 pub const fn stream_batch_size(mut self, size: usize) -> Self {
1209 self.stream_batch_size = size;
1210 self
1211 }
1212
1213 pub const fn min_concurrent_requests(mut self, min_concurrent_requests: usize) -> Self {
1218 self.min_concurrent_requests = min_concurrent_requests;
1219 self
1220 }
1221
1222 pub const fn max_concurrent_requests(mut self, max_concurrent_requests: usize) -> Self {
1226 self.max_concurrent_requests = max_concurrent_requests;
1227 self
1228 }
1229
1230 pub const fn max_buffered_responses(mut self, max_buffered_responses: usize) -> Self {
1237 self.max_buffered_responses = max_buffered_responses;
1238 self
1239 }
1240
1241 pub fn build<H>(
1244 self,
1245 client: H,
1246 consensus: Arc<dyn HeaderValidator<H::Header>>,
1247 ) -> ReverseHeadersDownloader<H>
1248 where
1249 H: HeadersClient + 'static,
1250 {
1251 let Self {
1252 request_limit,
1253 stream_batch_size,
1254 min_concurrent_requests,
1255 max_concurrent_requests,
1256 max_buffered_responses,
1257 } = self;
1258 ReverseHeadersDownloader {
1259 consensus,
1260 client: Arc::new(client),
1261 local_head: None,
1262 sync_target: None,
1263 next_request_block_number: 0,
1266 next_chain_tip_block_number: 0,
1267 lowest_validated_header: None,
1268 request_limit,
1269 min_concurrent_requests,
1270 max_concurrent_requests,
1271 stream_batch_size,
1272 max_buffered_responses,
1273 sync_target_request: None,
1274 in_progress_queue: Default::default(),
1275 buffered_responses: Default::default(),
1276 queued_validated_headers: Default::default(),
1277 metrics: Default::default(),
1278 }
1279 }
1280}
1281
1282#[inline]
1289fn calc_next_request(
1290 local_head: u64,
1291 next_request_block_number: u64,
1292 request_limit: u64,
1293) -> HeadersRequest {
1294 let diff = next_request_block_number - local_head;
1296 let limit = diff.min(request_limit);
1297 let start = next_request_block_number;
1298 HeadersRequest::falling(start.into(), limit)
1299}
1300
1301#[cfg(test)]
1302mod tests {
1303 use super::*;
1304 use crate::headers::test_utils::child_header;
1305 use alloy_consensus::Header;
1306 use alloy_eips::{eip1898::BlockWithParent, BlockNumHash};
1307 use assert_matches::assert_matches;
1308 use reth_consensus::test_utils::TestConsensus;
1309 use reth_network_p2p::{
1310 download::DownloadClient, error::PeerRequestResult, test_utils::TestHeadersClient,
1311 };
1312 use reth_network_peers::WithPeerId;
1313 use std::sync::{
1314 atomic::{AtomicU64, Ordering as AtomicOrdering},
1315 Mutex,
1316 };
1317
1318 #[derive(Clone, Debug)]
1319 struct CappedHeadersClient {
1320 responses: Arc<Mutex<Vec<Header>>>,
1321 requests: Arc<Mutex<Vec<HeadersRequest>>>,
1322 bad_messages: Arc<AtomicU64>,
1323 response_limit: usize,
1324 }
1325
1326 impl CappedHeadersClient {
1327 fn new(responses: Vec<Header>, response_limit: usize) -> Self {
1328 Self {
1329 responses: Arc::new(Mutex::new(responses)),
1330 requests: Default::default(),
1331 bad_messages: Default::default(),
1332 response_limit,
1333 }
1334 }
1335
1336 fn numeric_requests(&self) -> Vec<(u64, u64)> {
1337 self.requests
1338 .lock()
1339 .unwrap()
1340 .iter()
1341 .filter_map(|request| request.start.as_number().map(|start| (start, request.limit)))
1342 .collect()
1343 }
1344
1345 fn bad_message_count(&self) -> u64 {
1346 self.bad_messages.load(AtomicOrdering::SeqCst)
1347 }
1348 }
1349
1350 impl DownloadClient for CappedHeadersClient {
1351 fn report_bad_message(&self, _peer_id: PeerId) {
1352 self.bad_messages.fetch_add(1, AtomicOrdering::SeqCst);
1353 }
1354
1355 fn num_connected_peers(&self) -> usize {
1356 1
1357 }
1358 }
1359
1360 impl HeadersClient for CappedHeadersClient {
1361 type Header = Header;
1362 type Output = futures::future::Ready<PeerRequestResult<Vec<Header>>>;
1363
1364 fn get_headers_with_priority(
1365 &self,
1366 request: HeadersRequest,
1367 _priority: Priority,
1368 ) -> Self::Output {
1369 self.requests.lock().unwrap().push(request.clone());
1370 let mut responses = self.responses.lock().unwrap();
1371 let response_length = request.limit.min(self.response_limit as u64) as usize;
1372 let response = responses.drain(..response_length).collect();
1373
1374 futures::future::ready(Ok(WithPeerId::new(PeerId::default(), response)))
1375 }
1376 }
1377
1378 #[test]
1380 fn test_replace_number_semantics() {
1381 struct Fixture {
1382 sync_target_block: SyncTargetBlock,
1384 sync_target_option: Option<u64>,
1385
1386 replace_number: u64,
1388
1389 expected_result: Option<u64>,
1391
1392 new_number: u64,
1394 }
1395
1396 let fixtures = vec![
1397 Fixture {
1398 sync_target_block: SyncTargetBlock::Hash(B256::random()),
1399 sync_target_option: None,
1401 replace_number: 1,
1402 expected_result: None,
1403 new_number: 1,
1404 },
1405 Fixture {
1406 sync_target_block: SyncTargetBlock::Number(1),
1407 sync_target_option: Some(1),
1408 replace_number: 2,
1409 expected_result: Some(1),
1410 new_number: 2,
1411 },
1412 Fixture {
1413 sync_target_block: SyncTargetBlock::HashAndNumber {
1414 hash: B256::random(),
1415 number: 1,
1416 },
1417 sync_target_option: Some(1),
1418 replace_number: 2,
1419 expected_result: Some(1),
1420 new_number: 2,
1421 },
1422 ];
1423
1424 for fixture in fixtures {
1425 let mut sync_target_block = fixture.sync_target_block;
1426 let result = sync_target_block.replace_number(fixture.replace_number);
1427 assert_eq!(result, fixture.expected_result);
1428 assert_eq!(sync_target_block.number(), Some(fixture.new_number));
1429
1430 let mut sync_target_option = fixture.sync_target_option;
1431 let option_result = sync_target_option.replace(fixture.replace_number);
1432 assert_eq!(option_result, fixture.expected_result);
1433 assert_eq!(sync_target_option, Some(fixture.new_number));
1434 }
1435 }
1436
1437 #[test]
1439 fn test_sync_target_update() {
1440 let client = Arc::new(TestHeadersClient::default());
1441
1442 let genesis = SealedHeader::default();
1443
1444 let mut downloader = ReverseHeadersDownloaderBuilder::default()
1445 .build(Arc::clone(&client), Arc::new(TestConsensus::default()));
1446 downloader.update_local_head(genesis);
1447 downloader.update_sync_target(SyncTarget::Tip(B256::random()));
1448
1449 downloader.sync_target_request.take();
1450
1451 let target = SyncTarget::Tip(B256::random());
1452 downloader.update_sync_target(target);
1453 assert!(downloader.sync_target_request.is_some());
1454
1455 downloader.sync_target_request.take();
1456 let target = SyncTarget::Gap(BlockWithParent {
1457 block: BlockNumHash::new(0, B256::random()),
1458 parent: Default::default(),
1459 });
1460 downloader.update_sync_target(target);
1461 assert!(downloader.sync_target_request.is_none());
1462 assert_matches!(
1463 downloader.sync_target,
1464 Some(target) => target.number().is_some()
1465 );
1466 }
1467
1468 #[test]
1470 fn test_head_update() {
1471 let client = Arc::new(TestHeadersClient::default());
1472
1473 let header: SealedHeader = SealedHeader::default();
1474
1475 let mut downloader = ReverseHeadersDownloaderBuilder::default()
1476 .build(Arc::clone(&client), Arc::new(TestConsensus::default()));
1477 downloader.update_local_head(header.clone());
1478 downloader.update_sync_target(SyncTarget::Tip(B256::random()));
1479
1480 downloader.queued_validated_headers.push(header.clone());
1481 let mut next = header.as_ref().clone();
1482 next.number += 1;
1483 downloader.update_local_head(SealedHeader::new(next, B256::random()));
1484 assert!(downloader.queued_validated_headers.is_empty());
1485 }
1486
1487 #[test]
1488 fn test_request_calc() {
1489 let local = 0;
1491 let next = 1000;
1492 let batch_size = 2;
1493 let request = calc_next_request(local, next, batch_size);
1494 assert_eq!(request.start, next.into());
1495 assert_eq!(request.limit, batch_size);
1496
1497 let local = 999;
1499 let next = 1000;
1500 let batch_size = 2;
1501 let request = calc_next_request(local, next, batch_size);
1502 assert_eq!(request.start, next.into());
1503 assert_eq!(request.limit, 1);
1504 }
1505
1506 #[test]
1507 fn default_request_limit_matches_sync_config() {
1508 assert_eq!(
1509 ReverseHeadersDownloaderBuilder::default().request_limit,
1510 HeadersConfig::default().downloader_request_limit
1511 );
1512 }
1513
1514 #[test]
1516 fn test_next_request() {
1517 let client = Arc::new(TestHeadersClient::default());
1518
1519 let genesis = SealedHeader::default();
1520
1521 let batch_size = 99;
1522 let start = 1000;
1523 let mut downloader = ReverseHeadersDownloaderBuilder::default()
1524 .request_limit(batch_size)
1525 .build(Arc::clone(&client), Arc::new(TestConsensus::default()));
1526 downloader.update_local_head(genesis);
1527 downloader.update_sync_target(SyncTarget::Tip(B256::random()));
1528
1529 downloader.next_request_block_number = start;
1530
1531 let mut total = 0;
1532 while let Some(req) = downloader.next_request() {
1533 assert_eq!(req.start, (start - total).into());
1534 total += req.limit;
1535 }
1536 assert_eq!(total, start);
1537 assert_eq!(Some(downloader.next_request_block_number), downloader.local_block_number());
1538 }
1539
1540 #[test]
1541 fn test_resp_order() {
1542 let mut heap = BinaryHeap::new();
1543 let hi = 1u64;
1544 heap.push(OrderedHeadersResponse::<Header> {
1545 headers: vec![],
1546 request: HeadersRequest { start: hi.into(), limit: 0, direction: Default::default() },
1547 peer_id: Default::default(),
1548 });
1549
1550 let lo = 0u64;
1551 heap.push(OrderedHeadersResponse {
1552 headers: vec![],
1553 request: HeadersRequest { start: lo.into(), limit: 0, direction: Default::default() },
1554 peer_id: Default::default(),
1555 });
1556
1557 assert_eq!(heap.pop().unwrap().block_number(), hi);
1558 assert_eq!(heap.pop().unwrap().block_number(), lo);
1559 }
1560
1561 #[tokio::test]
1562 async fn rejects_a_detached_sync_target_above_the_local_head() {
1563 let head = SealedHeader::<Header>::default();
1564 let target = SealedHeader::seal_slow(Header {
1565 number: 1,
1566 parent_hash: B256::repeat_byte(1),
1567 ..Default::default()
1568 });
1569 let client = Arc::new(TestHeadersClient::default());
1570 client.extend(vec![target.as_ref().clone()]).await;
1571 let consensus = TestConsensus::default();
1572 consensus.set_fail_header_against_parent(true);
1573 let mut downloader = ReverseHeadersDownloaderBuilder::default()
1574 .build(Arc::clone(&client), Arc::new(consensus));
1575 downloader.update_local_head(head.clone());
1576 downloader.update_sync_target(SyncTarget::Tip(target.hash()));
1577
1578 let error = downloader.next().await.unwrap().unwrap_err();
1579
1580 assert_matches!(
1581 error,
1582 HeadersDownloaderError::DetachedHead { local_head, header, .. }
1583 if *local_head == head && *header == target
1584 );
1585 }
1586
1587 #[tokio::test]
1588 async fn download_at_fork_head() {
1589 reth_tracing::init_test_tracing();
1590
1591 let client = Arc::new(TestHeadersClient::default());
1592
1593 let p3 = SealedHeader::default();
1594 let p2 = child_header(&p3);
1595 let p1 = child_header(&p2);
1596 let p0 = child_header(&p1);
1597
1598 let mut downloader = ReverseHeadersDownloaderBuilder::default()
1599 .stream_batch_size(3)
1600 .request_limit(3)
1601 .build(Arc::clone(&client), Arc::new(TestConsensus::default()));
1602 downloader.update_local_head(p3.clone());
1603 downloader.update_sync_target(SyncTarget::Tip(p0.hash()));
1604
1605 client
1606 .extend(vec![
1607 p0.as_ref().clone(),
1608 p1.as_ref().clone(),
1609 p2.as_ref().clone(),
1610 p3.as_ref().clone(),
1611 ])
1612 .await;
1613
1614 let headers = downloader.next().await.unwrap();
1615 assert_eq!(headers.unwrap(), vec![p0, p1, p2,]);
1616 assert!(downloader.buffered_responses.is_empty());
1617 assert!(downloader.next().await.is_none());
1618 assert!(downloader.next().await.is_none());
1619 }
1620
1621 #[tokio::test]
1622 async fn download_one_by_one() {
1623 reth_tracing::init_test_tracing();
1624 let p3 = SealedHeader::default();
1625 let p2 = child_header(&p3);
1626 let p1 = child_header(&p2);
1627 let p0 = child_header(&p1);
1628
1629 let client = Arc::new(TestHeadersClient::default());
1630 let mut downloader = ReverseHeadersDownloaderBuilder::default()
1631 .stream_batch_size(1)
1632 .request_limit(1)
1633 .build(Arc::clone(&client), Arc::new(TestConsensus::default()));
1634 downloader.update_local_head(p3.clone());
1635 downloader.update_sync_target(SyncTarget::Tip(p0.hash()));
1636
1637 client
1638 .extend(vec![
1639 p0.as_ref().clone(),
1640 p1.as_ref().clone(),
1641 p2.as_ref().clone(),
1642 p3.as_ref().clone(),
1643 ])
1644 .await;
1645
1646 let headers = downloader.next().await.unwrap();
1647 let headers = headers.unwrap();
1648 assert_eq!(headers, vec![p0]);
1649 assert_eq!(headers.capacity(), headers.len());
1650
1651 let headers = downloader.next().await.unwrap();
1652 let headers = headers.unwrap();
1653 assert_eq!(headers, vec![p1]);
1654 assert_eq!(headers.capacity(), headers.len());
1655
1656 let headers = downloader.next().await.unwrap();
1657 let headers = headers.unwrap();
1658 assert_eq!(headers, vec![p2]);
1659 assert_eq!(headers.capacity(), headers.len());
1660
1661 assert!(downloader.next().await.is_none());
1662 }
1663
1664 #[tokio::test]
1665 async fn download_one_by_one_larger_request_limit() {
1666 reth_tracing::init_test_tracing();
1667 let p3 = SealedHeader::default();
1668 let p2 = child_header(&p3);
1669 let p1 = child_header(&p2);
1670 let p0 = child_header(&p1);
1671
1672 let client = Arc::new(TestHeadersClient::default());
1673 let mut downloader = ReverseHeadersDownloaderBuilder::default()
1674 .stream_batch_size(1)
1675 .request_limit(3)
1676 .build(Arc::clone(&client), Arc::new(TestConsensus::default()));
1677 downloader.update_local_head(p3.clone());
1678 downloader.update_sync_target(SyncTarget::Tip(p0.hash()));
1679
1680 client
1681 .extend(vec![
1682 p0.as_ref().clone(),
1683 p1.as_ref().clone(),
1684 p2.as_ref().clone(),
1685 p3.as_ref().clone(),
1686 ])
1687 .await;
1688
1689 let headers = downloader.next().await.unwrap();
1690 let headers = headers.unwrap();
1691 assert_eq!(headers, vec![p0]);
1692 assert_eq!(headers.capacity(), headers.len());
1693
1694 let headers = downloader.next().await.unwrap();
1695 let headers = headers.unwrap();
1696 assert_eq!(headers, vec![p1]);
1697 assert_eq!(headers.capacity(), headers.len());
1698
1699 let headers = downloader.next().await.unwrap();
1700 let headers = headers.unwrap();
1701 assert_eq!(headers, vec![p2]);
1702 assert_eq!(headers.capacity(), headers.len());
1703
1704 assert!(downloader.next().await.is_none());
1705 }
1706
1707 #[tokio::test]
1708 async fn downloads_capped_header_responses() {
1709 let p3 = SealedHeader::default();
1710 let p2 = child_header(&p3);
1711 let p1 = child_header(&p2);
1712 let p0 = child_header(&p1);
1713
1714 let client = CappedHeadersClient::new(
1715 vec![p0.as_ref().clone(), p1.as_ref().clone(), p2.as_ref().clone()],
1716 1,
1717 );
1718 let mut downloader = ReverseHeadersDownloaderBuilder::default()
1719 .stream_batch_size(1)
1720 .request_limit(2)
1721 .min_concurrent_requests(1)
1722 .max_concurrent_requests(1)
1723 .build(client.clone(), Arc::new(TestConsensus::default()));
1724 downloader.update_local_head(p3);
1725 downloader.update_sync_target(SyncTarget::Tip(p0.hash()));
1726
1727 assert_eq!(downloader.next().await.unwrap().unwrap(), vec![p0]);
1728 assert_eq!(downloader.next().await.unwrap().unwrap(), vec![p1]);
1729 assert_eq!(downloader.next().await.unwrap().unwrap(), vec![p2]);
1730 assert!(downloader.next().await.is_none());
1731
1732 assert_eq!(client.numeric_requests(), vec![(2, 2), (1, 1)]);
1733 assert_eq!(client.bad_message_count(), 0);
1734 }
1735
1736 #[test]
1737 fn requeues_missing_headers_for_buffered_partial_response() {
1738 let client = CappedHeadersClient::new(Vec::new(), 0);
1739 let mut downloader = ReverseHeadersDownloaderBuilder::default()
1740 .build(client.clone(), Arc::new(TestConsensus::default()));
1741 downloader.local_head = Some(SealedHeader::default());
1742 downloader.sync_target = Some(SyncTargetBlock::from_number(10));
1743 downloader.next_chain_tip_block_number = 10;
1744
1745 let request = HeadersRequest::falling(5u64.into(), 3);
1748 let response = vec![Header { number: 5, ..Default::default() }];
1749 let outcome = downloader.on_headers_outcome(HeadersRequestOutcome {
1750 request,
1751 outcome: Ok(WithPeerId::new(PeerId::default(), response)),
1752 });
1753
1754 assert!(outcome.is_ok());
1755 assert_eq!(downloader.buffered_responses.len(), 1);
1756 assert_eq!(client.numeric_requests(), vec![(4, 2)]);
1757 assert_eq!(client.bad_message_count(), 0);
1758 }
1759
1760 #[test]
1761 fn rejects_over_long_headers_response() {
1762 let client = CappedHeadersClient::new(Vec::new(), 0);
1763 let mut downloader = ReverseHeadersDownloaderBuilder::default()
1764 .build(client.clone(), Arc::new(TestConsensus::default()));
1765
1766 let request = HeadersRequest::falling(5u64.into(), 1);
1767 let response = vec![
1768 Header { number: 5, ..Default::default() },
1769 Header { number: 4, ..Default::default() },
1770 ];
1771 let outcome = downloader.on_headers_outcome(HeadersRequestOutcome {
1772 request,
1773 outcome: Ok(WithPeerId::new(PeerId::default(), response)),
1774 });
1775
1776 assert_matches!(
1777 outcome,
1778 Err(ReverseHeadersDownloaderError::Response(err))
1779 if matches!(err.error, DownloadError::HeadersResponseTooLong(_))
1780 );
1781 assert!(client.numeric_requests().is_empty());
1782 }
1783}