zebra_network/peer_set/set/
tests.rs1#![allow(clippy::unwrap_in_result)]
4
5#[cfg(test)]
6use std::net::SocketAddr;
7use std::{net::IpAddr, sync::Arc};
8
9use futures::channel::mpsc;
10#[cfg(test)]
11use futures::{stream, Stream, StreamExt};
12#[cfg(test)]
13use proptest::{collection::vec, prelude::*};
14#[cfg(test)]
15use proptest_derive::Arbitrary;
16use tokio::{
17 sync::{broadcast, watch},
18 task::JoinHandle,
19};
20#[cfg(test)]
21use tower::discover::Change;
22use tower::{discover::Discover, BoxError};
23use tracing::Span;
24
25#[cfg(test)]
26use zebra_chain::{block, parameters::NetworkUpgrade};
27use zebra_chain::{chain_tip::ChainTip, parameters::Network};
28
29use crate::{
30 address_book::AddressMetrics,
31 constants::DEFAULT_MAX_CONNS_PER_IP,
32 peer::{LoadTrackedClient, MinimumPeerVersion},
33 peer_set::{set::MorePeers, InventoryChange, PeerSet},
34 AddressBook, Config, PeerSocketAddr,
35};
36#[cfg(test)]
37use crate::{peer::ClientTestHarness, protocol::external::types::Version};
38
39pub mod consumer;
40
41#[cfg(test)]
42mod prop;
43
44#[cfg(test)]
45mod vectors;
46
47#[cfg(test)]
51const MAX_PEERS: usize = 20;
52
53#[cfg(test)]
55#[derive(Arbitrary, Debug)]
56struct PeerVersions {
57 #[proptest(strategy = "vec(any::<Version>(), 1..MAX_PEERS)")]
58 peer_versions: Vec<Version>,
59}
60
61#[cfg(test)]
62impl PeerVersions {
63 pub fn mock_peers(&self) -> (Vec<LoadTrackedClient>, Vec<ClientTestHarness>) {
72 let mut clients = Vec::with_capacity(self.peer_versions.len());
73 let mut harnesses = Vec::with_capacity(self.peer_versions.len());
74
75 for peer_version in &self.peer_versions {
76 let (client, harness) = ClientTestHarness::build()
77 .with_version(*peer_version)
78 .finish();
79
80 clients.push(client.into());
81 harnesses.push(harness);
82 }
83
84 (clients, harnesses)
85 }
86
87 pub fn mock_peer_discovery(
99 &self,
100 ) -> (
101 impl Stream<Item = Result<Change<PeerSocketAddr, LoadTrackedClient>, BoxError>>,
102 Vec<ClientTestHarness>,
103 ) {
104 let (clients, harnesses) = self.mock_peers();
105 let fake_ports = 1_u16..;
106
107 let discovered_peers_iterator = fake_ports.zip(clients).map(|(port, client)| {
108 let peer_address: PeerSocketAddr = SocketAddr::new([127, 0, 0, 1].into(), port).into();
109
110 Ok(Change::Insert(peer_address, client))
111 });
112
113 let discovered_peers = stream::iter(discovered_peers_iterator).chain(stream::pending());
114
115 (discovered_peers, harnesses)
116 }
117}
118
119#[cfg(test)]
121fn mock_peer_discovery_with_start_height(
122 start_height: block::Height,
123) -> (
124 impl Stream<Item = Result<Change<PeerSocketAddr, LoadTrackedClient>, BoxError>>,
125 ClientTestHarness,
126) {
127 let (client, harness) = ClientTestHarness::build()
128 .with_version(crate::constants::CURRENT_NETWORK_PROTOCOL_VERSION)
129 .with_start_height(start_height)
130 .finish();
131 let peer_address: PeerSocketAddr = SocketAddr::new([127, 0, 0, 1].into(), 1).into();
132 let discovery =
133 stream::iter([Ok(Change::Insert(peer_address, client.into()))]).chain(stream::pending());
134
135 (discovery, harness)
136}
137
138#[derive(Default)]
143pub(crate) struct PeerSetBuilder<D, C> {
144 config: Option<Config>,
145 discover: Option<D>,
146 demand_signal: Option<mpsc::Sender<MorePeers>>,
147 handle_rx: Option<tokio::sync::oneshot::Receiver<Vec<JoinHandle<Result<(), BoxError>>>>>,
148 inv_stream: Option<broadcast::Receiver<InventoryChange>>,
149 address_book: Option<Arc<std::sync::Mutex<AddressBook>>>,
150 minimum_peer_version: Option<MinimumPeerVersion<C>>,
151 max_conns_per_ip: Option<usize>,
152 block_gossip_peer_ips: Vec<IpAddr>,
153}
154
155impl PeerSetBuilder<(), ()> {
156 pub fn new() -> Self {
158 PeerSetBuilder::default()
159 }
160}
161
162impl<D, C> PeerSetBuilder<D, C> {
163 #[cfg(test)]
165 pub fn with_config(self, config: Config) -> PeerSetBuilder<D, C> {
166 PeerSetBuilder {
167 config: Some(config),
168 ..self
169 }
170 }
171
172 #[cfg(test)]
174 pub fn with_block_gossip_peer_ips(
175 self,
176 block_gossip_peer_ips: Vec<IpAddr>,
177 ) -> PeerSetBuilder<D, C> {
178 PeerSetBuilder {
179 block_gossip_peer_ips,
180 ..self
181 }
182 }
183
184 pub fn with_discover<NewD>(self, discover: NewD) -> PeerSetBuilder<NewD, C> {
186 PeerSetBuilder {
187 discover: Some(discover),
188 config: self.config,
189 demand_signal: self.demand_signal,
190 handle_rx: self.handle_rx,
191 inv_stream: self.inv_stream,
192 address_book: self.address_book,
193 minimum_peer_version: self.minimum_peer_version,
194 max_conns_per_ip: self.max_conns_per_ip,
195 block_gossip_peer_ips: self.block_gossip_peer_ips,
196 }
197 }
198
199 pub fn with_minimum_peer_version<NewC>(
201 self,
202 minimum_peer_version: MinimumPeerVersion<NewC>,
203 ) -> PeerSetBuilder<D, NewC> {
204 PeerSetBuilder {
205 config: self.config,
206 discover: self.discover,
207 demand_signal: self.demand_signal,
208 handle_rx: self.handle_rx,
209 inv_stream: self.inv_stream,
210 address_book: self.address_book,
211 minimum_peer_version: Some(minimum_peer_version),
212 max_conns_per_ip: self.max_conns_per_ip,
213 block_gossip_peer_ips: self.block_gossip_peer_ips,
214 }
215 }
216
217 #[cfg(test)]
219 pub fn max_conns_per_ip(self, max_conns_per_ip: usize) -> PeerSetBuilder<D, C> {
220 assert!(
221 max_conns_per_ip > 0,
222 "max_conns_per_ip must be greater than zero"
223 );
224
225 PeerSetBuilder {
226 config: self.config,
227 discover: self.discover,
228 demand_signal: self.demand_signal,
229 handle_rx: self.handle_rx,
230 inv_stream: self.inv_stream,
231 address_book: self.address_book,
232 minimum_peer_version: self.minimum_peer_version,
233 max_conns_per_ip: Some(max_conns_per_ip),
234 block_gossip_peer_ips: self.block_gossip_peer_ips,
235 }
236 }
237}
238
239impl<D, C> PeerSetBuilder<D, C>
240where
241 D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin,
242 D::Error: Into<BoxError>,
243 C: ChainTip,
244{
245 pub fn build(self) -> (PeerSet<D, C>, PeerSetGuard) {
250 let mut guard = PeerSetGuard::new();
251
252 let config = self.config.unwrap_or_default();
253 let discover = self.discover.expect("`discover` must be set");
254 let minimum_peer_version = self
255 .minimum_peer_version
256 .expect("`minimum_peer_version` must be set");
257 let max_conns_per_ip = self.max_conns_per_ip;
258
259 let demand_signal = self
260 .demand_signal
261 .unwrap_or_else(|| guard.create_demand_sender());
262 let handle_rx = self
263 .handle_rx
264 .unwrap_or_else(|| guard.create_background_tasks_receiver());
265 let inv_stream = self
266 .inv_stream
267 .unwrap_or_else(|| guard.create_inventory_receiver());
268
269 let address_metrics = guard.prepare_address_book(self.address_book);
270 let (_bans_sender, bans_receiver) = tokio::sync::watch::channel(Default::default());
271
272 let peer_set = PeerSet::new(
273 &config,
274 self.block_gossip_peer_ips,
275 discover,
276 demand_signal,
277 handle_rx,
278 inv_stream,
279 bans_receiver,
280 address_metrics,
281 minimum_peer_version,
282 max_conns_per_ip,
283 );
284
285 (peer_set, guard)
286 }
287}
288
289#[derive(Default)]
291pub struct PeerSetGuard {
292 background_tasks_sender:
293 Option<tokio::sync::oneshot::Sender<Vec<JoinHandle<Result<(), BoxError>>>>>,
294 demand_receiver: Option<mpsc::Receiver<MorePeers>>,
295 inventory_sender: Option<broadcast::Sender<InventoryChange>>,
296 address_book: Option<Arc<std::sync::Mutex<AddressBook>>>,
297}
298
299impl PeerSetGuard {
300 pub fn new() -> Self {
302 PeerSetGuard::default()
303 }
304
305 #[allow(dead_code)]
307 pub fn background_tasks_sender(
308 &mut self,
309 ) -> &mut Option<tokio::sync::oneshot::Sender<Vec<JoinHandle<Result<(), BoxError>>>>> {
310 &mut self.background_tasks_sender
311 }
312
313 #[allow(dead_code)]
315 pub fn demand_receiver(&mut self) -> &mut Option<mpsc::Receiver<MorePeers>> {
316 &mut self.demand_receiver
317 }
318
319 pub fn inventory_sender(&mut self) -> &mut Option<broadcast::Sender<InventoryChange>> {
321 &mut self.inventory_sender
322 }
323
324 #[allow(dead_code)]
326 pub fn address_book(&mut self) -> &mut Option<Arc<std::sync::Mutex<AddressBook>>> {
327 &mut self.address_book
328 }
329
330 pub fn create_background_tasks_receiver(
335 &mut self,
336 ) -> tokio::sync::oneshot::Receiver<Vec<JoinHandle<Result<(), BoxError>>>> {
337 let (sender, receiver) = tokio::sync::oneshot::channel();
338
339 self.background_tasks_sender = Some(sender);
340
341 receiver
342 }
343
344 pub fn create_demand_sender(&mut self) -> mpsc::Sender<MorePeers> {
349 let (sender, receiver) = mpsc::channel(1);
350
351 self.demand_receiver = Some(receiver);
352
353 sender
354 }
355
356 pub fn create_inventory_receiver(&mut self) -> broadcast::Receiver<InventoryChange> {
361 let (sender, receiver) = broadcast::channel(1);
362
363 self.inventory_sender = Some(sender);
364
365 receiver
366 }
367
368 pub fn prepare_address_book(
377 &mut self,
378 maybe_address_book: Option<Arc<std::sync::Mutex<AddressBook>>>,
379 ) -> watch::Receiver<AddressMetrics> {
380 let address_book = maybe_address_book.unwrap_or_else(Self::fallback_address_book);
381 let metrics_watcher = address_book
382 .lock()
383 .expect("unexpected panic in previous address book mutex guard")
384 .address_metrics_watcher();
385
386 self.address_book = Some(address_book);
387
388 metrics_watcher
389 }
390
391 fn fallback_address_book() -> Arc<std::sync::Mutex<AddressBook>> {
393 let local_listener = "127.0.0.1:1000"
394 .parse()
395 .expect("Invalid local listener address");
396 let address_book = AddressBook::new(
397 local_listener,
398 &Network::Mainnet,
399 DEFAULT_MAX_CONNS_PER_IP,
400 Span::none(),
401 );
402
403 Arc::new(std::sync::Mutex::new(address_book))
404 }
405}
406
407#[cfg(test)]
410#[derive(Clone, Debug)]
411pub struct BlockHeightPairAcrossNetworkUpgrades {
412 pub network: Network,
415
416 pub before_upgrade: block::Height,
418
419 pub after_upgrade: block::Height,
421}
422
423#[cfg(test)]
424impl Arbitrary for BlockHeightPairAcrossNetworkUpgrades {
425 type Parameters = ();
426
427 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
428 any::<(Network, NetworkUpgrade)>()
429 .prop_filter("no block height before genesis", |(_, upgrade)| {
431 !matches!(upgrade, NetworkUpgrade::Genesis)
432 })
433 .prop_filter_map(
435 "missing activation height for network upgrade",
436 |(network, upgrade)| {
437 upgrade
438 .activation_height(&network)
439 .map(|height| (network, height))
440 },
441 )
442 .prop_flat_map(|(network, activation_height)| {
444 let before_upgrade_strategy = 0..activation_height.0;
445 let after_upgrade_strategy = activation_height.0..;
446
447 (
448 Just(network),
449 before_upgrade_strategy,
450 after_upgrade_strategy,
451 )
452 })
453 .prop_map(|(network, before_upgrade_height, after_upgrade_height)| {
455 BlockHeightPairAcrossNetworkUpgrades {
456 network,
457 before_upgrade: block::Height(before_upgrade_height),
458 after_upgrade: block::Height(after_upgrade_height),
459 }
460 })
461 .boxed()
462 }
463
464 type Strategy = BoxedStrategy<Self>;
465}