reth_network_p2p/test_utils/
full_block.rs1use crate::{
2 bodies::client::BodiesClient,
3 download::DownloadClient,
4 error::PeerRequestResult,
5 headers::client::{HeadersClient, HeadersRequest},
6 priority::Priority,
7 BlockClient,
8};
9use alloy_consensus::Header;
10use alloy_eips::{BlockHashOrNumber, BlockNumHash};
11use alloy_primitives::B256;
12use parking_lot::Mutex;
13use reth_eth_wire_types::HeadersDirection;
14use reth_ethereum_primitives::{Block, BlockBody};
15use reth_network_peers::{PeerId, WithPeerId};
16use reth_primitives_traits::{SealedBlock, SealedHeader};
17use std::{collections::HashMap, sync::Arc};
18
19#[derive(Clone, Debug)]
24pub struct TestFullBlockClient {
25 headers: Arc<Mutex<HashMap<B256, Header>>>,
26 bodies: Arc<Mutex<HashMap<B256, BlockBody>>>,
27 soft_limit: usize,
29}
30
31impl Default for TestFullBlockClient {
32 fn default() -> Self {
33 Self {
34 headers: Arc::new(Mutex::new(HashMap::default())),
35 bodies: Arc::new(Mutex::new(HashMap::default())),
36 soft_limit: 20,
37 }
38 }
39}
40
41impl TestFullBlockClient {
42 pub fn insert(&self, header: SealedHeader, body: BlockBody) {
44 let hash = header.hash();
45 self.headers.lock().insert(hash, header.unseal());
46 self.bodies.lock().insert(hash, body);
47 }
48
49 pub const fn set_soft_limit(&mut self, limit: usize) {
51 self.soft_limit = limit;
52 }
53
54 pub fn highest_block(&self) -> Option<SealedBlock<Block>> {
56 self.headers.lock().iter().max_by_key(|(_, header)| header.number).and_then(
57 |(hash, header)| {
58 self.bodies.lock().get(hash).map(|body| {
59 SealedBlock::from_parts_unchecked(header.clone(), body.clone(), *hash)
60 })
61 },
62 )
63 }
64}
65
66impl DownloadClient for TestFullBlockClient {
67 fn report_bad_message(&self, _peer_id: PeerId) {}
69
70 fn num_connected_peers(&self) -> usize {
74 1
75 }
76}
77
78impl HeadersClient for TestFullBlockClient {
80 type Header = Header;
81 type Output = futures::future::Ready<PeerRequestResult<Vec<Header>>>;
83
84 fn get_headers_with_priority(
95 &self,
96 request: HeadersRequest,
97 _priority: Priority,
98 ) -> Self::Output {
99 let headers = self.headers.lock();
100
101 let mut block: BlockHashOrNumber = match request.start {
103 BlockHashOrNumber::Hash(hash) => headers.get(&hash).cloned(),
104 BlockHashOrNumber::Number(num) => headers.values().find(|h| h.number == num).cloned(),
105 }
106 .map(|h| h.number.into())
107 .unwrap();
108
109 let resp = (0..request.limit)
111 .filter_map(|_| {
112 headers.iter().find_map(|(hash, header)| {
113 BlockNumHash::new(header.number, *hash).matches_block_or_num(&block).then(
115 || {
116 match request.direction {
117 HeadersDirection::Falling => block = header.parent_hash.into(),
118 HeadersDirection::Rising => block = (header.number + 1).into(),
119 }
120 header.clone()
121 },
122 )
123 })
124 })
125 .collect::<Vec<_>>();
126
127 futures::future::ready(Ok(WithPeerId::new(PeerId::random(), resp)))
129 }
130}
131
132impl BodiesClient for TestFullBlockClient {
134 type Body = BlockBody;
135 type Output = futures::future::Ready<PeerRequestResult<Vec<BlockBody>>>;
137
138 fn get_block_bodies_with_priority(
149 &self,
150 hashes: Vec<B256>,
151 _priority: Priority,
152 ) -> Self::Output {
153 let bodies = self.bodies.lock();
155
156 futures::future::ready(Ok(WithPeerId::new(
159 PeerId::random(),
160 hashes
161 .iter()
162 .filter_map(|hash| bodies.get(hash).cloned())
163 .take(self.soft_limit)
164 .collect(),
165 )))
166 }
167}
168
169impl BlockClient for TestFullBlockClient {
170 type Block = reth_ethereum_primitives::Block;
171}