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                (
73                    Just(addr),
74                    vec(MetaAddrChange::addr_strategy(addr.addr), 1..max_addr_change),
75                )
76            })
77            .boxed()
78    }
79
80    /// Create a strategy that generates [`IpAddr`]s for [`MetaAddrChange`]s which are ready for
81    /// outbound connections.
82    pub fn ready_outbound_strategy_ip() -> BoxedStrategy<IpAddr> {
83        any::<IpAddr>()
84            .prop_filter("failed MetaAddr::is_valid_for_outbound", |ip| {
85                !ip.is_unspecified()
86            })
87            .boxed()
88    }
89
90    /// Create a strategy that generates port numbers for [`MetaAddr`]s which are ready for
91    /// outbound connections.
92    ///
93    /// Currently, all generated [`MetaAddr`]s are the [`NeverAttemptedGossiped`][1] variant.
94    ///
95    /// TODO: Generate all [`MetaAddr`] variants, and give them ready fields.
96    ///
97    /// [1]: super::NeverAttemptedGossiped
98    pub fn ready_outbound_strategy_port() -> BoxedStrategy<u16> {
99        (
100            canonical_peer_addr_strategy(),
101            any::<PeerServices>(),
102            any::<DateTime32>(),
103        )
104            .prop_filter_map(
105                "failed MetaAddr::is_valid_for_outbound",
106                |(addr, services, local_now)| {
107                    let addr = MetaAddr::new_gossiped_meta_addr(addr, services, local_now);
108
109                    if addr.last_known_info_is_valid_for_outbound(&Mainnet) {
110                        Some(addr.addr.port())
111                    } else {
112                        None
113                    }
114                },
115            )
116            .boxed()
117    }
118}