zebra_network/meta_addr/arbitrary.rs
1//! Randomised test data generation for MetaAddr.
2
3use std::net::IpAddr;
4
5use proptest::{collection::vec, prelude::*};
6
7use zebra_chain::{parameters::Network::*, serialization::DateTime32};
8
9use crate::protocol::external::arbitrary::canonical_peer_addr_strategy;
10
11use super::{MetaAddr, MetaAddrChange, PeerServices, PeerSocketAddr};
12
13/// The largest number of random changes we want to apply to a [`MetaAddr`].
14///
15/// This should be at least twice the number of [`PeerAddrState`][1]s, so the
16/// tests can cover multiple transitions through every state.
17///
18/// [1]: super::PeerAddrState
19#[allow(dead_code)]
20pub const MAX_ADDR_CHANGE: usize = 15;
21
22/// The largest number of random addresses we want to add to an [`AddressBook`][2].
23///
24/// This should be at least the number of [`PeerAddrState`][1]s, so the tests
25/// can cover interactions between addresses in different states.
26///
27/// [1]: super::PeerAddrState
28/// [2]: crate::AddressBook
29#[allow(dead_code)]
30pub const MAX_META_ADDR: usize = 8;
31
32impl MetaAddr {
33 /// Create a strategy that generates [`MetaAddr`]s in the
34 /// [`NeverAttemptedGossiped`][1] state.
35 ///
36 /// [1]: super::PeerAddrState::NeverAttemptedGossiped
37 pub fn gossiped_strategy() -> BoxedStrategy<Self> {
38 (
39 canonical_peer_addr_strategy(),
40 any::<PeerServices>(),
41 any::<DateTime32>(),
42 )
43 .prop_map(|(addr, untrusted_services, untrusted_last_seen)| {
44 MetaAddr::new_gossiped_meta_addr(addr, untrusted_services, untrusted_last_seen)
45 })
46 .boxed()
47 }
48}
49
50impl MetaAddrChange {
51 /// Returns a strategy which generates changes for `addr`.
52 ///
53 /// `addr` is typically generated by the `canonical_peer_addr` strategy.
54 pub fn addr_strategy(addr: PeerSocketAddr) -> BoxedStrategy<Self> {
55 any::<MetaAddrChange>()
56 .prop_map(move |mut change| {
57 change.set_addr(addr);
58 change
59 })
60 .boxed()
61 }
62
63 /// Returns a strategy which generates a `MetaAddr`, and a vector of up to
64 /// `max_addr_change` changes.
65 ///
66 /// The address and the changes all have matching `PeerSocketAddr`s.
67 pub fn addr_changes_strategy(
68 max_addr_change: usize,
69 ) -> BoxedStrategy<(MetaAddr, Vec<MetaAddrChange>)> {
70 any::<MetaAddr>()
71 .prop_flat_map(move |addr| {
72 let peer_addr = addr.addr;
73 (
74 Just(addr),
75 vec(MetaAddrChange::addr_strategy(peer_addr), 1..max_addr_change),
76 )
77 })
78 .boxed()
79 }
80
81 /// Create a strategy that generates [`IpAddr`]s for [`MetaAddrChange`]s which are ready for
82 /// outbound connections.
83 pub fn ready_outbound_strategy_ip() -> BoxedStrategy<IpAddr> {
84 any::<IpAddr>()
85 .prop_filter("failed MetaAddr::is_valid_for_outbound", |ip| {
86 !ip.is_unspecified()
87 })
88 .boxed()
89 }
90
91 /// Create a strategy that generates port numbers for [`MetaAddr`]s which are ready for
92 /// outbound connections.
93 ///
94 /// Currently, all generated [`MetaAddr`]s are the [`NeverAttemptedGossiped`][1] variant.
95 ///
96 /// TODO: Generate all [`MetaAddr`] variants, and give them ready fields.
97 ///
98 /// [1]: super::NeverAttemptedGossiped
99 pub fn ready_outbound_strategy_port() -> BoxedStrategy<u16> {
100 (
101 canonical_peer_addr_strategy(),
102 any::<PeerServices>(),
103 any::<DateTime32>(),
104 )
105 .prop_filter_map(
106 "failed MetaAddr::is_valid_for_outbound",
107 |(addr, services, local_now)| {
108 let addr = MetaAddr::new_gossiped_meta_addr(addr, services, local_now);
109
110 if addr.last_known_info_is_valid_for_outbound(&Mainnet) {
111 Some(addr.addr.port())
112 } else {
113 None
114 }
115 },
116 )
117 .boxed()
118 }
119}