reth_discv4/
config.rs

1//! A set of configuration parameters to tune the discovery protocol.
2//!
3//! This basis of this file has been taken from the discv5 codebase:
4//! <https://github.com/sigp/discv5>
5
6use alloy_primitives::bytes::Bytes;
7use alloy_rlp::Encodable;
8use reth_net_banlist::BanList;
9use reth_net_nat::{NatResolver, ResolveNatInterval};
10use reth_network_peers::NodeRecord;
11use std::{
12    collections::{HashMap, HashSet},
13    time::Duration,
14};
15
16/// Configuration parameters that define the performance of the discovery network.
17#[derive(Clone, Debug)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19pub struct Discv4Config {
20    /// Whether to enable the incoming packet filter. Default: false.
21    pub enable_packet_filter: bool,
22    /// Size of the channel buffer for outgoing messages.
23    pub udp_egress_message_buffer: usize,
24    /// Size of the channel buffer for incoming messages.
25    pub udp_ingress_message_buffer: usize,
26    /// The number of allowed consecutive failures for `FindNode` requests. Default: 5.
27    pub max_find_node_failures: u8,
28    /// The interval to use when checking for expired nodes that need to be re-pinged. Default:
29    /// 10min.
30    pub ping_interval: Duration,
31    /// The duration of we consider a ping timed out.
32    pub ping_expiration: Duration,
33    /// The rate at which new random lookups should be triggered.
34    pub lookup_interval: Duration,
35    /// The duration of we consider a `FindNode` request timed out.
36    pub request_timeout: Duration,
37    /// The duration after which we consider an enr request timed out.
38    pub enr_expiration: Duration,
39    /// The duration we set for neighbours responses.
40    pub neighbours_expiration: Duration,
41    /// Provides a way to ban peers and ips.
42    #[cfg_attr(feature = "serde", serde(skip))]
43    pub ban_list: BanList,
44    /// Set the default duration for which nodes are banned for. This timeouts are checked every 5
45    /// minutes, so the precision will be to the nearest 5 minutes. If set to `None`, bans from
46    /// the filter will last indefinitely. Default is 1 hour.
47    pub ban_duration: Option<Duration>,
48    /// Nodes to boot from.
49    pub bootstrap_nodes: HashSet<NodeRecord>,
50    /// Whether to randomly discover new peers.
51    ///
52    /// If true, the node will automatically randomly walk the DHT in order to find new peers.
53    pub enable_dht_random_walk: bool,
54    /// Whether to automatically lookup peers.
55    pub enable_lookup: bool,
56    /// Whether to enforce EIP-868 extension.
57    pub enable_eip868: bool,
58    /// Whether to respect expiration timestamps in messages.
59    pub enforce_expiration_timestamps: bool,
60    /// Additional pairs to include in The [`Enr`](enr::Enr) if EIP-868 extension is enabled <https://eips.ethereum.org/EIPS/eip-868>
61    pub additional_eip868_rlp_pairs: HashMap<Vec<u8>, Bytes>,
62    /// If configured, try to resolve public ip
63    pub external_ip_resolver: Option<NatResolver>,
64    /// If configured and a `external_ip_resolver` is configured, try to resolve the external ip
65    /// using this interval.
66    pub resolve_external_ip_interval: Option<Duration>,
67    /// The duration after which we consider a bond expired.
68    pub bond_expiration: Duration,
69}
70
71impl Discv4Config {
72    /// Returns a new default builder instance
73    pub fn builder() -> Discv4ConfigBuilder {
74        Default::default()
75    }
76
77    /// Add another key value pair to include in the ENR
78    pub fn add_eip868_pair(&mut self, key: impl Into<Vec<u8>>, value: impl Encodable) -> &mut Self {
79        self.add_eip868_rlp_pair(key, Bytes::from(alloy_rlp::encode(&value)))
80    }
81
82    /// Add another key value pair to include in the ENR
83    pub fn add_eip868_rlp_pair(&mut self, key: impl Into<Vec<u8>>, rlp: Bytes) -> &mut Self {
84        self.additional_eip868_rlp_pairs.insert(key.into(), rlp);
85        self
86    }
87
88    /// Extend additional key value pairs to include in the ENR
89    pub fn extend_eip868_rlp_pairs(
90        &mut self,
91        pairs: impl IntoIterator<Item = (impl Into<Vec<u8>>, Bytes)>,
92    ) -> &mut Self {
93        for (k, v) in pairs {
94            self.add_eip868_rlp_pair(k, v);
95        }
96        self
97    }
98
99    /// Returns the corresponding [`ResolveNatInterval`], if a [`NatResolver`] and an interval was
100    /// configured
101    pub fn resolve_external_ip_interval(&self) -> Option<ResolveNatInterval> {
102        let resolver = self.external_ip_resolver?;
103        let interval = self.resolve_external_ip_interval?;
104        Some(ResolveNatInterval::interval(resolver, interval))
105    }
106}
107
108impl Default for Discv4Config {
109    fn default() -> Self {
110        Self {
111            enable_packet_filter: false,
112            // This should be high enough to cover an entire recursive FindNode lookup which is
113            // includes sending FindNode to nodes it discovered in the rounds using the concurrency
114            // factor ALPHA
115            udp_egress_message_buffer: 1024,
116            // Every outgoing request will eventually lead to an incoming response
117            udp_ingress_message_buffer: 1024,
118            max_find_node_failures: 5,
119            ping_interval: Duration::from_secs(10),
120            // Unified expiration and timeout durations, mirrors geth's `expiration` duration
121            ping_expiration: Duration::from_secs(20),
122            bond_expiration: Duration::from_secs(60 * 60),
123            enr_expiration: Duration::from_secs(20),
124            neighbours_expiration: Duration::from_secs(20),
125            request_timeout: Duration::from_secs(20),
126
127            lookup_interval: Duration::from_secs(20),
128            ban_list: Default::default(),
129            ban_duration: Some(Duration::from_secs(60 * 60)), // 1 hour
130            bootstrap_nodes: Default::default(),
131            enable_dht_random_walk: true,
132            enable_lookup: true,
133            enable_eip868: true,
134            enforce_expiration_timestamps: true,
135            additional_eip868_rlp_pairs: Default::default(),
136            external_ip_resolver: Some(Default::default()),
137            // By default retry public IP using a 5min interval
138            resolve_external_ip_interval: Some(Duration::from_secs(60 * 5)),
139        }
140    }
141}
142
143/// Builder type for [`Discv4Config`]
144#[derive(Clone, Debug, Default)]
145#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
146pub struct Discv4ConfigBuilder {
147    config: Discv4Config,
148}
149
150impl Discv4ConfigBuilder {
151    /// Whether to enable the incoming packet filter.
152    pub const fn enable_packet_filter(&mut self) -> &mut Self {
153        self.config.enable_packet_filter = true;
154        self
155    }
156
157    /// Sets the channel size for incoming messages
158    pub const fn udp_ingress_message_buffer(
159        &mut self,
160        udp_ingress_message_buffer: usize,
161    ) -> &mut Self {
162        self.config.udp_ingress_message_buffer = udp_ingress_message_buffer;
163        self
164    }
165
166    /// Sets the channel size for outgoing messages
167    pub const fn udp_egress_message_buffer(
168        &mut self,
169        udp_egress_message_buffer: usize,
170    ) -> &mut Self {
171        self.config.udp_egress_message_buffer = udp_egress_message_buffer;
172        self
173    }
174
175    /// The number of allowed request failures for `findNode` requests.
176    pub const fn max_find_node_failures(&mut self, max_find_node_failures: u8) -> &mut Self {
177        self.config.max_find_node_failures = max_find_node_failures;
178        self
179    }
180
181    /// The time between pings to ensure connectivity amongst connected nodes.
182    pub const fn ping_interval(&mut self, interval: Duration) -> &mut Self {
183        self.config.ping_interval = interval;
184        self
185    }
186
187    /// Sets the timeout after which requests are considered timed out
188    pub const fn request_timeout(&mut self, duration: Duration) -> &mut Self {
189        self.config.request_timeout = duration;
190        self
191    }
192
193    /// Sets the expiration duration for pings
194    pub const fn ping_expiration(&mut self, duration: Duration) -> &mut Self {
195        self.config.ping_expiration = duration;
196        self
197    }
198
199    /// Sets the expiration duration for enr requests
200    pub const fn enr_request_expiration(&mut self, duration: Duration) -> &mut Self {
201        self.config.enr_expiration = duration;
202        self
203    }
204
205    /// Sets the expiration duration for lookup neighbor requests
206    pub const fn lookup_neighbours_expiration(&mut self, duration: Duration) -> &mut Self {
207        self.config.neighbours_expiration = duration;
208        self
209    }
210
211    /// Sets the expiration duration for a bond with a peer
212    pub const fn bond_expiration(&mut self, duration: Duration) -> &mut Self {
213        self.config.bond_expiration = duration;
214        self
215    }
216
217    /// Whether to discover random nodes in the DHT.
218    pub const fn enable_dht_random_walk(&mut self, enable_dht_random_walk: bool) -> &mut Self {
219        self.config.enable_dht_random_walk = enable_dht_random_walk;
220        self
221    }
222
223    /// Whether to automatically lookup
224    pub const fn enable_lookup(&mut self, enable_lookup: bool) -> &mut Self {
225        self.config.enable_lookup = enable_lookup;
226        self
227    }
228
229    /// Whether to enforce expiration timestamps in messages.
230    pub const fn enable_eip868(&mut self, enable_eip868: bool) -> &mut Self {
231        self.config.enable_eip868 = enable_eip868;
232        self
233    }
234
235    /// Whether to enable EIP-868
236    pub const fn enforce_expiration_timestamps(
237        &mut self,
238        enforce_expiration_timestamps: bool,
239    ) -> &mut Self {
240        self.config.enforce_expiration_timestamps = enforce_expiration_timestamps;
241        self
242    }
243
244    /// Add another key value pair to include in the ENR
245    pub fn add_eip868_pair(&mut self, key: impl Into<Vec<u8>>, value: impl Encodable) -> &mut Self {
246        self.add_eip868_rlp_pair(key, Bytes::from(alloy_rlp::encode(&value)))
247    }
248
249    /// Add another key value pair to include in the ENR
250    pub fn add_eip868_rlp_pair(&mut self, key: impl Into<Vec<u8>>, rlp: Bytes) -> &mut Self {
251        self.config.additional_eip868_rlp_pairs.insert(key.into(), rlp);
252        self
253    }
254
255    /// Extend additional key value pairs to include in the ENR
256    pub fn extend_eip868_rlp_pairs(
257        &mut self,
258        pairs: impl IntoIterator<Item = (impl Into<Vec<u8>>, Bytes)>,
259    ) -> &mut Self {
260        for (k, v) in pairs {
261            self.add_eip868_rlp_pair(k, v);
262        }
263        self
264    }
265
266    /// A set of lists that can ban IP's or `PeerIds` from the server. See
267    /// [`BanList`].
268    pub fn ban_list(&mut self, ban_list: BanList) -> &mut Self {
269        self.config.ban_list = ban_list;
270        self
271    }
272
273    /// Sets the lookup interval duration.
274    pub const fn lookup_interval(&mut self, lookup_interval: Duration) -> &mut Self {
275        self.config.lookup_interval = lookup_interval;
276        self
277    }
278
279    /// Set the default duration for which nodes are banned for. This timeouts are checked every 5
280    /// minutes, so the precision will be to the nearest 5 minutes. If set to `None`, bans from
281    /// the filter will last indefinitely. Default is 1 hour.
282    pub const fn ban_duration(&mut self, ban_duration: Option<Duration>) -> &mut Self {
283        self.config.ban_duration = ban_duration;
284        self
285    }
286
287    /// Adds a boot node
288    pub fn add_boot_node(&mut self, node: NodeRecord) -> &mut Self {
289        self.config.bootstrap_nodes.insert(node);
290        self
291    }
292
293    /// Adds multiple boot nodes
294    pub fn add_boot_nodes(&mut self, nodes: impl IntoIterator<Item = NodeRecord>) -> &mut Self {
295        self.config.bootstrap_nodes.extend(nodes);
296        self
297    }
298
299    /// Configures if and how the external IP of the node should be resolved.
300    pub const fn external_ip_resolver(
301        &mut self,
302        external_ip_resolver: Option<NatResolver>,
303    ) -> &mut Self {
304        self.config.external_ip_resolver = external_ip_resolver;
305        self
306    }
307
308    /// Sets the interval at which the external IP is to be resolved.
309    pub const fn resolve_external_ip_interval(
310        &mut self,
311        resolve_external_ip_interval: Option<Duration>,
312    ) -> &mut Self {
313        self.config.resolve_external_ip_interval = resolve_external_ip_interval;
314        self
315    }
316
317    /// Returns the configured [`Discv4Config`]
318    pub fn build(&self) -> Discv4Config {
319        self.config.clone()
320    }
321}
322
323#[cfg(test)]
324mod tests {
325    use super::*;
326
327    #[test]
328    fn test_config_builder() {
329        let mut builder = Discv4Config::builder();
330        let _ = builder
331            .enable_lookup(true)
332            .enable_dht_random_walk(true)
333            .add_boot_nodes(HashSet::new())
334            .ban_duration(None)
335            .lookup_interval(Duration::from_secs(3))
336            .enable_lookup(true)
337            .build();
338    }
339}