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 }
835
836 let hashes = headers_falling.iter().map(|h| h.hash()).collect::<Vec<_>>();
838
839 self.pending_headers = headers_falling.clone().into();
841
842 if !self.has_bodies_request_started() {
844 self.request.bodies = Some(self.client.get_block_bodies(hashes));
846 }
847
848 self.headers = Some(headers_falling);
850 } else {
851 self.client.report_bad_message(peer);
853 }
854 }
855 }
856
857 const fn has_bodies_request_started(&self) -> bool {
860 self.request.bodies.is_some()
861 }
862
863 pub const fn start_hash(&self) -> B256 {
865 self.start_hash
866 }
867
868 pub const fn count(&self) -> u64 {
870 self.count
871 }
872}
873
874impl<Client> Future for FetchFullBlockRangeFuture<Client>
875where
876 Client: BlockClient<Header: Debug + BlockHeader + Sealable + Clone + Hash + Eq> + 'static,
877{
878 type Output = Vec<SealedBlock<Client::Block>>;
879
880 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
881 let this = self.get_mut();
882
883 loop {
884 match ready!(this.request.poll(cx)) {
885 RangeResponseResult::Header(res) => {
894 match res {
895 Ok(headers) => {
896 this.on_headers_response(headers);
897 }
898 Err(err) => {
899 debug!(target: "downloaders", %err, ?this.start_hash, "Header range download failed");
900 }
901 }
902
903 if this.headers.is_none() {
904 this.request.headers = Some(this.client.get_headers(HeadersRequest {
906 start: this.start_hash.into(),
907 limit: this.count,
908 direction: HeadersDirection::Falling,
909 }));
910 }
911 }
912 RangeResponseResult::Body(res) => {
918 match res {
919 Ok(bodies_resp) => {
920 let (peer, new_bodies) = bodies_resp.split();
921
922 this.insert_bodies(
924 new_bodies
925 .into_iter()
926 .map(|resp| WithPeerId::new(peer, resp))
927 .map(BodyResponse::PendingValidation),
928 );
929
930 if !this.is_bodies_complete() {
931 let req_hashes = this.remaining_bodies_hashes();
933
934 this.request.bodies = Some(this.client.get_block_bodies(req_hashes))
936 }
937 }
938 Err(err) => {
939 debug!(target: "downloaders", %err, ?this.start_hash, "Body range download failed");
940 }
941 }
942 if this.request.bodies.is_none() && !this.is_bodies_complete() {
943 let hashes = this.remaining_bodies_hashes();
956 if !hashes.is_empty() {
957 this.request.bodies = Some(this.client.get_block_bodies(hashes));
958 }
959 }
960 }
961 }
962
963 if let Some(res) = this.take_blocks() {
964 return Poll::Ready(res)
965 }
966 }
967 }
968}
969
970struct FullBlockRangeRequest<Client>
974where
975 Client: BlockClient,
976{
977 headers: Option<<Client as HeadersClient>::Output>,
978 bodies: Option<<Client as BodiesClient>::Output>,
979}
980
981impl<Client> FullBlockRangeRequest<Client>
982where
983 Client: BlockClient,
984{
985 fn poll(
986 &mut self,
987 cx: &mut Context<'_>,
988 ) -> Poll<RangeResponseResult<Client::Header, Client::Body>> {
989 if let Some(fut) = Pin::new(&mut self.headers).as_pin_mut() &&
990 let Poll::Ready(res) = fut.poll(cx)
991 {
992 self.headers = None;
993 return Poll::Ready(RangeResponseResult::Header(res))
994 }
995
996 if let Some(fut) = Pin::new(&mut self.bodies).as_pin_mut() &&
997 let Poll::Ready(res) = fut.poll(cx)
998 {
999 self.bodies = None;
1000 return Poll::Ready(RangeResponseResult::Body(res))
1001 }
1002
1003 Poll::Pending
1004 }
1005}
1006
1007enum RangeResponseResult<H, B> {
1010 Header(PeerRequestResult<Vec<H>>),
1011 Body(PeerRequestResult<Vec<B>>),
1012}
1013
1014#[derive(Debug, Clone)]
1016#[non_exhaustive]
1017pub struct NoopFullBlockClient<Net = EthNetworkPrimitives>(PhantomData<Net>);
1018
1019impl<Net> DownloadClient for NoopFullBlockClient<Net>
1021where
1022 Net: Debug + Send + Sync,
1023{
1024 fn report_bad_message(&self, _peer_id: PeerId) {}
1031
1032 fn num_connected_peers(&self) -> usize {
1038 0
1039 }
1040}
1041
1042impl<Net> BodiesClient for NoopFullBlockClient<Net>
1044where
1045 Net: NetworkPrimitives,
1046{
1047 type Body = Net::BlockBody;
1048 type Output = futures::future::Ready<PeerRequestResult<Vec<Self::Body>>>;
1050
1051 fn get_block_bodies_with_priority_and_range_hint(
1062 &self,
1063 _hashes: Vec<B256>,
1064 _priority: Priority,
1065 _range_hint: Option<RangeInclusive<u64>>,
1066 ) -> Self::Output {
1067 futures::future::ready(Ok(WithPeerId::new(PeerId::random(), vec![])))
1070 }
1071}
1072
1073impl<Net> HeadersClient for NoopFullBlockClient<Net>
1074where
1075 Net: NetworkPrimitives,
1076{
1077 type Header = Net::BlockHeader;
1078 type Output = futures::future::Ready<PeerRequestResult<Vec<Self::Header>>>;
1081
1082 fn get_headers_with_priority(
1096 &self,
1097 _request: HeadersRequest,
1098 _priority: Priority,
1099 ) -> Self::Output {
1100 futures::future::ready(Ok(WithPeerId::new(PeerId::random(), vec![])))
1101 }
1102}
1103
1104impl<Net> BlockClient for NoopFullBlockClient<Net>
1105where
1106 Net: NetworkPrimitives,
1107{
1108 type Block = Net::Block;
1109}
1110
1111impl<Net> BlockAccessListsClient for NoopFullBlockClient<Net>
1112where
1113 Net: NetworkPrimitives,
1114{
1115 type Output = futures::future::Ready<PeerRequestResult<BlockAccessLists>>;
1116
1117 fn get_block_access_lists_with_priority_and_requirement(
1118 &self,
1119 _hashes: Vec<B256>,
1120 _priority: Priority,
1121 _requirement: BalRequirement,
1122 ) -> Self::Output {
1123 futures::future::ready(Ok(WithPeerId::new(PeerId::random(), BlockAccessLists::default())))
1124 }
1125}
1126
1127impl<Net> Default for NoopFullBlockClient<Net> {
1128 fn default() -> Self {
1129 Self(PhantomData::<Net>)
1130 }
1131}
1132
1133fn seal_block_access_list_for_block<B: Block>(
1139 block: &SealedBlock<B>,
1140 bal: WithPeerId<Option<Bytes>>,
1141) -> Result<Option<RawBal>, PeerId> {
1142 let Some(expected) = block.header().block_access_list_hash() else { return Ok(None) };
1143
1144 let (peer, bal) = bal.split();
1145 let Some(bal) = bal else { return Ok(None) };
1146 let raw_bal = RawBal::new(bal);
1147 raw_bal.ensure_hash(expected).map_err(|error| {
1148 debug!(
1149 target: "downloaders",
1150 block_hash = ?block.hash(),
1151 computed = ?error.computed,
1152 expected = ?error.expected,
1153 "Received block access list with wrong hash",
1154 );
1155 peer
1156 })?;
1157 Ok(Some(raw_bal))
1158}
1159
1160fn seal_blocks_with_access_lists<Client>(
1167 client: &Client,
1168 blocks: Vec<SealedBlock<Client::Block>>,
1169 access_lists: Option<WithPeerId<BlockAccessLists>>,
1170) -> Vec<SealedBlockWithAccessList<Client::Block>>
1171where
1172 Client: BlockClient,
1173{
1174 let Some(access_lists) = access_lists else {
1175 return blocks.into_iter().map(SealedBlockWith::from_block).collect()
1176 };
1177
1178 let (peer, access_lists) = access_lists.split();
1179 let expected = blocks.len();
1180 let received = access_lists.0.len();
1181
1182 if received > expected {
1183 trace!(
1184 target: "downloaders",
1185 expected,
1186 received,
1187 "Ignoring overlong access list range response",
1188 );
1189 return blocks.into_iter().map(SealedBlockWith::from_block).collect()
1190 }
1191
1192 let mut access_lists = access_lists.0.into_iter();
1193 let mut blocks = blocks.into_iter();
1194 let mut response = Vec::with_capacity(expected);
1195
1196 for block in blocks.by_ref() {
1197 let Some(bal) = access_lists.next() else {
1198 response.push(SealedBlockWith::from_block(block));
1201 break
1202 };
1203
1204 match seal_block_access_list_for_block(&block, WithPeerId::new(peer, bal)) {
1205 Ok(raw_bal) => response.push(SealedBlockWith::new(block, raw_bal)),
1206 Err(peer) => {
1207 client.report_bad_message(peer);
1210 response.push(SealedBlockWith::from_block(block));
1211 break
1212 }
1213 }
1214 }
1215
1216 response.extend(blocks.map(SealedBlockWith::from_block));
1217 response
1218}
1219
1220#[cfg(test)]
1221mod tests {
1222 use reth_ethereum_primitives::BlockBody;
1223
1224 use super::*;
1225 use crate::{error::RequestError, test_utils::TestFullBlockClient};
1226 use alloy_consensus::Header;
1227 use alloy_primitives::{keccak256, map::B256Map, Bytes};
1228 use parking_lot::Mutex;
1229 use std::{
1230 ops::Range,
1231 sync::{
1232 atomic::{AtomicBool, AtomicUsize, Ordering},
1233 Arc,
1234 },
1235 };
1236
1237 const EMPTY_LIST_CODE: u8 = 0xc0;
1238 use tokio::time::{timeout, Duration};
1239
1240 fn sealed_header_with_access_list_hash(bal: &Bytes) -> SealedHeader {
1241 let header =
1242 Header { block_access_list_hash: Some(keccak256(bal.as_ref())), ..Default::default() };
1243 SealedHeader::seal_slow(header)
1244 }
1245
1246 fn range_access_lists<B: Block>(
1247 blocks: &[SealedBlockWithAccessList<B>],
1248 ) -> Vec<Option<RawBal>> {
1249 blocks.iter().map(|block| block.data().clone()).collect()
1250 }
1251
1252 #[tokio::test]
1253 async fn download_single_full_block() {
1254 let client = TestFullBlockClient::default();
1255 let header: SealedHeader = SealedHeader::default();
1256 let body = BlockBody::default();
1257 client.insert(header.clone(), body.clone());
1258 let client = FullBlockClient::test_client(client);
1259
1260 let received = client.get_full_block(header.hash()).await;
1261 assert_eq!(received, SealedBlock::from_sealed_parts(header, body));
1262 }
1263
1264 #[tokio::test]
1265 async fn download_single_full_block_range() {
1266 let client = TestFullBlockClient::default();
1267 let header: SealedHeader = SealedHeader::default();
1268 let body = BlockBody::default();
1269 client.insert(header.clone(), body.clone());
1270 let client = FullBlockClient::test_client(client);
1271
1272 let received = client.get_full_block_range(header.hash(), 1).await;
1273 let received = received.first().expect("response should include a block");
1274 assert_eq!(*received, SealedBlock::from_sealed_parts(header, body));
1275 }
1276
1277 #[tokio::test]
1278 async fn download_single_full_block_with_access_lists() {
1279 let client = FullBlockWithAccessListsClient::default();
1280 let body = BlockBody::default();
1281 let bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1282 let header = sealed_header_with_access_list_hash(&bal);
1283 client.insert(header.clone(), body.clone(), bal.clone());
1284
1285 let request_count = Arc::clone(&client.access_list_requests);
1286 let client = FullBlockClient::test_client(client);
1287
1288 let received = client.get_full_block_with_access_lists(header.hash()).await;
1289 let expected_raw_bal = RawBal::from(bal);
1290
1291 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1292 assert_eq!(received.data().as_ref(), Some(&expected_raw_bal));
1293 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1294 }
1295
1296 #[tokio::test]
1297 async fn download_single_full_block_with_access_lists_uses_requested_requirement() {
1298 let client = FullBlockWithAccessListsClient::default();
1299 let body = BlockBody::default();
1300 let bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1301 let header = sealed_header_with_access_list_hash(&bal);
1302 client.insert(header.clone(), body.clone(), bal.clone());
1303
1304 let requirement = Arc::clone(&client.last_access_list_requirement);
1305 let client = FullBlockClient::test_client(client);
1306
1307 let received = client
1308 .get_full_block_with_access_lists_with_requirement(
1309 header.hash(),
1310 BalRequirement::Mandatory,
1311 )
1312 .await;
1313
1314 let expected_raw_bal = RawBal::from(bal);
1315 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1316 assert_eq!(received.data().as_ref(), Some(&expected_raw_bal));
1317 assert_eq!(*requirement.lock(), Some(BalRequirement::Mandatory));
1318 }
1319
1320 #[tokio::test]
1321 async fn download_single_full_block_with_access_lists_waits_for_pending_access_lists() {
1322 let client = FullBlockWithAccessListsClient::default();
1323 client.set_access_list_pending_polls(1);
1324
1325 let body = BlockBody::default();
1326 let bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1327 let header = sealed_header_with_access_list_hash(&bal);
1328 client.insert(header.clone(), body.clone(), bal.clone());
1329
1330 let request_count = Arc::clone(&client.access_list_requests);
1331 let client = FullBlockClient::test_client(client);
1332
1333 let received =
1334 timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1335 .await
1336 .expect("access list request should complete");
1337
1338 let expected_raw_bal = RawBal::from(bal);
1339 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1340 assert_eq!(received.data().as_ref(), Some(&expected_raw_bal));
1341 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1342 }
1343
1344 #[tokio::test]
1345 async fn download_single_full_block_with_access_lists_rejects_wrong_hash() {
1346 let client = FullBlockWithAccessListsClient::default();
1347 let body = BlockBody::default();
1348 let expected_bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1349 let wrong_bal = Bytes::from_static(&[0xc1, 0x01]);
1350 let header = sealed_header_with_access_list_hash(&expected_bal);
1351 client.insert(header.clone(), body.clone(), wrong_bal);
1352
1353 let bad_messages = Arc::clone(&client.bad_messages);
1354 let client = FullBlockClient::test_client(client);
1355
1356 let received =
1357 timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1358 .await
1359 .expect("block request should complete without access lists");
1360
1361 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1362 assert!(received.data().is_none());
1363 assert_eq!(bad_messages.load(Ordering::SeqCst), 1);
1364 }
1365
1366 #[tokio::test]
1367 async fn download_single_full_block_with_access_lists_treats_none_as_unavailable() {
1368 let client = FullBlockWithAccessListsClient::default();
1369 let body = BlockBody::default();
1370 let expected_bal = Bytes::from_static(&[0xc1, 0x01]);
1371 let header = sealed_header_with_access_list_hash(&expected_bal);
1372 client.inner.insert(header.clone(), body.clone());
1373
1374 let bad_messages = Arc::clone(&client.bad_messages);
1375 let client = FullBlockClient::test_client(client);
1376
1377 let received =
1378 timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1379 .await
1380 .expect("block request should complete without access lists");
1381
1382 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1383 assert!(received.data().is_none());
1384 assert_eq!(bad_messages.load(Ordering::SeqCst), 0);
1385 }
1386
1387 #[tokio::test]
1388 async fn download_single_full_block_with_access_lists_rejects_wrong_empty_list() {
1389 let client = FullBlockWithAccessListsClient::default();
1390 let body = BlockBody::default();
1391 let expected_bal = Bytes::from_static(&[0xc1, 0x01]);
1392 let wrong_empty_bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1393 let header = sealed_header_with_access_list_hash(&expected_bal);
1394 client.insert(header.clone(), body.clone(), wrong_empty_bal);
1395
1396 let bad_messages = Arc::clone(&client.bad_messages);
1397 let client = FullBlockClient::test_client(client);
1398
1399 let received =
1400 timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1401 .await
1402 .expect("block request should complete without access lists");
1403
1404 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1405 assert!(received.data().is_none());
1406 assert_eq!(bad_messages.load(Ordering::SeqCst), 1);
1407 }
1408
1409 #[tokio::test]
1410 async fn download_single_full_block_with_access_lists_returns_none_after_empty_response() {
1411 let client = FullBlockWithAccessListsClient::default();
1412 client.empty_first_response.store(true, Ordering::SeqCst);
1413
1414 let body = BlockBody::default();
1415 let bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1416 let header = sealed_header_with_access_list_hash(&bal);
1417 client.insert(header.clone(), body.clone(), bal.clone());
1418
1419 let request_count = Arc::clone(&client.access_list_requests);
1420 let bad_messages = Arc::clone(&client.bad_messages);
1421 let client = FullBlockClient::test_client(client);
1422
1423 let received =
1424 timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1425 .await
1426 .expect("block request should complete without access lists");
1427
1428 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1429 assert!(received.data().is_none());
1430 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1431 assert_eq!(bad_messages.load(Ordering::SeqCst), 0);
1432 }
1433
1434 #[tokio::test]
1435 async fn download_single_full_block_with_access_lists_returns_block_when_unavailable() {
1436 let client = FullBlockWithAccessListsClient::default();
1437 client.set_access_lists_unsupported(true);
1438
1439 let body = BlockBody::default();
1440 let bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1441 let header = sealed_header_with_access_list_hash(&bal);
1442 client.insert(header.clone(), body.clone(), bal);
1443
1444 let request_count = Arc::clone(&client.access_list_requests);
1445 let requirement = Arc::clone(&client.last_access_list_requirement);
1446 let client = FullBlockClient::test_client(client);
1447
1448 let received =
1449 timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1450 .await
1451 .expect("block request should complete without access lists");
1452
1453 assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1454 assert!(received.data().is_none());
1455 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1456 assert_eq!(
1457 *requirement.lock(),
1458 Some(BalRequirement::Optional),
1459 "single block BAL lookup should be best-effort"
1460 );
1461 }
1462
1463 fn insert_headers_into_client(
1465 client: &TestFullBlockClient,
1466 range: Range<usize>,
1467 ) -> (SealedHeader, BlockBody) {
1468 let mut sealed_header: SealedHeader = SealedHeader::default();
1469 let body = BlockBody::default();
1470 for _ in range {
1471 let (mut header, hash) = sealed_header.split();
1472 header.parent_hash = hash;
1474 header.number += 1;
1475
1476 sealed_header = SealedHeader::seal_slow(header);
1477
1478 client.insert(sealed_header.clone(), body.clone());
1479 }
1480
1481 (sealed_header, body)
1482 }
1483
1484 #[derive(Clone, Debug)]
1485 struct FullBlockWithAccessListsClient {
1486 inner: TestFullBlockClient,
1487 access_lists: Arc<Mutex<B256Map<Bytes>>>,
1488 access_list_requests: Arc<AtomicUsize>,
1489 access_list_soft_limit: Arc<AtomicUsize>,
1490 access_list_pending_polls: Arc<AtomicUsize>,
1491 extra_access_list_entries: Arc<AtomicUsize>,
1492 unsupported_access_lists: Arc<AtomicBool>,
1493 last_access_list_requirement: Arc<Mutex<Option<BalRequirement>>>,
1494 bad_messages: Arc<AtomicUsize>,
1495 empty_first_response: Arc<AtomicBool>,
1496 }
1497
1498 impl Default for FullBlockWithAccessListsClient {
1499 fn default() -> Self {
1500 Self {
1501 inner: TestFullBlockClient::default(),
1502 access_lists: Arc::new(Mutex::new(B256Map::default())),
1503 access_list_requests: Arc::new(AtomicUsize::new(0)),
1504 access_list_soft_limit: Arc::new(AtomicUsize::new(usize::MAX)),
1505 access_list_pending_polls: Arc::new(AtomicUsize::new(0)),
1506 extra_access_list_entries: Arc::new(AtomicUsize::new(0)),
1507 unsupported_access_lists: Arc::new(AtomicBool::new(false)),
1508 last_access_list_requirement: Arc::new(Mutex::new(None)),
1509 bad_messages: Arc::new(AtomicUsize::new(0)),
1510 empty_first_response: Arc::new(AtomicBool::new(false)),
1511 }
1512 }
1513 }
1514
1515 impl FullBlockWithAccessListsClient {
1516 fn insert(&self, header: SealedHeader, body: BlockBody, bal: Bytes) {
1517 self.inner.insert(header.clone(), body);
1518 self.access_lists.lock().insert(header.hash(), bal);
1519 }
1520
1521 fn set_access_list_soft_limit(&self, limit: usize) {
1522 self.access_list_soft_limit.store(limit, Ordering::SeqCst);
1523 }
1524
1525 fn set_access_list_pending_polls(&self, polls: usize) {
1526 self.access_list_pending_polls.store(polls, Ordering::SeqCst);
1527 }
1528
1529 fn set_extra_access_list_entries(&self, count: usize) {
1530 self.extra_access_list_entries.store(count, Ordering::SeqCst);
1531 }
1532
1533 fn set_access_lists_unsupported(&self, unsupported: bool) {
1534 self.unsupported_access_lists.store(unsupported, Ordering::SeqCst);
1535 }
1536 }
1537
1538 fn insert_headers_with_access_lists_into_client(
1540 client: &FullBlockWithAccessListsClient,
1541 range: Range<usize>,
1542 ) -> (SealedHeader, BlockBody) {
1543 let mut sealed_header: SealedHeader = SealedHeader::default();
1544 let body = BlockBody::default();
1545 for block_idx in range {
1546 let (mut header, hash) = sealed_header.split();
1547 header.parent_hash = hash;
1548 header.number += 1;
1549 let bal = Bytes::from(vec![0xc1, block_idx as u8]);
1550 header.block_access_list_hash = Some(keccak256(bal.as_ref()));
1551
1552 sealed_header = SealedHeader::seal_slow(header);
1553
1554 client.insert(sealed_header.clone(), body.clone(), bal);
1555 }
1556
1557 (sealed_header, body)
1558 }
1559
1560 impl DownloadClient for FullBlockWithAccessListsClient {
1561 fn report_bad_message(&self, peer_id: PeerId) {
1562 self.bad_messages.fetch_add(1, Ordering::SeqCst);
1563 self.inner.report_bad_message(peer_id);
1564 }
1565
1566 fn num_connected_peers(&self) -> usize {
1567 self.inner.num_connected_peers()
1568 }
1569 }
1570
1571 impl HeadersClient for FullBlockWithAccessListsClient {
1572 type Header = <TestFullBlockClient as HeadersClient>::Header;
1573 type Output = <TestFullBlockClient as HeadersClient>::Output;
1574
1575 fn get_headers_with_priority(
1576 &self,
1577 request: HeadersRequest,
1578 priority: Priority,
1579 ) -> Self::Output {
1580 self.inner.get_headers_with_priority(request, priority)
1581 }
1582 }
1583
1584 impl BodiesClient for FullBlockWithAccessListsClient {
1585 type Body = <TestFullBlockClient as BodiesClient>::Body;
1586 type Output = <TestFullBlockClient as BodiesClient>::Output;
1587
1588 fn get_block_bodies_with_priority_and_range_hint(
1589 &self,
1590 hashes: Vec<B256>,
1591 priority: Priority,
1592 range_hint: Option<RangeInclusive<u64>>,
1593 ) -> Self::Output {
1594 self.inner.get_block_bodies_with_priority_and_range_hint(hashes, priority, range_hint)
1595 }
1596 }
1597
1598 struct MaybePendingAccessLists {
1599 response: Option<PeerRequestResult<BlockAccessLists>>,
1600 pending_polls: usize,
1601 }
1602
1603 impl MaybePendingAccessLists {
1604 const fn new(response: PeerRequestResult<BlockAccessLists>, pending_polls: usize) -> Self {
1605 Self { response: Some(response), pending_polls }
1606 }
1607 }
1608
1609 impl std::future::Future for MaybePendingAccessLists {
1610 type Output = PeerRequestResult<BlockAccessLists>;
1611
1612 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1613 if self.pending_polls > 0 {
1614 self.pending_polls -= 1;
1615 cx.waker().wake_by_ref();
1616 return Poll::Pending
1617 }
1618
1619 Poll::Ready(self.response.take().expect("future polled after completion"))
1620 }
1621 }
1622
1623 impl BlockAccessListsClient for FullBlockWithAccessListsClient {
1624 type Output = MaybePendingAccessLists;
1625
1626 fn get_block_access_lists_with_priority_and_requirement(
1627 &self,
1628 hashes: Vec<B256>,
1629 _priority: Priority,
1630 requirement: BalRequirement,
1631 ) -> Self::Output {
1632 self.access_list_requests.fetch_add(1, Ordering::SeqCst);
1633 *self.last_access_list_requirement.lock() = Some(requirement);
1634 let pending_polls = self.access_list_pending_polls.swap(0, Ordering::SeqCst);
1635
1636 if self.unsupported_access_lists.load(Ordering::SeqCst) {
1637 return MaybePendingAccessLists::new(
1638 Err(RequestError::UnsupportedCapability),
1639 pending_polls,
1640 )
1641 }
1642
1643 if self.empty_first_response.swap(false, Ordering::SeqCst) {
1644 return MaybePendingAccessLists::new(
1645 Ok(WithPeerId::new(PeerId::random(), BlockAccessLists(Vec::new()))),
1646 pending_polls,
1647 )
1648 }
1649
1650 let mut access_lists: Vec<_> = hashes
1651 .into_iter()
1652 .take(self.access_list_soft_limit.load(Ordering::SeqCst))
1653 .map(|hash| self.access_lists.lock().get(&hash).cloned())
1654 .collect();
1655 for _ in 0..self.extra_access_list_entries.load(Ordering::SeqCst) {
1656 access_lists.push(None);
1657 }
1658
1659 MaybePendingAccessLists::new(
1660 Ok(WithPeerId::new(PeerId::random(), BlockAccessLists(access_lists))),
1661 pending_polls,
1662 )
1663 }
1664 }
1665
1666 impl BlockClient for FullBlockWithAccessListsClient {
1667 type Block = reth_ethereum_primitives::Block;
1668 }
1669
1670 #[derive(Clone, Debug)]
1671 struct FailingBodiesClient {
1672 inner: TestFullBlockClient,
1673 fail_on: usize,
1674 body_requests: Arc<AtomicUsize>,
1675 }
1676
1677 impl FailingBodiesClient {
1678 fn new(inner: TestFullBlockClient, fail_on: usize) -> Self {
1679 Self { inner, fail_on, body_requests: Arc::new(AtomicUsize::new(0)) }
1680 }
1681 }
1682
1683 impl DownloadClient for FailingBodiesClient {
1684 fn report_bad_message(&self, peer_id: PeerId) {
1685 self.inner.report_bad_message(peer_id);
1686 }
1687
1688 fn num_connected_peers(&self) -> usize {
1689 self.inner.num_connected_peers()
1690 }
1691 }
1692
1693 impl HeadersClient for FailingBodiesClient {
1694 type Header = <TestFullBlockClient as HeadersClient>::Header;
1695 type Output = <TestFullBlockClient as HeadersClient>::Output;
1696
1697 fn get_headers_with_priority(
1698 &self,
1699 request: HeadersRequest,
1700 priority: Priority,
1701 ) -> Self::Output {
1702 self.inner.get_headers_with_priority(request, priority)
1703 }
1704 }
1705
1706 impl BodiesClient for FailingBodiesClient {
1707 type Body = <TestFullBlockClient as BodiesClient>::Body;
1708 type Output = <TestFullBlockClient as BodiesClient>::Output;
1709
1710 fn get_block_bodies_with_priority_and_range_hint(
1711 &self,
1712 hashes: Vec<B256>,
1713 priority: Priority,
1714 range_hint: Option<RangeInclusive<u64>>,
1715 ) -> Self::Output {
1716 let attempt = self.body_requests.fetch_add(1, Ordering::SeqCst);
1717 if attempt == self.fail_on {
1718 return futures::future::ready(Err(RequestError::Timeout))
1719 }
1720
1721 self.inner.get_block_bodies_with_priority_and_range_hint(hashes, priority, range_hint)
1722 }
1723 }
1724
1725 impl BlockClient for FailingBodiesClient {
1726 type Block = reth_ethereum_primitives::Block;
1727 }
1728
1729 #[tokio::test]
1730 async fn download_full_block_range() {
1731 let client = TestFullBlockClient::default();
1732 let (header, body) = insert_headers_into_client(&client, 0..50);
1733 let client = FullBlockClient::test_client(client);
1734
1735 let received = client.get_full_block_range(header.hash(), 1).await;
1736 let received = received.first().expect("response should include a block");
1737 assert_eq!(*received, SealedBlock::from_sealed_parts(header.clone(), body));
1738
1739 let received = client.get_full_block_range(header.hash(), 10).await;
1740 assert_eq!(received.len(), 10);
1741 for (i, block) in received.iter().enumerate() {
1742 let expected_number = header.number - i as u64;
1743 assert_eq!(block.number, expected_number);
1744 }
1745 }
1746
1747 #[tokio::test]
1748 async fn download_full_block_range_over_soft_limit() {
1749 let client = TestFullBlockClient::default();
1751 let (header, body) = insert_headers_into_client(&client, 0..50);
1752 let client = FullBlockClient::test_client(client);
1753
1754 let received = client.get_full_block_range(header.hash(), 1).await;
1755 let received = received.first().expect("response should include a block");
1756 assert_eq!(*received, SealedBlock::from_sealed_parts(header.clone(), body));
1757
1758 let received = client.get_full_block_range(header.hash(), 50).await;
1759 assert_eq!(received.len(), 50);
1760 for (i, block) in received.iter().enumerate() {
1761 let expected_number = header.number - i as u64;
1762 assert_eq!(block.number, expected_number);
1763 }
1764 }
1765
1766 #[tokio::test]
1767 async fn download_full_block_range_retries_after_body_error() {
1768 let mut client = TestFullBlockClient::default();
1769 client.set_soft_limit(2);
1770 let (header, _) = insert_headers_into_client(&client, 0..3);
1771
1772 let client = FailingBodiesClient::new(client, 1);
1773 let body_requests = Arc::clone(&client.body_requests);
1774 let client = FullBlockClient::test_client(client);
1775
1776 let received =
1777 timeout(Duration::from_secs(1), client.get_full_block_range(header.hash(), 3))
1778 .await
1779 .expect("body request retry should complete");
1780
1781 assert_eq!(received.len(), 3);
1782 assert_eq!(body_requests.load(Ordering::SeqCst), 3);
1783 }
1784
1785 #[tokio::test]
1786 async fn download_full_block_range_with_access_lists() {
1787 let client = FullBlockWithAccessListsClient::default();
1788 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1789
1790 let access_lists = Arc::clone(&client.access_lists);
1791 let request_count = Arc::clone(&client.access_list_requests);
1792 let requirement = Arc::clone(&client.last_access_list_requirement);
1793 let client = FullBlockClient::test_client(client);
1794
1795 let response = timeout(
1796 Duration::from_secs(1),
1797 client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1798 )
1799 .await
1800 .expect("range request should complete");
1801
1802 let blocks = response;
1803 assert_eq!(blocks.len(), 3);
1804 let expected = {
1805 let bals = access_lists.lock();
1806 blocks
1807 .iter()
1808 .map(|block| {
1809 let bal = bals.get(&block.block().hash()).cloned().expect("access list exists");
1810 Some(RawBal::from(bal))
1811 })
1812 .collect::<Vec<_>>()
1813 };
1814 assert_eq!(range_access_lists(&blocks), expected);
1815 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1816 assert_eq!(*requirement.lock(), Some(BalRequirement::Optional));
1817 }
1818
1819 #[tokio::test]
1820 async fn download_full_block_range_with_access_lists_returns_none_for_empty_response() {
1821 let client = FullBlockWithAccessListsClient::default();
1822 client.empty_first_response.store(true, Ordering::SeqCst);
1823 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1824
1825 let request_count = Arc::clone(&client.access_list_requests);
1826 let client = FullBlockClient::test_client(client);
1827
1828 let response = timeout(
1829 Duration::from_secs(1),
1830 client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1831 )
1832 .await
1833 .expect("range request should complete without access lists");
1834
1835 let blocks = response;
1836 assert_eq!(blocks.len(), 3);
1837 assert_eq!(range_access_lists(&blocks), vec![None; blocks.len()]);
1838 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1839 }
1840
1841 #[tokio::test]
1842 async fn download_full_block_range_with_access_lists_uses_requested_requirement() {
1843 let client = FullBlockWithAccessListsClient::default();
1844 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1845
1846 let requirement = Arc::clone(&client.last_access_list_requirement);
1847 let client = FullBlockClient::test_client(client);
1848
1849 let blocks = timeout(
1850 Duration::from_secs(1),
1851 client.get_full_block_range_with_optional_access_lists_with_requirement(
1852 header.hash(),
1853 3,
1854 BalRequirement::Mandatory,
1855 ),
1856 )
1857 .await
1858 .expect("range request should complete");
1859
1860 assert_eq!(blocks.len(), 3);
1861 assert_eq!(*requirement.lock(), Some(BalRequirement::Mandatory));
1862 }
1863
1864 #[tokio::test]
1865 async fn download_full_block_range_with_access_lists_preserves_short_response() {
1866 let client = FullBlockWithAccessListsClient::default();
1867 client.set_access_list_soft_limit(2);
1868 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..5);
1869
1870 let access_lists = Arc::clone(&client.access_lists);
1871 let request_count = Arc::clone(&client.access_list_requests);
1872 let client = FullBlockClient::test_client(client);
1873
1874 let blocks = timeout(
1875 Duration::from_secs(1),
1876 client.get_full_block_range_with_optional_access_lists(header.hash(), 5),
1877 )
1878 .await
1879 .expect("range request should complete without access lists");
1880
1881 assert_eq!(blocks.len(), 5);
1882 let expected = {
1883 let bals = access_lists.lock();
1884 blocks
1885 .iter()
1886 .enumerate()
1887 .map(|(idx, block)| {
1888 if idx >= 2 {
1889 return None
1890 }
1891
1892 let bal = bals.get(&block.block().hash()).cloned().expect("access list exists");
1893 Some(RawBal::from(bal))
1894 })
1895 .collect::<Vec<_>>()
1896 };
1897 assert_eq!(range_access_lists(&blocks), expected);
1898 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1899 }
1900
1901 #[tokio::test]
1902 async fn download_full_block_range_with_access_lists_preserves_unavailable_entries() {
1903 let client = FullBlockWithAccessListsClient::default();
1904 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1905 client.access_lists.lock().remove(&header.hash());
1906
1907 let access_lists = Arc::clone(&client.access_lists);
1908 let bad_messages = Arc::clone(&client.bad_messages);
1909 let client = FullBlockClient::test_client(client);
1910
1911 let blocks = timeout(
1912 Duration::from_secs(1),
1913 client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1914 )
1915 .await
1916 .expect("range request should complete");
1917
1918 assert_eq!(blocks.len(), 3);
1919 let expected = {
1920 let bals = access_lists.lock();
1921 blocks
1922 .iter()
1923 .map(|block| {
1924 if block.block().hash() == header.hash() {
1925 return None
1926 }
1927
1928 let bal = bals.get(&block.block().hash()).cloned().expect("access list exists");
1929 Some(RawBal::from(bal))
1930 })
1931 .collect::<Vec<_>>()
1932 };
1933 assert_eq!(range_access_lists(&blocks), expected);
1934 assert_eq!(bad_messages.load(Ordering::SeqCst), 0);
1935 }
1936
1937 #[tokio::test]
1938 async fn download_full_block_range_with_access_lists_returns_none_when_unavailable() {
1939 let client = FullBlockWithAccessListsClient::default();
1940 client.set_access_lists_unsupported(true);
1941 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1942
1943 let request_count = Arc::clone(&client.access_list_requests);
1944 let client = FullBlockClient::test_client(client);
1945
1946 let blocks = timeout(
1947 Duration::from_secs(1),
1948 client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1949 )
1950 .await
1951 .expect("range request should complete without access lists");
1952
1953 assert_eq!(blocks.len(), 3);
1954 assert_eq!(range_access_lists(&blocks), vec![None; blocks.len()]);
1955 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1956 }
1957
1958 #[tokio::test]
1959 async fn download_full_block_range_with_access_lists_ignores_long_response() {
1960 let client = FullBlockWithAccessListsClient::default();
1961 client.set_extra_access_list_entries(1);
1962 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1963
1964 let request_count = Arc::clone(&client.access_list_requests);
1965 let bad_messages = Arc::clone(&client.bad_messages);
1966 let client = FullBlockClient::test_client(client);
1967
1968 let blocks = timeout(
1969 Duration::from_secs(1),
1970 client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1971 )
1972 .await
1973 .expect("range request should complete without access lists");
1974
1975 assert_eq!(blocks.len(), 3);
1976 assert_eq!(range_access_lists(&blocks), vec![None; blocks.len()]);
1977 assert_eq!(request_count.load(Ordering::SeqCst), 1);
1978 assert_eq!(bad_messages.load(Ordering::SeqCst), 0);
1979 }
1980
1981 #[tokio::test]
1982 async fn download_full_block_range_with_access_lists_rejects_wrong_hash() {
1983 let client = FullBlockWithAccessListsClient::default();
1984 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1985 client.access_lists.lock().insert(header.hash(), Bytes::from_static(&[0xc1, 0x7f]));
1986
1987 let bad_messages = Arc::clone(&client.bad_messages);
1988 let client = FullBlockClient::test_client(client);
1989
1990 let blocks = timeout(
1991 Duration::from_secs(1),
1992 client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1993 )
1994 .await
1995 .expect("range request should complete without access lists");
1996
1997 assert_eq!(blocks.len(), 3);
1998 assert_eq!(range_access_lists(&blocks), vec![None; blocks.len()]);
1999 assert_eq!(bad_messages.load(Ordering::SeqCst), 1);
2000 }
2001
2002 #[tokio::test]
2003 async fn download_full_block_range_with_access_lists_preserves_valid_prefix_until_wrong_hash() {
2004 let client = FullBlockWithAccessListsClient::default();
2005 let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
2006 let first_bal =
2007 client.access_lists.lock().get(&header.hash()).cloned().expect("access list exists");
2008 let second_hash = header.parent_hash;
2009 client.access_lists.lock().insert(second_hash, Bytes::from_static(&[0xc1, 0x7f]));
2010
2011 let bad_messages = Arc::clone(&client.bad_messages);
2012 let client = FullBlockClient::test_client(client);
2013
2014 let blocks = timeout(
2015 Duration::from_secs(1),
2016 client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
2017 )
2018 .await
2019 .expect("range request should complete without unvalidated access lists");
2020
2021 assert_eq!(blocks.len(), 3);
2022 assert_eq!(blocks[1].block().hash(), second_hash);
2023 assert_eq!(range_access_lists(&blocks), vec![Some(RawBal::from(first_bal)), None, None]);
2024 assert_eq!(bad_messages.load(Ordering::SeqCst), 1);
2025 }
2026
2027 #[tokio::test]
2028 async fn download_full_block_range_with_invalid_header() {
2029 let client = TestFullBlockClient::default();
2030 let range_length: usize = 3;
2031 let (header, _) = insert_headers_into_client(&client, 0..range_length);
2032
2033 let test_consensus = reth_consensus::test_utils::TestConsensus::default();
2034 test_consensus.set_fail_validation(true);
2035 test_consensus.set_fail_body_against_header(false);
2036 let client = FullBlockClient::new(client, Arc::new(test_consensus));
2037
2038 let received = client.get_full_block_range(header.hash(), range_length as u64).await;
2039
2040 assert_eq!(received.len(), range_length);
2041 for (i, block) in received.iter().enumerate() {
2042 let expected_number = header.number - i as u64;
2043 assert_eq!(block.number, expected_number);
2044 }
2045 }
2046}