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}