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