1use super::headers::client::HeadersRequest;
2use crate::{
3 block_access_lists::client::{BalRequirement, BlockAccessListsClient},
4 bodies::client::{BodiesClient, SingleBodyRequest},
5 download::DownloadClient,
6 error::PeerRequestResult,
7 headers::client::{HeadersClient, SingleHeaderRequest},
8 priority::Priority,
9 BlockClient,
10};
11use alloy_consensus::BlockHeader;
12use alloy_eip7928::bal::RawBal;
13use alloy_primitives::{Bytes, Sealable, B256};
14use core::marker::PhantomData;
15use futures::FutureExt;
16use reth_consensus::Consensus;
17use reth_eth_wire_types::{
18 BlockAccessLists, EthNetworkPrimitives, HeadersDirection, NetworkPrimitives,
19};
20use reth_network_peers::{PeerId, WithPeerId};
21use reth_primitives_traits::{Block, SealedBlock, SealedBlockWith, SealedHeader};
22use std::{
23 cmp::Reverse,
24 collections::{HashMap, VecDeque},
25 fmt::Debug,
26 hash::Hash,
27 ops::RangeInclusive,
28 pin::Pin,
29 sync::Arc,
30 task::{ready, Context, Poll},
31};
32use tracing::{debug, trace};
33
34pub type SealedBlockWithAccessList<B> = SealedBlockWith<B, Option<RawBal>>;
36
37#[derive(Debug, Clone)]
39pub struct FullBlockClient<Client>
40where
41 Client: BlockClient,
42{
43 client: Client,
44 consensus: Arc<dyn Consensus<Client::Block>>,
45}
46
47impl<Client> FullBlockClient<Client>
48where
49 Client: BlockClient,
50{
51 pub fn new(client: Client, consensus: Arc<dyn Consensus<Client::Block>>) -> Self {
53 Self { client, consensus }
54 }
55
56 #[cfg(any(test, feature = "test-utils"))]
58 pub fn test_client(client: Client) -> Self {
59 Self::new(client, Arc::new(reth_consensus::test_utils::TestConsensus::default()))
60 }
61}
62
63impl<Client> FullBlockClient<Client>
64where
65 Client: BlockClient,
66{
67 pub fn get_full_block(&self, hash: B256) -> FetchFullBlockFuture<Client> {
74 FetchFullBlockFuture::new(self.client.clone(), self.consensus.clone(), hash)
75 }
76
77 pub fn get_full_block_range(
87 &self,
88 hash: B256,
89 count: u64,
90 ) -> FetchFullBlockRangeFuture<Client> {
91 let client = self.client.clone();
92 FetchFullBlockRangeFuture {
93 start_hash: hash,
94 count,
95 request: FullBlockRangeRequest {
96 headers: Some(client.get_headers(HeadersRequest::falling(hash.into(), count))),
97 bodies: None,
98 },
99 client,
100 headers: None,
101 pending_headers: VecDeque::new(),
102 bodies: HashMap::default(),
103 consensus: Arc::clone(&self.consensus),
104 }
105 }
106}
107
108impl<Client> FullBlockClient<Client>
109where
110 Client: BlockClient + BlockAccessListsClient,
111{
112 pub fn get_full_block_with_access_lists(
120 &self,
121 hash: B256,
122 ) -> FetchFullBlockWithBalFuture<Client> {
123 self.get_full_block_with_access_lists_with_requirement(hash, BalRequirement::default())
124 }
125
126 pub fn get_full_block_with_access_lists_with_requirement(
134 &self,
135 hash: B256,
136 requirement: BalRequirement,
137 ) -> FetchFullBlockWithBalFuture<Client> {
138 let client = self.client.clone();
139 FetchFullBlockWithBalFuture {
140 block: FetchFullBlockFuture::new(client.clone(), self.consensus.clone(), hash),
141 block_result: None,
142 bal_request_state: BalRequestState::Pending(
143 client.get_block_access_lists_with_requirement(vec![hash], requirement),
144 ),
145 }
146 }
147
148 pub fn get_full_block_range_with_optional_access_lists(
155 &self,
156 hash: B256,
157 count: u64,
158 ) -> FetchFullBlockRangeWithBalFuture<Client> {
159 self.get_full_block_range_with_optional_access_lists_with_requirement(
160 hash,
161 count,
162 BalRequirement::default(),
163 )
164 }
165
166 pub fn get_full_block_range_with_optional_access_lists_with_requirement(
173 &self,
174 hash: B256,
175 count: u64,
176 requirement: BalRequirement,
177 ) -> FetchFullBlockRangeWithBalFuture<Client> {
178 let client = self.client.clone();
179 FetchFullBlockRangeWithBalFuture {
180 blocks: self.get_full_block_range(hash, count),
181 client,
182 block_result: None,
183 access_lists: OptionalBlockAccessListsState::WaitingForBlocks { requirement },
184 }
185 }
186}
187
188#[must_use = "futures do nothing unless polled"]
193pub struct FetchFullBlockFuture<Client>
194where
195 Client: BlockClient,
196{
197 client: Client,
198 consensus: Arc<dyn Consensus<Client::Block>>,
199 hash: B256,
200 request: FullBlockRequest<Client>,
201 header: Option<SealedHeader<Client::Header>>,
202 body: Option<BodyResponse<Client::Body>>,
203}
204
205impl<Client> FetchFullBlockFuture<Client>
206where
207 Client: BlockClient,
208{
209 fn new(client: Client, consensus: Arc<dyn Consensus<Client::Block>>, hash: B256) -> Self {
210 Self {
211 hash,
212 consensus,
213 request: FullBlockRequest {
214 header: Some(client.get_header(hash.into())),
215 body: Some(client.get_block_body(hash)),
216 },
217 client,
218 header: None,
219 body: None,
220 }
221 }
222
223 pub const fn hash(&self) -> &B256 {
225 &self.hash
226 }
227
228 pub fn block_number(&self) -> Option<u64> {
230 self.header.as_ref().map(|h| h.number())
231 }
232
233 fn take_block(&mut self) -> Option<SealedBlock<Client::Block>> {
235 if self.header.is_none() || self.body.is_none() {
236 return None
237 }
238
239 let header = self.header.take().unwrap();
240 let resp = self.body.take().unwrap();
241 match resp {
242 BodyResponse::Validated(body) => Some(SealedBlock::from_sealed_parts(header, body)),
243 BodyResponse::PendingValidation(resp) => {
244 if let Err(err) = self.consensus.validate_body_against_header(resp.data(), &header)
246 {
247 debug!(target: "downloaders", %err, hash=?header.hash(), "Received wrong body");
248 self.client.report_bad_message(resp.peer_id());
249 self.header = Some(header);
250 self.request.body = Some(self.client.get_block_body(self.hash));
251 return None
252 }
253 Some(SealedBlock::from_sealed_parts(header, resp.into_data()))
254 }
255 }
256 }
257
258 fn on_block_response(&mut self, resp: WithPeerId<Client::Body>) {
259 if let Some(ref header) = self.header {
260 if let Err(err) = self.consensus.validate_body_against_header(resp.data(), header) {
261 debug!(target: "downloaders", %err, hash=?header.hash(), "Received wrong body");
262 self.client.report_bad_message(resp.peer_id());
263 return
264 }
265 self.body = Some(BodyResponse::Validated(resp.into_data()));
266 return
267 }
268 self.body = Some(BodyResponse::PendingValidation(resp));
269 }
270}
271
272impl<Client> Future for FetchFullBlockFuture<Client>
273where
274 Client: BlockClient<Header: BlockHeader + Sealable> + 'static,
275{
276 type Output = SealedBlock<Client::Block>;
277
278 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
279 let this = self.get_mut();
280
281 let mut budget = 4;
283
284 loop {
285 match ready!(this.request.poll(cx)) {
286 ResponseResult::Header(res) => {
287 match res {
288 Ok(maybe_header) => {
289 let (peer, maybe_header) =
290 maybe_header.map(|h| h.map(SealedHeader::seal_slow)).split();
291 if let Some(header) = maybe_header {
292 if header.hash() == this.hash {
293 this.header = Some(header);
294 } else {
295 debug!(target: "downloaders", expected=?this.hash, received=?header.hash(), "Received wrong header");
296 this.client.report_bad_message(peer)
298 }
299 }
300 }
301 Err(err) => {
302 debug!(target: "downloaders", %err, ?this.hash, "Header download failed");
303 }
304 }
305
306 if this.header.is_none() {
307 this.request.header = Some(this.client.get_header(this.hash.into()));
309 }
310 }
311 ResponseResult::Body(res) => {
312 match res {
313 Ok(maybe_body) => {
314 if let Some(body) = maybe_body.transpose() {
315 this.on_block_response(body);
316 }
317 }
318 Err(err) => {
319 debug!(target: "downloaders", %err, ?this.hash, "Body download failed");
320 }
321 }
322 if this.body.is_none() {
323 this.request.body = Some(this.client.get_block_body(this.hash));
325 }
326 }
327 }
328
329 if let Some(res) = this.take_block() {
330 return Poll::Ready(res)
331 }
332
333 budget -= 1;
335 if budget == 0 {
336 cx.waker().wake_by_ref();
338 return Poll::Pending
339 }
340 }
341 }
342}
343
344#[must_use = "futures do nothing unless polled"]
349pub struct FetchFullBlockWithBalFuture<Client>
350where
351 Client: BlockClient + BlockAccessListsClient,
352{
353 block: FetchFullBlockFuture<Client>,
354 block_result: Option<SealedBlock<Client::Block>>,
355 bal_request_state: BalRequestState<<Client as BlockAccessListsClient>::Output>,
356}
357
358impl<Client> FetchFullBlockWithBalFuture<Client>
359where
360 Client: BlockClient<Header: BlockHeader> + BlockAccessListsClient,
361{
362 pub const fn hash(&self) -> &B256 {
364 self.block.hash()
365 }
366}
367
368impl<Client> FetchFullBlockWithBalFuture<Client>
369where
370 Client: BlockClient<Header: BlockHeader + Sealable> + BlockAccessListsClient + 'static,
371{
372 pub fn block_number(&self) -> Option<u64> {
374 self.block_result.as_ref().map(|block| block.number()).or_else(|| self.block.block_number())
375 }
376
377 fn poll_bal_request(&mut self, cx: &mut Context<'_>) -> Poll<()> {
383 let res = match &mut self.bal_request_state {
384 BalRequestState::Pending(fut) => ready!(fut.poll_unpin(cx)),
385 BalRequestState::Ready(_) => return Poll::Ready(()),
386 };
387
388 match res {
389 Ok(bal) => {
390 let (peer, access_lists) = bal.split();
391 match access_lists.0.len() {
392 0 => self.bal_request_state = BalRequestState::Ready(None),
393 1 => {
394 let bal = access_lists.0.into_iter().next().expect("len checked");
395 self.bal_request_state =
396 BalRequestState::Ready(Some(WithPeerId::new(peer, bal)));
397 }
398 received => {
399 debug!(
400 target: "downloaders",
401 hash = ?self.block.hash(),
402 expected = 1,
403 received,
404 "Received wrong access list response",
405 );
406 self.block.client.report_bad_message(peer);
407 self.bal_request_state = BalRequestState::Ready(None);
408 }
409 }
410 }
411 Err(err) => {
412 debug!(
413 target: "downloaders",
414 %err,
415 hash = ?self.block.hash(),
416 "Access list download failed",
417 );
418 self.bal_request_state = BalRequestState::Ready(None);
419 }
420 }
421
422 Poll::Ready(())
423 }
424
425 fn take_block_and_access_lists(&mut self) -> Option<SealedBlockWithAccessList<Client::Block>> {
431 let BalRequestState::Ready(bal) = &mut self.bal_request_state else { return None };
432 let block = self.block_result.take()?;
433 let raw_bal =
434 bal.take().and_then(|bal| match seal_block_access_list_for_block(&block, bal) {
435 Ok(raw_bal) => raw_bal,
436 Err(peer) => {
437 self.block.client.report_bad_message(peer);
438 None
439 }
440 });
441 Some(SealedBlockWith::new(block, raw_bal))
442 }
443}
444
445impl<Client> Future for FetchFullBlockWithBalFuture<Client>
446where
447 Client: BlockClient<Header: BlockHeader + Sealable> + BlockAccessListsClient + 'static,
448{
449 type Output = SealedBlockWithAccessList<Client::Block>;
450
451 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
452 let this = self.get_mut();
453
454 if this.block_result.is_none() &&
455 let Poll::Ready(block) = this.block.poll_unpin(cx)
456 {
457 this.block_result = Some(block);
458 }
459
460 ready!(this.poll_bal_request(cx));
461
462 if let Some(res) = this.take_block_and_access_lists() {
463 return Poll::Ready(res)
464 }
465
466 Poll::Pending
467 }
468}
469
470impl<Client> Debug for FetchFullBlockWithBalFuture<Client>
471where
472 Client: BlockClient<Header: BlockHeader> + BlockAccessListsClient,
473{
474 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
475 f.debug_struct("FetchFullBlockWithBalFuture")
476 .field("hash", &self.block.hash())
477 .field("block_ready", &self.block_result.is_some())
478 .field("bal_request_ready", &self.bal_request_state.is_ready())
479 .finish()
480 }
481}
482
483enum BalRequestState<Req> {
485 Pending(Req),
486 Ready(Option<WithPeerId<Option<Bytes>>>),
487}
488
489impl<Req> BalRequestState<Req> {
490 const fn is_ready(&self) -> bool {
491 matches!(self, Self::Ready(_))
492 }
493}
494
495#[must_use = "futures do nothing unless polled"]
502#[expect(missing_debug_implementations)]
503pub struct FetchFullBlockRangeWithBalFuture<Client>
504where
505 Client: BlockClient + BlockAccessListsClient,
506{
507 blocks: FetchFullBlockRangeFuture<Client>,
508 client: Client,
509 block_result: Option<Vec<SealedBlock<Client::Block>>>,
510 access_lists: OptionalBlockAccessListsState<<Client as BlockAccessListsClient>::Output>,
511}
512
513impl<Client> FetchFullBlockRangeWithBalFuture<Client>
514where
515 Client: BlockClient<Header: Debug + BlockHeader + Sealable + Clone + Hash + Eq>
516 + BlockAccessListsClient,
517{
518 pub const fn start_hash(&self) -> B256 {
520 self.blocks.start_hash()
521 }
522
523 pub const fn count(&self) -> u64 {
525 self.blocks.count()
526 }
527
528 fn start_access_lists_request_if_possible(&mut self) {
529 let requirement = match &self.access_lists {
530 OptionalBlockAccessListsState::WaitingForBlocks { requirement } => *requirement,
531 OptionalBlockAccessListsState::Pending(_) | OptionalBlockAccessListsState::Ready(_) => {
532 return
533 }
534 };
535
536 let Some(blocks) = self.block_result.as_ref() else { return };
538 let hashes = blocks.iter().map(|block| block.hash()).collect::<Vec<_>>();
539 self.access_lists = OptionalBlockAccessListsState::Pending(
540 self.client.get_block_access_lists_with_requirement(hashes, requirement),
541 );
542 }
543
544 fn poll_access_lists(&mut self, cx: &mut Context<'_>) {
546 self.start_access_lists_request_if_possible();
547
548 let poll = match &mut self.access_lists {
549 OptionalBlockAccessListsState::Pending(fut) => fut.poll_unpin(cx),
550 OptionalBlockAccessListsState::WaitingForBlocks { .. } |
551 OptionalBlockAccessListsState::Ready(_) => return,
552 };
553
554 match poll {
555 Poll::Pending => {}
556 Poll::Ready(Ok(access_lists)) => {
557 self.access_lists = OptionalBlockAccessListsState::Ready(Some(access_lists));
558 }
559 Poll::Ready(Err(err)) => {
560 debug!(
561 target: "downloaders",
562 %err,
563 start_hash = ?self.blocks.start_hash(),
564 "Access list range download failed",
565 );
566
567 self.access_lists = OptionalBlockAccessListsState::Ready(None);
570 }
571 }
572 }
573
574 fn take_response(&mut self) -> Option<Vec<SealedBlockWithAccessList<Client::Block>>> {
576 let OptionalBlockAccessListsState::Ready(access_lists) = &mut self.access_lists else {
577 return None
578 };
579
580 let blocks = self.block_result.take()?;
581
582 Some(seal_blocks_with_access_lists(&self.client, blocks, access_lists.take()))
583 }
584}
585
586impl<Client> Future for FetchFullBlockRangeWithBalFuture<Client>
587where
588 Client: BlockClient<Header: Debug + BlockHeader + Sealable + Clone + Hash + Eq>
589 + BlockAccessListsClient
590 + 'static,
591{
592 type Output = Vec<SealedBlockWithAccessList<Client::Block>>;
593
594 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
595 let this = self.get_mut();
596
597 if this.block_result.is_none() &&
599 let Poll::Ready(blocks) = this.blocks.poll_unpin(cx)
600 {
601 this.block_result = Some(blocks);
602 }
603
604 this.poll_access_lists(cx);
605
606 if let Some(response) = this.take_response() {
607 return Poll::Ready(response)
608 }
609
610 Poll::Pending
611 }
612}
613
614enum OptionalBlockAccessListsState<Req> {
616 WaitingForBlocks {
618 requirement: BalRequirement,
620 },
621 Pending(Req),
623 Ready(Option<WithPeerId<BlockAccessLists>>),
625}
626
627impl<Client> Debug for FetchFullBlockFuture<Client>
628where
629 Client: BlockClient<Header: Debug, Body: Debug>,
630{
631 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
632 f.debug_struct("FetchFullBlockFuture")
633 .field("hash", &self.hash)
634 .field("header", &self.header)
635 .field("body", &self.body)
636 .finish()
637 }
638}
639
640struct FullBlockRequest<Client>
641where
642 Client: BlockClient,
643{
644 header: Option<SingleHeaderRequest<<Client as HeadersClient>::Output>>,
645 body: Option<SingleBodyRequest<<Client as BodiesClient>::Output>>,
646}
647
648impl<Client> FullBlockRequest<Client>
649where
650 Client: BlockClient,
651{
652 fn poll(&mut self, cx: &mut Context<'_>) -> Poll<ResponseResult<Client::Header, Client::Body>> {
653 if let Some(fut) = Pin::new(&mut self.header).as_pin_mut() &&
654 let Poll::Ready(res) = fut.poll(cx)
655 {
656 self.header = None;
657 return Poll::Ready(ResponseResult::Header(res))
658 }
659
660 if let Some(fut) = Pin::new(&mut self.body).as_pin_mut() &&
661 let Poll::Ready(res) = fut.poll(cx)
662 {
663 self.body = None;
664 return Poll::Ready(ResponseResult::Body(res))
665 }
666
667 Poll::Pending
668 }
669}
670
671enum ResponseResult<H, B> {
674 Header(PeerRequestResult<Option<H>>),
675 Body(PeerRequestResult<Option<B>>),
676}
677
678#[derive(Debug)]
680enum BodyResponse<B> {
681 Validated(B),
683 PendingValidation(WithPeerId<B>),
685}
686#[must_use = "futures do nothing unless polled"]
699#[expect(missing_debug_implementations)]
700pub struct FetchFullBlockRangeFuture<Client>
701where
702 Client: BlockClient,
703{
704 client: Client,
706 consensus: Arc<dyn Consensus<Client::Block>>,
708 start_hash: B256,
710 count: u64,
712 request: FullBlockRangeRequest<Client>,
714 headers: Option<Vec<SealedHeader<Client::Header>>>,
716 pending_headers: VecDeque<SealedHeader<Client::Header>>,
718 bodies: HashMap<SealedHeader<Client::Header>, BodyResponse<Client::Body>>,
720}
721
722impl<Client> FetchFullBlockRangeFuture<Client>
723where
724 Client: BlockClient<Header: Debug + BlockHeader + Sealable + Clone + Hash + Eq>,
725{
726 fn is_bodies_complete(&self) -> bool {
728 self.bodies.len() == self.count as usize
729 }
730
731 fn insert_body(&mut self, body_response: BodyResponse<Client::Body>) {
735 if let Some(header) = self.pending_headers.pop_front() {
736 self.bodies.insert(header, body_response);
737 }
738 }
739
740 fn insert_bodies(&mut self, bodies: impl IntoIterator<Item = BodyResponse<Client::Body>>) {
742 for body in bodies {
743 self.insert_body(body);
744 }
745 }
746
747 fn remaining_bodies_hashes(&self) -> Vec<B256> {
750 self.pending_headers.iter().map(|h| h.hash()).collect()
751 }
752
753 fn take_blocks(&mut self) -> Option<Vec<SealedBlock<Client::Block>>> {
761 if !self.is_bodies_complete() {
762 return None
764 }
765
766 let headers = self.headers.take()?;
767 let mut needs_retry = false;
768 let mut valid_responses = Vec::new();
769
770 for header in &headers {
771 if let Some(body_resp) = self.bodies.remove(header) {
772 let body = match body_resp {
774 BodyResponse::Validated(body) => body,
775 BodyResponse::PendingValidation(resp) => {
776 if let Err(err) =
778 self.consensus.validate_body_against_header(resp.data(), header)
779 {
780 debug!(target: "downloaders", %err, hash=?header.hash(), "Received wrong body in range response");
781 self.client.report_bad_message(resp.peer_id());
782
783 self.pending_headers.push_back(header.clone());
785 needs_retry = true;
786 continue
787 }
788
789 resp.into_data()
790 }
791 };
792
793 valid_responses
794 .push(SealedBlock::<Client::Block>::from_sealed_parts(header.clone(), body));
795 }
796 }
797
798 if needs_retry {
799 for block in valid_responses {
802 let (header, body) = block.split_sealed_header_body();
803 self.bodies.insert(header, BodyResponse::Validated(body));
804 }
805
806 self.headers = Some(headers);
808
809 let hashes = self.remaining_bodies_hashes();
811 self.request.bodies = Some(self.client.get_block_bodies(hashes));
812 return None
813 }
814
815 Some(valid_responses)
816 }
817
818 fn on_headers_response(&mut self, headers: WithPeerId<Vec<Client::Header>>) {
819 let (peer, mut headers_falling) =
820 headers.map(|h| h.into_iter().map(SealedHeader::seal_slow).collect::<Vec<_>>()).split();
821
822 if headers_falling.len() == self.count as usize {
824 headers_falling.sort_unstable_by_key(|h| Reverse(h.number()));
826
827 if headers_falling[0].hash() == self.start_hash {
829 let headers_rising = headers_falling.iter().rev().cloned().collect::<Vec<_>>();
830 if let Err(err) = self.consensus.validate_header_range(&headers_rising) {
832 debug!(target: "downloaders", %err, ?self.start_hash, "Received bad header response");
833 self.client.report_bad_message(peer);
834 return
835 }
836
837 let hashes = headers_falling.iter().map(|h| h.hash()).collect::<Vec<_>>();
839
840 self.pending_headers = headers_falling.clone().into();
842
843 if !self.has_bodies_request_started() {
845 self.request.bodies = Some(self.client.get_block_bodies(hashes));
847 }
848
849 self.headers = Some(headers_falling);
851 } else {
852 self.client.report_bad_message(peer);
854 }
855 }
856 }
857
858 const fn has_bodies_request_started(&self) -> bool {
861 self.request.bodies.is_some()
862 }
863
864 pub const fn start_hash(&self) -> B256 {
866 self.start_hash
867 }
868
869 pub const fn count(&self) -> u64 {
871 self.count
872 }
873}
874
875impl<Client> Future for FetchFullBlockRangeFuture<Client>
876where
877 Client: BlockClient<Header: Debug + BlockHeader + Sealable + Clone + Hash + Eq> + 'static,
878{
879 type Output = Vec<SealedBlock<Client::Block>>;
880
881 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
882 let this = self.get_mut();
883
884 loop {
885 match ready!(this.request.poll(cx)) {
886 RangeResponseResult::Header(res) => {
895 match res {
896 Ok(headers) => {
897 this.on_headers_response(headers);
898 }
899 Err(err) => {
900 debug!(target: "downloaders", %err, ?this.start_hash, "Header range download failed");
901 }
902 }
903
904 if this.headers.is_none() {
905 this.request.headers = Some(this.client.get_headers(HeadersRequest {
907 start: this.start_hash.into(),
908 limit: this.count,
909 direction: HeadersDirection::Falling,
910 }));
911 }
912 }
913 RangeResponseResult::Body(res) => {
919 match res {
920 Ok(bodies_resp) => {
921 let (peer, new_bodies) = bodies_resp.split();
922
923 this.insert_bodies(
925 new_bodies
926 .into_iter()
927 .map(|resp| WithPeerId::new(peer, resp))
928 .map(BodyResponse::PendingValidation),
929 );
930
931 if !this.is_bodies_complete() {
932 let req_hashes = this.remaining_bodies_hashes();
934
935 this.request.bodies = Some(this.client.get_block_bodies(req_hashes))
937 }
938 }
939 Err(err) => {
940 debug!(target: "downloaders", %err, ?this.start_hash, "Body range download failed");
941 }
942 }
943 if this.request.bodies.is_none() && !this.is_bodies_complete() {
944 let hashes = this.remaining_bodies_hashes();
957 if !hashes.is_empty() {
958 this.request.bodies = Some(this.client.get_block_bodies(hashes));
959 }
960 }
961 }
962 }
963
964 if let Some(res) = this.take_blocks() {
965 return Poll::Ready(res)
966 }
967 }
968 }
969}
970
971struct FullBlockRangeRequest<Client>
975where
976 Client: BlockClient,
977{
978 headers: Option<<Client as HeadersClient>::Output>,
979 bodies: Option<<Client as BodiesClient>::Output>,
980}
981
982impl<Client> FullBlockRangeRequest<Client>
983where
984 Client: BlockClient,
985{
986 fn poll(
987 &mut self,
988 cx: &mut Context<'_>,
989 ) -> Poll<RangeResponseResult<Client::Header, Client::Body>> {
990 if let Some(fut) = Pin::new(&mut self.headers).as_pin_mut() &&
991 let Poll::Ready(res) = fut.poll(cx)
992 {
993 self.headers = None;
994 return Poll::Ready(RangeResponseResult::Header(res))
995 }
996
997 if let Some(fut) = Pin::new(&mut self.bodies).as_pin_mut() &&
998 let Poll::Ready(res) = fut.poll(cx)
999 {
1000 self.bodies = None;
1001 return Poll::Ready(RangeResponseResult::Body(res))
1002 }
1003
1004 Poll::Pending
1005 }
1006}
1007
1008enum RangeResponseResult<H, B> {
1011 Header(PeerRequestResult<Vec<H>>),
1012 Body(PeerRequestResult<Vec<B>>),
1013}
1014
1015#[derive(Debug, Clone)]
1017#[non_exhaustive]
1018pub struct NoopFullBlockClient<Net = EthNetworkPrimitives>(PhantomData<Net>);
1019
1020impl<Net> DownloadClient for NoopFullBlockClient<Net>
1022where
1023 Net: Debug + Send + Sync,
1024{
1025 fn report_bad_message(&self, _peer_id: PeerId) {}
1032
1033 fn num_connected_peers(&self) -> usize {
1039 0
1040 }
1041}
1042
1043impl<Net> BodiesClient for NoopFullBlockClient<Net>
1045where
1046 Net: NetworkPrimitives,
1047{
1048 type Body = Net::BlockBody;
1049 type Output = futures::future::Ready<PeerRequestResult<Vec<Self::Body>>>;
1051
1052 fn get_block_bodies_with_priority_and_range_hint(
1063 &self,
1064 _hashes: Vec<B256>,
1065 _priority: Priority,
1066 _range_hint: Option<RangeInclusive<u64>>,
1067 ) -> Self::Output {
1068 futures::future::ready(Ok(WithPeerId::new(PeerId::random(), vec![])))
1071 }
1072}
1073
1074impl<Net> HeadersClient for NoopFullBlockClient<Net>
1075where
1076 Net: NetworkPrimitives,
1077{
1078 type Header = Net::BlockHeader;
1079 type Output = futures::future::Ready<PeerRequestResult<Vec<Self::Header>>>;
1082
1083 fn get_headers_with_priority(
1097 &self,
1098 _request: HeadersRequest,
1099 _priority: Priority,
1100 ) -> Self::Output {
1101 futures::future::ready(Ok(WithPeerId::new(PeerId::random(), vec![])))
1102 }
1103}
1104
1105impl<Net> BlockClient for NoopFullBlockClient<Net>
1106where
1107 Net: NetworkPrimitives,
1108{
1109 type Block = Net::Block;
1110}
1111
1112impl<Net> BlockAccessListsClient for NoopFullBlockClient<Net>
1113where
1114 Net: NetworkPrimitives,
1115{
1116 type Output = futures::future::Ready<PeerRequestResult<BlockAccessLists>>;
1117
1118 fn get_block_access_lists_with_priority_and_requirement(
1119 &self,
1120 _hashes: Vec<B256>,
1121 _priority: Priority,
1122 _requirement: BalRequirement,
1123 ) -> Self::Output {
1124 futures::future::ready(Ok(WithPeerId::new(PeerId::random(), BlockAccessLists::default())))
1125 }
1126}
1127
1128impl<Net> Default for NoopFullBlockClient<Net> {
1129 fn default() -> Self {
1130 Self(PhantomData::<Net>)
1131 }
1132}
1133
1134fn seal_block_access_list_for_block<B: Block>(
1140 block: &SealedBlock<B>,
1141 bal: WithPeerId<Option<Bytes>>,
1142) -> Result<Option<RawBal>, PeerId> {
1143 let Some(expected) = block.header().block_access_list_hash() else { return Ok(None) };
1144
1145 let (peer, bal) = bal.split();
1146 let Some(bal) = bal else { return Ok(None) };
1147 let raw_bal = RawBal::new(bal);
1148 raw_bal.ensure_hash(expected).map_err(|error| {
1149 debug!(
1150 target: "downloaders",
1151 block_hash = ?block.hash(),
1152 computed = ?error.computed,
1153 expected = ?error.expected,
1154 "Received block access list with wrong hash",
1155 );
1156 peer
1157 })?;
1158 Ok(Some(raw_bal))
1159}
1160
1161fn seal_blocks_with_access_lists<Client>(
1168 client: &Client,
1169 blocks: Vec<SealedBlock<Client::Block>>,
1170 access_lists: Option<WithPeerId<BlockAccessLists>>,
1171) -> Vec<SealedBlockWithAccessList<Client::Block>>
1172where
1173 Client: BlockClient,
1174{
1175 let Some(access_lists) = access_lists else {
1176 return blocks.into_iter().map(SealedBlockWith::from_block).collect()
1177 };
1178
1179 let (peer, access_lists) = access_lists.split();
1180 let expected = blocks.len();
1181 let received = access_lists.0.len();
1182
1183 if received > expected {
1184 trace!(
1185 target: "downloaders",
1186 expected,
1187 received,
1188 "Ignoring overlong access list range response",
1189 );
1190 return blocks.into_iter().map(SealedBlockWith::from_block).collect()
1191 }
1192
1193 let mut access_lists = access_lists.0.into_iter();
1194 let mut blocks = blocks.into_iter();
1195 let mut response = Vec::with_capacity(expected);
1196
1197 for block in blocks.by_ref() {
1198 let Some(bal) = access_lists.next() else {
1199 response.push(SealedBlockWith::from_block(block));
1202 break
1203 };
1204
1205 match seal_block_access_list_for_block(&block, WithPeerId::new(peer, bal)) {
1206 Ok(raw_bal) => response.push(SealedBlockWith::new(block, raw_bal)),
1207 Err(peer) => {
1208 client.report_bad_message(peer);
1211 response.push(SealedBlockWith::from_block(block));
1212 break
1213 }
1214 }
1215 }
1216
1217 response.extend(blocks.map(SealedBlockWith::from_block));
1218 response
1219}
1220
1221#[cfg(test)]
1222mod tests {
1223 use reth_ethereum_primitives::BlockBody;
1224
1225 use super::*;
1226 use crate::{error::RequestError, test_utils::TestFullBlockClient};
1227 use alloy_consensus::Header;
1228 use alloy_primitives::{keccak256, map::B256Map, Bytes};
1229 use parking_lot::Mutex;
1230 use std::{
1231 ops::Range,
1232 sync::{
1233 atomic::{AtomicBool, AtomicUsize, Ordering},
1234 Arc,
1235 },
1236 };
1237
1238 const EMPTY_LIST_CODE: u8 = 0xc0;
1239 use tokio::time::{timeout, Duration};
1240
1241 fn sealed_header_with_access_list_hash(bal: &Bytes) -> SealedHeader {
1242 let header =
1243 Header { block_access_list_hash: Some(keccak256(bal.as_ref())), ..Default::default() };
1244 SealedHeader::seal_slow(header)
1245 }
1246
1247 fn range_access_lists<B: Block>(
1248 blocks: &[SealedBlockWithAccessList<B>],
1249 ) -> Vec<Option<RawBal>> {
1250 blocks.iter().map(|block| block.data().clone()).collect()
1251 }
1252
1253 #[tokio::test]
1254 async fn download_single_full_block() {
1255 let client = TestFullBlockClient::default();
1256 let header: SealedHeader = SealedHeader::default();
1257 let body = BlockBody::default();
1258 client.insert(header.clone(), body.clone());
1259 let client = FullBlockClient::test_client(client);
1260
1261 let received = client.get_full_block(header.hash()).await;
1262 assert_eq!(received, SealedBlock::from_sealed_parts(header, body));
1263 }
1264
1265 #[tokio::test]
1266 async fn download_single_full_block_range() {
1267 let client = TestFullBlockClient::default();
1268 let header: SealedHeader = SealedHeader::default();
1269 let body = BlockBody::default();
1270 client.insert(header.clone(), body.clone());
1271 let client = FullBlockClient::test_client(client);
1272
1273 let received = client.get_full_block_range(header.hash(), 1).await;
1274 let received = received.first().expect("response should include a block");
1275 assert_eq!(*received, SealedBlock::from_sealed_parts(header, body));
1276 }
1277
1278 #[tokio::test]
1279 async fn download_single_full_block_with_access_lists() {
1280 let client = FullBlockWithAccessListsClient::default();
1281 let body = BlockBody::default();
1282 let bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1283 let header = sealed_header_with_access_list_hash(&bal);
1284 client.insert(header.clone(), body.clone(), bal.clone());
1285
1286 let request_count = Arc::clone(&client.access_list_requests);
1287 let client = FullBlockClient::test_client(client);
1288
1289 let received = client.get_full_block_with_access_lists(header.hash()).await;
1290 let expected_raw_bal = RawBal::from(bal);
1291
1292 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1293 assert_eq!(received.data().as_ref(), Some(&expected_raw_bal));
1294 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1295 }
1296
1297 #[tokio::test]
1298 async fn download_single_full_block_with_access_lists_uses_requested_requirement() {
1299 let client = FullBlockWithAccessListsClient::default();
1300 let body = BlockBody::default();
1301 let bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1302 let header = sealed_header_with_access_list_hash(&bal);
1303 client.insert(header.clone(), body.clone(), bal.clone());
1304
1305 let requirement = Arc::clone(&client.last_access_list_requirement);
1306 let client = FullBlockClient::test_client(client);
1307
1308 let received = client
1309 .get_full_block_with_access_lists_with_requirement(
1310 header.hash(),
1311 BalRequirement::Mandatory,
1312 )
1313 .await;
1314
1315 let expected_raw_bal = RawBal::from(bal);
1316 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1317 assert_eq!(received.data().as_ref(), Some(&expected_raw_bal));
1318 assert_eq!(*requirement.lock(), Some(BalRequirement::Mandatory));
1319 }
1320
1321 #[tokio::test]
1322 async fn download_single_full_block_with_access_lists_waits_for_pending_access_lists() {
1323 let client = FullBlockWithAccessListsClient::default();
1324 client.set_access_list_pending_polls(1);
1325
1326 let body = BlockBody::default();
1327 let bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1328 let header = sealed_header_with_access_list_hash(&bal);
1329 client.insert(header.clone(), body.clone(), bal.clone());
1330
1331 let request_count = Arc::clone(&client.access_list_requests);
1332 let client = FullBlockClient::test_client(client);
1333
1334 let received =
1335 timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1336 .await
1337 .expect("access list request should complete");
1338
1339 let expected_raw_bal = RawBal::from(bal);
1340 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1341 assert_eq!(received.data().as_ref(), Some(&expected_raw_bal));
1342 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1343 }
1344
1345 #[tokio::test]
1346 async fn download_single_full_block_with_access_lists_rejects_wrong_hash() {
1347 let client = FullBlockWithAccessListsClient::default();
1348 let body = BlockBody::default();
1349 let expected_bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1350 let wrong_bal = Bytes::from_static(&[0xc1, 0x01]);
1351 let header = sealed_header_with_access_list_hash(&expected_bal);
1352 client.insert(header.clone(), body.clone(), wrong_bal);
1353
1354 let bad_messages = Arc::clone(&client.bad_messages);
1355 let client = FullBlockClient::test_client(client);
1356
1357 let received =
1358 timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1359 .await
1360 .expect("block request should complete without access lists");
1361
1362 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1363 assert!(received.data().is_none());
1364 assert_eq!(bad_messages.load(Ordering::SeqCst), 1);
1365 }
1366
1367 #[tokio::test]
1368 async fn download_single_full_block_with_access_lists_treats_none_as_unavailable() {
1369 let client = FullBlockWithAccessListsClient::default();
1370 let body = BlockBody::default();
1371 let expected_bal = Bytes::from_static(&[0xc1, 0x01]);
1372 let header = sealed_header_with_access_list_hash(&expected_bal);
1373 client.inner.insert(header.clone(), body.clone());
1374
1375 let bad_messages = Arc::clone(&client.bad_messages);
1376 let client = FullBlockClient::test_client(client);
1377
1378 let received =
1379 timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1380 .await
1381 .expect("block request should complete without access lists");
1382
1383 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1384 assert!(received.data().is_none());
1385 assert_eq!(bad_messages.load(Ordering::SeqCst), 0);
1386 }
1387
1388 #[tokio::test]
1389 async fn download_single_full_block_with_access_lists_rejects_wrong_empty_list() {
1390 let client = FullBlockWithAccessListsClient::default();
1391 let body = BlockBody::default();
1392 let expected_bal = Bytes::from_static(&[0xc1, 0x01]);
1393 let wrong_empty_bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1394 let header = sealed_header_with_access_list_hash(&expected_bal);
1395 client.insert(header.clone(), body.clone(), wrong_empty_bal);
1396
1397 let bad_messages = Arc::clone(&client.bad_messages);
1398 let client = FullBlockClient::test_client(client);
1399
1400 let received =
1401 timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1402 .await
1403 .expect("block request should complete without access lists");
1404
1405 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1406 assert!(received.data().is_none());
1407 assert_eq!(bad_messages.load(Ordering::SeqCst), 1);
1408 }
1409
1410 #[tokio::test]
1411 async fn download_single_full_block_with_access_lists_returns_none_after_empty_response() {
1412 let client = FullBlockWithAccessListsClient::default();
1413 client.empty_first_response.store(true, Ordering::SeqCst);
1414
1415 let body = BlockBody::default();
1416 let bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1417 let header = sealed_header_with_access_list_hash(&bal);
1418 client.insert(header.clone(), body.clone(), bal.clone());
1419
1420 let request_count = Arc::clone(&client.access_list_requests);
1421 let bad_messages = Arc::clone(&client.bad_messages);
1422 let client = FullBlockClient::test_client(client);
1423
1424 let received =
1425 timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1426 .await
1427 .expect("block request should complete without access lists");
1428
1429 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1430 assert!(received.data().is_none());
1431 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1432 assert_eq!(bad_messages.load(Ordering::SeqCst), 0);
1433 }
1434
1435 #[tokio::test]
1436 async fn download_single_full_block_with_access_lists_returns_block_when_unavailable() {
1437 let client = FullBlockWithAccessListsClient::default();
1438 client.set_access_lists_unsupported(true);
1439
1440 let body = BlockBody::default();
1441 let bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1442 let header = sealed_header_with_access_list_hash(&bal);
1443 client.insert(header.clone(), body.clone(), bal);
1444
1445 let request_count = Arc::clone(&client.access_list_requests);
1446 let requirement = Arc::clone(&client.last_access_list_requirement);
1447 let client = FullBlockClient::test_client(client);
1448
1449 let received =
1450 timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1451 .await
1452 .expect("block request should complete without access lists");
1453
1454 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1455 assert!(received.data().is_none());
1456 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1457 assert_eq!(
1458 *requirement.lock(),
1459 Some(BalRequirement::Optional),
1460 "single block BAL lookup should be best-effort"
1461 );
1462 }
1463
1464 fn insert_headers_into_client(
1466 client: &TestFullBlockClient,
1467 range: Range<usize>,
1468 ) -> (SealedHeader, BlockBody) {
1469 let mut sealed_header: SealedHeader = SealedHeader::default();
1470 let body = BlockBody::default();
1471 for _ in range {
1472 let (mut header, hash) = sealed_header.split();
1473 header.parent_hash = hash;
1475 header.number += 1;
1476
1477 sealed_header = SealedHeader::seal_slow(header);
1478
1479 client.insert(sealed_header.clone(), body.clone());
1480 }
1481
1482 (sealed_header, body)
1483 }
1484
1485 #[derive(Clone, Debug)]
1486 struct FullBlockWithAccessListsClient {
1487 inner: TestFullBlockClient,
1488 access_lists: Arc<Mutex<B256Map<Bytes>>>,
1489 access_list_requests: Arc<AtomicUsize>,
1490 access_list_soft_limit: Arc<AtomicUsize>,
1491 access_list_pending_polls: Arc<AtomicUsize>,
1492 extra_access_list_entries: Arc<AtomicUsize>,
1493 unsupported_access_lists: Arc<AtomicBool>,
1494 last_access_list_requirement: Arc<Mutex<Option<BalRequirement>>>,
1495 bad_messages: Arc<AtomicUsize>,
1496 empty_first_response: Arc<AtomicBool>,
1497 }
1498
1499 impl Default for FullBlockWithAccessListsClient {
1500 fn default() -> Self {
1501 Self {
1502 inner: TestFullBlockClient::default(),
1503 access_lists: Arc::new(Mutex::new(B256Map::default())),
1504 access_list_requests: Arc::new(AtomicUsize::new(0)),
1505 access_list_soft_limit: Arc::new(AtomicUsize::new(usize::MAX)),
1506 access_list_pending_polls: Arc::new(AtomicUsize::new(0)),
1507 extra_access_list_entries: Arc::new(AtomicUsize::new(0)),
1508 unsupported_access_lists: Arc::new(AtomicBool::new(false)),
1509 last_access_list_requirement: Arc::new(Mutex::new(None)),
1510 bad_messages: Arc::new(AtomicUsize::new(0)),
1511 empty_first_response: Arc::new(AtomicBool::new(false)),
1512 }
1513 }
1514 }
1515
1516 impl FullBlockWithAccessListsClient {
1517 fn insert(&self, header: SealedHeader, body: BlockBody, bal: Bytes) {
1518 self.inner.insert(header.clone(), body);
1519 self.access_lists.lock().insert(header.hash(), bal);
1520 }
1521
1522 fn set_access_list_soft_limit(&self, limit: usize) {
1523 self.access_list_soft_limit.store(limit, Ordering::SeqCst);
1524 }
1525
1526 fn set_access_list_pending_polls(&self, polls: usize) {
1527 self.access_list_pending_polls.store(polls, Ordering::SeqCst);
1528 }
1529
1530 fn set_extra_access_list_entries(&self, count: usize) {
1531 self.extra_access_list_entries.store(count, Ordering::SeqCst);
1532 }
1533
1534 fn set_access_lists_unsupported(&self, unsupported: bool) {
1535 self.unsupported_access_lists.store(unsupported, Ordering::SeqCst);
1536 }
1537 }
1538
1539 fn insert_headers_with_access_lists_into_client(
1541 client: &FullBlockWithAccessListsClient,
1542 range: Range<usize>,
1543 ) -> (SealedHeader, BlockBody) {
1544 let mut sealed_header: SealedHeader = SealedHeader::default();
1545 let body = BlockBody::default();
1546 for block_idx in range {
1547 let (mut header, hash) = sealed_header.split();
1548 header.parent_hash = hash;
1549 header.number += 1;
1550 let bal = Bytes::from(vec![0xc1, block_idx as u8]);
1551 header.block_access_list_hash = Some(keccak256(bal.as_ref()));
1552
1553 sealed_header = SealedHeader::seal_slow(header);
1554
1555 client.insert(sealed_header.clone(), body.clone(), bal);
1556 }
1557
1558 (sealed_header, body)
1559 }
1560
1561 impl DownloadClient for FullBlockWithAccessListsClient {
1562 fn report_bad_message(&self, peer_id: PeerId) {
1563 self.bad_messages.fetch_add(1, Ordering::SeqCst);
1564 self.inner.report_bad_message(peer_id);
1565 }
1566
1567 fn num_connected_peers(&self) -> usize {
1568 self.inner.num_connected_peers()
1569 }
1570 }
1571
1572 impl HeadersClient for FullBlockWithAccessListsClient {
1573 type Header = <TestFullBlockClient as HeadersClient>::Header;
1574 type Output = <TestFullBlockClient as HeadersClient>::Output;
1575
1576 fn get_headers_with_priority(
1577 &self,
1578 request: HeadersRequest,
1579 priority: Priority,
1580 ) -> Self::Output {
1581 self.inner.get_headers_with_priority(request, priority)
1582 }
1583 }
1584
1585 impl BodiesClient for FullBlockWithAccessListsClient {
1586 type Body = <TestFullBlockClient as BodiesClient>::Body;
1587 type Output = <TestFullBlockClient as BodiesClient>::Output;
1588
1589 fn get_block_bodies_with_priority_and_range_hint(
1590 &self,
1591 hashes: Vec<B256>,
1592 priority: Priority,
1593 range_hint: Option<RangeInclusive<u64>>,
1594 ) -> Self::Output {
1595 self.inner.get_block_bodies_with_priority_and_range_hint(hashes, priority, range_hint)
1596 }
1597 }
1598
1599 struct MaybePendingAccessLists {
1600 response: Option<PeerRequestResult<BlockAccessLists>>,
1601 pending_polls: usize,
1602 }
1603
1604 impl MaybePendingAccessLists {
1605 const fn new(response: PeerRequestResult<BlockAccessLists>, pending_polls: usize) -> Self {
1606 Self { response: Some(response), pending_polls }
1607 }
1608 }
1609
1610 impl std::future::Future for MaybePendingAccessLists {
1611 type Output = PeerRequestResult<BlockAccessLists>;
1612
1613 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1614 if self.pending_polls > 0 {
1615 self.pending_polls -= 1;
1616 cx.waker().wake_by_ref();
1617 return Poll::Pending
1618 }
1619
1620 Poll::Ready(self.response.take().expect("future polled after completion"))
1621 }
1622 }
1623
1624 impl BlockAccessListsClient for FullBlockWithAccessListsClient {
1625 type Output = MaybePendingAccessLists;
1626
1627 fn get_block_access_lists_with_priority_and_requirement(
1628 &self,
1629 hashes: Vec<B256>,
1630 _priority: Priority,
1631 requirement: BalRequirement,
1632 ) -> Self::Output {
1633 self.access_list_requests.fetch_add(1, Ordering::SeqCst);
1634 *self.last_access_list_requirement.lock() = Some(requirement);
1635 let pending_polls = self.access_list_pending_polls.swap(0, Ordering::SeqCst);
1636
1637 if self.unsupported_access_lists.load(Ordering::SeqCst) {
1638 return MaybePendingAccessLists::new(
1639 Err(RequestError::UnsupportedCapability),
1640 pending_polls,
1641 )
1642 }
1643
1644 if self.empty_first_response.swap(false, Ordering::SeqCst) {
1645 return MaybePendingAccessLists::new(
1646 Ok(WithPeerId::new(PeerId::random(), BlockAccessLists(Vec::new()))),
1647 pending_polls,
1648 )
1649 }
1650
1651 let mut access_lists: Vec<_> = hashes
1652 .into_iter()
1653 .take(self.access_list_soft_limit.load(Ordering::SeqCst))
1654 .map(|hash| self.access_lists.lock().get(&hash).cloned())
1655 .collect();
1656 for _ in 0..self.extra_access_list_entries.load(Ordering::SeqCst) {
1657 access_lists.push(None);
1658 }
1659
1660 MaybePendingAccessLists::new(
1661 Ok(WithPeerId::new(PeerId::random(), BlockAccessLists(access_lists))),
1662 pending_polls,
1663 )
1664 }
1665 }
1666
1667 impl BlockClient for FullBlockWithAccessListsClient {
1668 type Block = reth_ethereum_primitives::Block;
1669 }
1670
1671 #[derive(Clone, Debug)]
1672 struct FailingBodiesClient {
1673 inner: TestFullBlockClient,
1674 fail_on: usize,
1675 body_requests: Arc<AtomicUsize>,
1676 }
1677
1678 impl FailingBodiesClient {
1679 fn new(inner: TestFullBlockClient, fail_on: usize) -> Self {
1680 Self { inner, fail_on, body_requests: Arc::new(AtomicUsize::new(0)) }
1681 }
1682 }
1683
1684 impl DownloadClient for FailingBodiesClient {
1685 fn report_bad_message(&self, peer_id: PeerId) {
1686 self.inner.report_bad_message(peer_id);
1687 }
1688
1689 fn num_connected_peers(&self) -> usize {
1690 self.inner.num_connected_peers()
1691 }
1692 }
1693
1694 impl HeadersClient for FailingBodiesClient {
1695 type Header = <TestFullBlockClient as HeadersClient>::Header;
1696 type Output = <TestFullBlockClient as HeadersClient>::Output;
1697
1698 fn get_headers_with_priority(
1699 &self,
1700 request: HeadersRequest,
1701 priority: Priority,
1702 ) -> Self::Output {
1703 self.inner.get_headers_with_priority(request, priority)
1704 }
1705 }
1706
1707 impl BodiesClient for FailingBodiesClient {
1708 type Body = <TestFullBlockClient as BodiesClient>::Body;
1709 type Output = <TestFullBlockClient as BodiesClient>::Output;
1710
1711 fn get_block_bodies_with_priority_and_range_hint(
1712 &self,
1713 hashes: Vec<B256>,
1714 priority: Priority,
1715 range_hint: Option<RangeInclusive<u64>>,
1716 ) -> Self::Output {
1717 let attempt = self.body_requests.fetch_add(1, Ordering::SeqCst);
1718 if attempt == self.fail_on {
1719 return futures::future::ready(Err(RequestError::Timeout))
1720 }
1721
1722 self.inner.get_block_bodies_with_priority_and_range_hint(hashes, priority, range_hint)
1723 }
1724 }
1725
1726 impl BlockClient for FailingBodiesClient {
1727 type Block = reth_ethereum_primitives::Block;
1728 }
1729
1730 #[tokio::test]
1731 async fn download_full_block_range() {
1732 let client = TestFullBlockClient::default();
1733 let (header, body) = insert_headers_into_client(&client, 0..50);
1734 let client = FullBlockClient::test_client(client);
1735
1736 let received = client.get_full_block_range(header.hash(), 1).await;
1737 let received = received.first().expect("response should include a block");
1738 assert_eq!(*received, SealedBlock::from_sealed_parts(header.clone(), body));
1739
1740 let received = client.get_full_block_range(header.hash(), 10).await;
1741 assert_eq!(received.len(), 10);
1742 for (i, block) in received.iter().enumerate() {
1743 let expected_number = header.number - i as u64;
1744 assert_eq!(block.number, expected_number);
1745 }
1746 }
1747
1748 #[tokio::test]
1749 async fn download_full_block_range_over_soft_limit() {
1750 let client = TestFullBlockClient::default();
1752 let (header, body) = insert_headers_into_client(&client, 0..50);
1753 let client = FullBlockClient::test_client(client);
1754
1755 let received = client.get_full_block_range(header.hash(), 1).await;
1756 let received = received.first().expect("response should include a block");
1757 assert_eq!(*received, SealedBlock::from_sealed_parts(header.clone(), body));
1758
1759 let received = client.get_full_block_range(header.hash(), 50).await;
1760 assert_eq!(received.len(), 50);
1761 for (i, block) in received.iter().enumerate() {
1762 let expected_number = header.number - i as u64;
1763 assert_eq!(block.number, expected_number);
1764 }
1765 }
1766
1767 #[tokio::test]
1768 async fn download_full_block_range_retries_after_body_error() {
1769 let mut client = TestFullBlockClient::default();
1770 client.set_soft_limit(2);
1771 let (header, _) = insert_headers_into_client(&client, 0..3);
1772
1773 let client = FailingBodiesClient::new(client, 1);
1774 let body_requests = Arc::clone(&client.body_requests);
1775 let client = FullBlockClient::test_client(client);
1776
1777 let received =
1778 timeout(Duration::from_secs(1), client.get_full_block_range(header.hash(), 3))
1779 .await
1780 .expect("body request retry should complete");
1781
1782 assert_eq!(received.len(), 3);
1783 assert_eq!(body_requests.load(Ordering::SeqCst), 3);
1784 }
1785
1786 #[tokio::test]
1787 async fn download_full_block_range_with_access_lists() {
1788 let client = FullBlockWithAccessListsClient::default();
1789 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1790
1791 let access_lists = Arc::clone(&client.access_lists);
1792 let request_count = Arc::clone(&client.access_list_requests);
1793 let requirement = Arc::clone(&client.last_access_list_requirement);
1794 let client = FullBlockClient::test_client(client);
1795
1796 let response = timeout(
1797 Duration::from_secs(1),
1798 client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1799 )
1800 .await
1801 .expect("range request should complete");
1802
1803 let blocks = response;
1804 assert_eq!(blocks.len(), 3);
1805 let expected = {
1806 let bals = access_lists.lock();
1807 blocks
1808 .iter()
1809 .map(|block| {
1810 let bal = bals.get(&block.block().hash()).cloned().expect("access list exists");
1811 Some(RawBal::from(bal))
1812 })
1813 .collect::<Vec<_>>()
1814 };
1815 assert_eq!(range_access_lists(&blocks), expected);
1816 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1817 assert_eq!(*requirement.lock(), Some(BalRequirement::Optional));
1818 }
1819
1820 #[tokio::test]
1821 async fn download_full_block_range_with_access_lists_returns_none_for_empty_response() {
1822 let client = FullBlockWithAccessListsClient::default();
1823 client.empty_first_response.store(true, Ordering::SeqCst);
1824 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1825
1826 let request_count = Arc::clone(&client.access_list_requests);
1827 let client = FullBlockClient::test_client(client);
1828
1829 let response = timeout(
1830 Duration::from_secs(1),
1831 client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1832 )
1833 .await
1834 .expect("range request should complete without access lists");
1835
1836 let blocks = response;
1837 assert_eq!(blocks.len(), 3);
1838 assert_eq!(range_access_lists(&blocks), vec![None; blocks.len()]);
1839 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1840 }
1841
1842 #[tokio::test]
1843 async fn download_full_block_range_with_access_lists_uses_requested_requirement() {
1844 let client = FullBlockWithAccessListsClient::default();
1845 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1846
1847 let requirement = Arc::clone(&client.last_access_list_requirement);
1848 let client = FullBlockClient::test_client(client);
1849
1850 let blocks = timeout(
1851 Duration::from_secs(1),
1852 client.get_full_block_range_with_optional_access_lists_with_requirement(
1853 header.hash(),
1854 3,
1855 BalRequirement::Mandatory,
1856 ),
1857 )
1858 .await
1859 .expect("range request should complete");
1860
1861 assert_eq!(blocks.len(), 3);
1862 assert_eq!(*requirement.lock(), Some(BalRequirement::Mandatory));
1863 }
1864
1865 #[tokio::test]
1866 async fn download_full_block_range_with_access_lists_preserves_short_response() {
1867 let client = FullBlockWithAccessListsClient::default();
1868 client.set_access_list_soft_limit(2);
1869 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..5);
1870
1871 let access_lists = Arc::clone(&client.access_lists);
1872 let request_count = Arc::clone(&client.access_list_requests);
1873 let client = FullBlockClient::test_client(client);
1874
1875 let blocks = timeout(
1876 Duration::from_secs(1),
1877 client.get_full_block_range_with_optional_access_lists(header.hash(), 5),
1878 )
1879 .await
1880 .expect("range request should complete without access lists");
1881
1882 assert_eq!(blocks.len(), 5);
1883 let expected = {
1884 let bals = access_lists.lock();
1885 blocks
1886 .iter()
1887 .enumerate()
1888 .map(|(idx, block)| {
1889 if idx >= 2 {
1890 return None
1891 }
1892
1893 let bal = bals.get(&block.block().hash()).cloned().expect("access list exists");
1894 Some(RawBal::from(bal))
1895 })
1896 .collect::<Vec<_>>()
1897 };
1898 assert_eq!(range_access_lists(&blocks), expected);
1899 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1900 }
1901
1902 #[tokio::test]
1903 async fn download_full_block_range_with_access_lists_preserves_unavailable_entries() {
1904 let client = FullBlockWithAccessListsClient::default();
1905 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1906 client.access_lists.lock().remove(&header.hash());
1907
1908 let access_lists = Arc::clone(&client.access_lists);
1909 let bad_messages = Arc::clone(&client.bad_messages);
1910 let client = FullBlockClient::test_client(client);
1911
1912 let blocks = timeout(
1913 Duration::from_secs(1),
1914 client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1915 )
1916 .await
1917 .expect("range request should complete");
1918
1919 assert_eq!(blocks.len(), 3);
1920 let expected = {
1921 let bals = access_lists.lock();
1922 blocks
1923 .iter()
1924 .map(|block| {
1925 if block.block().hash() == header.hash() {
1926 return None
1927 }
1928
1929 let bal = bals.get(&block.block().hash()).cloned().expect("access list exists");
1930 Some(RawBal::from(bal))
1931 })
1932 .collect::<Vec<_>>()
1933 };
1934 assert_eq!(range_access_lists(&blocks), expected);
1935 assert_eq!(bad_messages.load(Ordering::SeqCst), 0);
1936 }
1937
1938 #[tokio::test]
1939 async fn download_full_block_range_with_access_lists_returns_none_when_unavailable() {
1940 let client = FullBlockWithAccessListsClient::default();
1941 client.set_access_lists_unsupported(true);
1942 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1943
1944 let request_count = Arc::clone(&client.access_list_requests);
1945 let client = FullBlockClient::test_client(client);
1946
1947 let blocks = timeout(
1948 Duration::from_secs(1),
1949 client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1950 )
1951 .await
1952 .expect("range request should complete without access lists");
1953
1954 assert_eq!(blocks.len(), 3);
1955 assert_eq!(range_access_lists(&blocks), vec![None; blocks.len()]);
1956 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1957 }
1958
1959 #[tokio::test]
1960 async fn download_full_block_range_with_access_lists_ignores_long_response() {
1961 let client = FullBlockWithAccessListsClient::default();
1962 client.set_extra_access_list_entries(1);
1963 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1964
1965 let request_count = Arc::clone(&client.access_list_requests);
1966 let bad_messages = Arc::clone(&client.bad_messages);
1967 let client = FullBlockClient::test_client(client);
1968
1969 let blocks = timeout(
1970 Duration::from_secs(1),
1971 client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1972 )
1973 .await
1974 .expect("range request should complete without access lists");
1975
1976 assert_eq!(blocks.len(), 3);
1977 assert_eq!(range_access_lists(&blocks), vec![None; blocks.len()]);
1978 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1979 assert_eq!(bad_messages.load(Ordering::SeqCst), 0);
1980 }
1981
1982 #[tokio::test]
1983 async fn download_full_block_range_with_access_lists_rejects_wrong_hash() {
1984 let client = FullBlockWithAccessListsClient::default();
1985 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1986 client.access_lists.lock().insert(header.hash(), Bytes::from_static(&[0xc1, 0x7f]));
1987
1988 let bad_messages = Arc::clone(&client.bad_messages);
1989 let client = FullBlockClient::test_client(client);
1990
1991 let blocks = timeout(
1992 Duration::from_secs(1),
1993 client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1994 )
1995 .await
1996 .expect("range request should complete without access lists");
1997
1998 assert_eq!(blocks.len(), 3);
1999 assert_eq!(range_access_lists(&blocks), vec![None; blocks.len()]);
2000 assert_eq!(bad_messages.load(Ordering::SeqCst), 1);
2001 }
2002
2003 #[tokio::test]
2004 async fn download_full_block_range_with_access_lists_preserves_valid_prefix_until_wrong_hash() {
2005 let client = FullBlockWithAccessListsClient::default();
2006 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
2007 let first_bal =
2008 client.access_lists.lock().get(&header.hash()).cloned().expect("access list exists");
2009 let second_hash = header.parent_hash;
2010 client.access_lists.lock().insert(second_hash, Bytes::from_static(&[0xc1, 0x7f]));
2011
2012 let bad_messages = Arc::clone(&client.bad_messages);
2013 let client = FullBlockClient::test_client(client);
2014
2015 let blocks = timeout(
2016 Duration::from_secs(1),
2017 client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
2018 )
2019 .await
2020 .expect("range request should complete without unvalidated access lists");
2021
2022 assert_eq!(blocks.len(), 3);
2023 assert_eq!(blocks[1].block().hash(), second_hash);
2024 assert_eq!(range_access_lists(&blocks), vec![Some(RawBal::from(first_bal)), None, None]);
2025 assert_eq!(bad_messages.load(Ordering::SeqCst), 1);
2026 }
2027
2028 #[tokio::test]
2029 async fn download_full_block_range_rejects_invalid_headers() {
2030 let client = TestFullBlockClient::default();
2031 let (header, _) = insert_headers_into_client(&client, 0..3);
2032 let consensus = reth_consensus::test_utils::TestConsensus::default();
2033 consensus.set_fail_validation(true);
2034 let client = FullBlockClient::new(client, Arc::new(consensus));
2035 let mut request = client.get_full_block_range(header.hash(), 3);
2036 let headers = client
2037 .client
2038 .get_headers(HeadersRequest::falling(header.hash().into(), 3))
2039 .await
2040 .unwrap();
2041
2042 request.on_headers_response(headers);
2043
2044 assert!(request.headers.is_none());
2045 assert!(request.pending_headers.is_empty());
2046 assert!(request.request.bodies.is_none());
2047 }
2048}