1use 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#[derive(Clone, Debug)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19pub struct Discv4Config {
20 pub udp_egress_message_buffer: usize,
22 pub udp_ingress_message_buffer: usize,
24 pub max_find_node_failures: u8,
26 pub ping_interval: Duration,
29 pub ping_expiration: Duration,
31 pub lookup_interval: Duration,
33 pub request_timeout: Duration,
35 pub enr_expiration: Duration,
37 pub neighbours_expiration: Duration,
39 #[cfg_attr(feature = "serde", serde(skip))]
41 pub ban_list: BanList,
42 pub bootstrap_nodes: HashSet<NodeRecord>,
44 pub enable_dht_random_walk: bool,
48 pub enable_lookup: bool,
50 pub enable_eip868: bool,
52 pub enforce_expiration_timestamps: bool,
54 pub additional_eip868_rlp_pairs: HashMap<Vec<u8>, Bytes>,
56 pub external_ip_resolver: Option<NatResolver>,
58 pub resolve_external_ip_interval: Option<Duration>,
61 pub bond_expiration: Duration,
63}
64
65impl Discv4Config {
66 pub fn builder() -> Discv4ConfigBuilder {
68 Default::default()
69 }
70
71 pub fn add_eip868_pair(&mut self, key: impl Into<Vec<u8>>, value: impl Encodable) -> &mut Self {
73 self.add_eip868_rlp_pair(key, Bytes::from(alloy_rlp::encode(&value)))
74 }
75
76 pub fn add_eip868_rlp_pair(&mut self, key: impl Into<Vec<u8>>, rlp: Bytes) -> &mut Self {
78 self.additional_eip868_rlp_pairs.insert(key.into(), rlp);
79 self
80 }
81
82 pub fn extend_eip868_rlp_pairs(
84 &mut self,
85 pairs: impl IntoIterator<Item = (impl Into<Vec<u8>>, Bytes)>,
86 ) -> &mut Self {
87 for (k, v) in pairs {
88 self.add_eip868_rlp_pair(k, v);
89 }
90 self
91 }
92
93 pub fn resolve_external_ip_interval(&self) -> Option<ResolveNatInterval> {
96 let resolver = self.external_ip_resolver?;
97 let interval = self.resolve_external_ip_interval?;
98 Some(ResolveNatInterval::interval_at(resolver, tokio::time::Instant::now(), interval))
99 }
100}
101
102impl Default for Discv4Config {
103 fn default() -> Self {
104 Self {
105 udp_egress_message_buffer: 1024,
109 udp_ingress_message_buffer: 1024,
111 max_find_node_failures: 5,
112 ping_interval: Duration::from_secs(10),
113 ping_expiration: Duration::from_secs(20),
115 bond_expiration: Duration::from_secs(60 * 60),
116 enr_expiration: Duration::from_secs(20),
117 neighbours_expiration: Duration::from_secs(20),
118 request_timeout: Duration::from_secs(20),
119
120 lookup_interval: Duration::from_secs(20),
121 ban_list: Default::default(),
122 bootstrap_nodes: Default::default(),
123 enable_dht_random_walk: true,
124 enable_lookup: true,
125 enable_eip868: true,
126 enforce_expiration_timestamps: true,
127 additional_eip868_rlp_pairs: Default::default(),
128 external_ip_resolver: Some(Default::default()),
129 resolve_external_ip_interval: Some(Duration::from_secs(60 * 5)),
131 }
132 }
133}
134
135#[derive(Clone, Debug, Default)]
137#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
138pub struct Discv4ConfigBuilder {
139 config: Discv4Config,
140}
141
142impl Discv4ConfigBuilder {
143 pub const fn udp_ingress_message_buffer(
145 &mut self,
146 udp_ingress_message_buffer: usize,
147 ) -> &mut Self {
148 self.config.udp_ingress_message_buffer = udp_ingress_message_buffer;
149 self
150 }
151
152 pub const fn udp_egress_message_buffer(
154 &mut self,
155 udp_egress_message_buffer: usize,
156 ) -> &mut Self {
157 self.config.udp_egress_message_buffer = udp_egress_message_buffer;
158 self
159 }
160
161 pub const fn max_find_node_failures(&mut self, max_find_node_failures: u8) -> &mut Self {
163 self.config.max_find_node_failures = max_find_node_failures;
164 self
165 }
166
167 pub const fn ping_interval(&mut self, interval: Duration) -> &mut Self {
169 self.config.ping_interval = interval;
170 self
171 }
172
173 pub const fn request_timeout(&mut self, duration: Duration) -> &mut Self {
175 self.config.request_timeout = duration;
176 self
177 }
178
179 pub const fn ping_expiration(&mut self, duration: Duration) -> &mut Self {
181 self.config.ping_expiration = duration;
182 self
183 }
184
185 pub const fn enr_request_expiration(&mut self, duration: Duration) -> &mut Self {
187 self.config.enr_expiration = duration;
188 self
189 }
190
191 pub const fn lookup_neighbours_expiration(&mut self, duration: Duration) -> &mut Self {
193 self.config.neighbours_expiration = duration;
194 self
195 }
196
197 pub const fn bond_expiration(&mut self, duration: Duration) -> &mut Self {
199 self.config.bond_expiration = duration;
200 self
201 }
202
203 pub const fn enable_dht_random_walk(&mut self, enable_dht_random_walk: bool) -> &mut Self {
205 self.config.enable_dht_random_walk = enable_dht_random_walk;
206 self
207 }
208
209 pub const fn enable_lookup(&mut self, enable_lookup: bool) -> &mut Self {
211 self.config.enable_lookup = enable_lookup;
212 self
213 }
214
215 pub const fn enable_eip868(&mut self, enable_eip868: bool) -> &mut Self {
217 self.config.enable_eip868 = enable_eip868;
218 self
219 }
220
221 pub const fn enforce_expiration_timestamps(
223 &mut self,
224 enforce_expiration_timestamps: bool,
225 ) -> &mut Self {
226 self.config.enforce_expiration_timestamps = enforce_expiration_timestamps;
227 self
228 }
229
230 pub fn add_eip868_pair(&mut self, key: impl Into<Vec<u8>>, value: impl Encodable) -> &mut Self {
232 self.add_eip868_rlp_pair(key, Bytes::from(alloy_rlp::encode(&value)))
233 }
234
235 pub fn add_eip868_rlp_pair(&mut self, key: impl Into<Vec<u8>>, rlp: Bytes) -> &mut Self {
237 self.config.additional_eip868_rlp_pairs.insert(key.into(), rlp);
238 self
239 }
240
241 pub fn extend_eip868_rlp_pairs(
243 &mut self,
244 pairs: impl IntoIterator<Item = (impl Into<Vec<u8>>, Bytes)>,
245 ) -> &mut Self {
246 for (k, v) in pairs {
247 self.add_eip868_rlp_pair(k, v);
248 }
249 self
250 }
251
252 pub fn ban_list(&mut self, ban_list: BanList) -> &mut Self {
255 self.config.ban_list = ban_list;
256 self
257 }
258
259 pub const fn lookup_interval(&mut self, lookup_interval: Duration) -> &mut Self {
261 self.config.lookup_interval = lookup_interval;
262 self
263 }
264
265 pub fn add_boot_node(&mut self, node: NodeRecord) -> &mut Self {
267 self.config.bootstrap_nodes.insert(node);
268 self
269 }
270
271 pub fn add_boot_nodes(&mut self, nodes: impl IntoIterator<Item = NodeRecord>) -> &mut Self {
273 self.config.bootstrap_nodes.extend(nodes);
274 self
275 }
276
277 pub const fn external_ip_resolver(
279 &mut self,
280 external_ip_resolver: Option<NatResolver>,
281 ) -> &mut Self {
282 self.config.external_ip_resolver = external_ip_resolver;
283 self
284 }
285
286 pub const fn resolve_external_ip_interval(
288 &mut self,
289 resolve_external_ip_interval: Option<Duration>,
290 ) -> &mut Self {
291 self.config.resolve_external_ip_interval = resolve_external_ip_interval;
292 self
293 }
294
295 pub fn build(&self) -> Discv4Config {
297 self.config.clone()
298 }
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304
305 #[test]
306 fn test_config_builder() {
307 let mut builder = Discv4Config::builder();
308 let _ = builder
309 .enable_lookup(true)
310 .enable_dht_random_walk(true)
311 .add_boot_nodes(HashSet::new())
312 .lookup_interval(Duration::from_secs(3))
313 .enable_lookup(true)
314 .build();
315 }
316
317 #[tokio::test]
318 async fn test_resolve_external_ip_interval_uses_interval_at() {
319 use reth_net_nat::NatResolver;
320 use std::net::{IpAddr, Ipv4Addr};
321
322 let ip_addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1));
323
324 let mut builder = Discv4Config::builder();
326 builder.external_ip_resolver(Some(NatResolver::ExternalIp(ip_addr)));
327 builder.resolve_external_ip_interval(Some(Duration::from_secs(60 * 5)));
328 let config = builder.build();
329
330 let mut interval = config.resolve_external_ip_interval().expect("should have interval");
332
333 let ip = interval.tick().await;
335 assert_eq!(ip, Some(ip_addr));
336 }
337}