reth_network_p2p/test_utils/full_block.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
use crate::{
bodies::client::BodiesClient,
download::DownloadClient,
error::PeerRequestResult,
headers::client::{HeadersClient, HeadersRequest},
priority::Priority,
};
use alloy_consensus::Header;
use alloy_eips::{BlockHashOrNumber, BlockNumHash};
use alloy_primitives::B256;
use parking_lot::Mutex;
use reth_eth_wire_types::HeadersDirection;
use reth_network_peers::{PeerId, WithPeerId};
use reth_primitives::{BlockBody, SealedBlock, SealedHeader};
use std::{collections::HashMap, sync::Arc};
/// A headers+bodies client implementation that does nothing.
#[derive(Debug, Default, Clone)]
#[non_exhaustive]
pub struct NoopFullBlockClient;
/// Implements the `DownloadClient` trait for the `NoopFullBlockClient` struct.
impl DownloadClient for NoopFullBlockClient {
/// Reports a bad message received from a peer.
///
/// # Arguments
///
/// * `_peer_id` - Identifier for the peer sending the bad message (unused in this
/// implementation).
fn report_bad_message(&self, _peer_id: PeerId) {}
/// Retrieves the number of connected peers.
///
/// # Returns
///
/// The number of connected peers, which is always zero in this implementation.
fn num_connected_peers(&self) -> usize {
0
}
}
/// Implements the `BodiesClient` trait for the `NoopFullBlockClient` struct.
impl BodiesClient for NoopFullBlockClient {
type Body = BlockBody;
/// Defines the output type of the function.
type Output = futures::future::Ready<PeerRequestResult<Vec<BlockBody>>>;
/// Retrieves block bodies based on provided hashes and priority.
///
/// # Arguments
///
/// * `_hashes` - A vector of block hashes (unused in this implementation).
/// * `_priority` - Priority level for block body retrieval (unused in this implementation).
///
/// # Returns
///
/// A future containing an empty vector of block bodies and a randomly generated `PeerId`.
fn get_block_bodies_with_priority(
&self,
_hashes: Vec<B256>,
_priority: Priority,
) -> Self::Output {
// Create a future that immediately returns an empty vector of block bodies and a random
// PeerId.
futures::future::ready(Ok(WithPeerId::new(PeerId::random(), vec![])))
}
}
impl HeadersClient for NoopFullBlockClient {
type Header = Header;
/// The output type representing a future containing a peer request result with a vector of
/// headers.
type Output = futures::future::Ready<PeerRequestResult<Vec<Header>>>;
/// Retrieves headers with a specified priority level.
///
/// This implementation does nothing and returns an empty vector of headers.
///
/// # Arguments
///
/// * `_request` - A request for headers (unused in this implementation).
/// * `_priority` - The priority level for the headers request (unused in this implementation).
///
/// # Returns
///
/// Always returns a ready future with an empty vector of headers wrapped in a
/// `PeerRequestResult`.
fn get_headers_with_priority(
&self,
_request: HeadersRequest,
_priority: Priority,
) -> Self::Output {
futures::future::ready(Ok(WithPeerId::new(PeerId::random(), vec![])))
}
}
/// A headers+bodies client that stores the headers and bodies in memory, with an artificial soft
/// bodies response limit that is set to 20 by default.
///
/// This full block client can be [Clone]d and shared between multiple tasks.
#[derive(Clone, Debug)]
pub struct TestFullBlockClient {
headers: Arc<Mutex<HashMap<B256, Header>>>,
bodies: Arc<Mutex<HashMap<B256, BlockBody>>>,
// soft response limit, max number of bodies to respond with
soft_limit: usize,
}
impl Default for TestFullBlockClient {
fn default() -> Self {
Self {
headers: Arc::new(Mutex::new(HashMap::default())),
bodies: Arc::new(Mutex::new(HashMap::default())),
soft_limit: 20,
}
}
}
impl TestFullBlockClient {
/// Insert a header and body into the client maps.
pub fn insert(&self, header: SealedHeader, body: BlockBody) {
let hash = header.hash();
self.headers.lock().insert(hash, header.unseal());
self.bodies.lock().insert(hash, body);
}
/// Set the soft response limit.
pub fn set_soft_limit(&mut self, limit: usize) {
self.soft_limit = limit;
}
/// Get the block with the highest block number.
pub fn highest_block(&self) -> Option<SealedBlock> {
self.headers.lock().iter().max_by_key(|(_, header)| header.number).and_then(
|(hash, header)| {
self.bodies.lock().get(hash).map(|body| {
SealedBlock::new(SealedHeader::new(header.clone(), *hash), body.clone())
})
},
)
}
}
impl DownloadClient for TestFullBlockClient {
/// Reports a bad message from a specific peer.
fn report_bad_message(&self, _peer_id: PeerId) {}
/// Retrieves the number of connected peers.
///
/// Returns the number of connected peers in the test scenario (1).
fn num_connected_peers(&self) -> usize {
1
}
}
/// Implements the `HeadersClient` trait for the `TestFullBlockClient` struct.
impl HeadersClient for TestFullBlockClient {
type Header = Header;
/// Specifies the associated output type.
type Output = futures::future::Ready<PeerRequestResult<Vec<Header>>>;
/// Retrieves headers with a given priority level.
///
/// # Arguments
///
/// * `request` - A `HeadersRequest` indicating the headers to retrieve.
/// * `_priority` - A `Priority` level for the request.
///
/// # Returns
///
/// A `Ready` future containing a `PeerRequestResult` with a vector of retrieved headers.
fn get_headers_with_priority(
&self,
request: HeadersRequest,
_priority: Priority,
) -> Self::Output {
let headers = self.headers.lock();
// Initializes the block hash or number.
let mut block: BlockHashOrNumber = match request.start {
BlockHashOrNumber::Hash(hash) => headers.get(&hash).cloned(),
BlockHashOrNumber::Number(num) => headers.values().find(|h| h.number == num).cloned(),
}
.map(|h| h.number.into())
.unwrap();
// Retrieves headers based on the provided limit and request direction.
let resp = (0..request.limit)
.filter_map(|_| {
headers.iter().find_map(|(hash, header)| {
// Checks if the header matches the specified block or number.
BlockNumHash::new(header.number, *hash).matches_block_or_num(&block).then(
|| {
match request.direction {
HeadersDirection::Falling => block = header.parent_hash.into(),
HeadersDirection::Rising => block = (header.number + 1).into(),
}
header.clone()
},
)
})
})
.collect::<Vec<_>>();
// Returns a future containing the retrieved headers with a random peer ID.
futures::future::ready(Ok(WithPeerId::new(PeerId::random(), resp)))
}
}
/// Implements the `BodiesClient` trait for the `TestFullBlockClient` struct.
impl BodiesClient for TestFullBlockClient {
type Body = BlockBody;
/// Defines the output type of the function.
type Output = futures::future::Ready<PeerRequestResult<Vec<BlockBody>>>;
/// Retrieves block bodies corresponding to provided hashes with a given priority.
///
/// # Arguments
///
/// * `hashes` - A vector of block hashes to retrieve bodies for.
/// * `_priority` - Priority level for block body retrieval (unused in this implementation).
///
/// # Returns
///
/// A future containing the result of the block body retrieval operation.
fn get_block_bodies_with_priority(
&self,
hashes: Vec<B256>,
_priority: Priority,
) -> Self::Output {
// Acquire a lock on the bodies.
let bodies = self.bodies.lock();
// Create a future that immediately returns the result of the block body retrieval
// operation.
futures::future::ready(Ok(WithPeerId::new(
PeerId::random(),
hashes
.iter()
.filter_map(|hash| bodies.get(hash).cloned())
.take(self.soft_limit)
.collect(),
)))
}
}