Skip to content

Commit cf6da46

Browse files
weixie.cuicuiweixie
authored andcommitted
feat(network): add support for bootnodes_v4(enode) and bootnodes_v5(enode and enr) in config file similiar in geth
1 parent 2f58f67 commit cf6da46

File tree

7 files changed

+312
-14
lines changed

7 files changed

+312
-14
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/config/src/config.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,7 @@ mod tests {
595595
use crate::PruneConfig;
596596
use alloy_primitives::Address;
597597
use reth_network_peers::TrustedPeer;
598+
use reth_network_types::peers::Discv5BootNode;
598599
use reth_prune_types::{PruneMode, PruneModes, ReceiptsLogPruneConfig};
599600
use std::{collections::BTreeMap, path::Path, str::FromStr, time::Duration};
600601

@@ -1164,4 +1165,133 @@ connect_trusted_nodes_only = true
11641165
assert!(conf.peers.trusted_nodes.contains(&node));
11651166
}
11661167
}
1168+
1169+
#[test]
1170+
fn test_bootnodes_v4_config() {
1171+
let reth_toml = r#"
1172+
[peers]
1173+
bootnodes_v4 = [
1174+
"enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303",
1175+
"enode://22a8232c3abc76a16ae9d6c3b164f98775fe226f0917b0ca871128a74a8e9630b458460865bab457221f1d448dd9791d24c4e5d88786180ac185df813a68d4de@3.209.45.79:30303"
1176+
]
1177+
"#;
1178+
1179+
let conf: Config = toml::from_str(reth_toml).unwrap();
1180+
assert_eq!(conf.peers.bootnodes_v4.len(), 2);
1181+
1182+
let expected_enodes = vec![
1183+
"enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303",
1184+
"enode://22a8232c3abc76a16ae9d6c3b164f98775fe226f0917b0ca871128a74a8e9630b458460865bab457221f1d448dd9791d24c4e5d88786180ac185df813a68d4de@3.209.45.79:30303",
1185+
];
1186+
1187+
for enode in expected_enodes {
1188+
let node = TrustedPeer::from_str(enode).unwrap();
1189+
assert!(conf.peers.bootnodes_v4.contains(&node));
1190+
}
1191+
}
1192+
1193+
#[test]
1194+
fn test_bootnodes_v5_config_enode() {
1195+
let reth_toml = r#"
1196+
[peers]
1197+
bootnodes_v5 = [
1198+
"enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303",
1199+
"enode://22a8232c3abc76a16ae9d6c3b164f98775fe226f0917b0ca871128a74a8e9630b458460865bab457221f1d448dd9791d24c4e5d88786180ac185df813a68d4de@3.209.45.79:30303"
1200+
]
1201+
"#;
1202+
1203+
let conf: Config = toml::from_str(reth_toml).unwrap();
1204+
assert_eq!(conf.peers.bootnodes_v5.len(), 2);
1205+
1206+
let expected_enodes = vec![
1207+
"enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303",
1208+
"enode://22a8232c3abc76a16ae9d6c3b164f98775fe226f0917b0ca871128a74a8e9630b458460865bab457221f1d448dd9791d24c4e5d88786180ac185df813a68d4de@3.209.45.79:30303",
1209+
];
1210+
1211+
for enode in expected_enodes {
1212+
let node = TrustedPeer::from_str(enode).unwrap();
1213+
let bootnode = Discv5BootNode::Enode(node);
1214+
assert!(conf.peers.bootnodes_v5.contains(&bootnode));
1215+
}
1216+
}
1217+
1218+
#[test]
1219+
fn test_bootnodes_v5_config_enr() {
1220+
let reth_toml = r#"
1221+
[peers]
1222+
bootnodes_v5 = [
1223+
"enr:-J64QBwRIWAco7lv6jImSOjPU_W266lHXzpAS5YOh7WmgTyBZkgLgOwo_mxKJq3wz2XRbsoBItbv1dCyjIoNq67mFguGAYrTxM42gmlkgnY0gmlwhBLSsHKHb3BzdGFja4S0lAUAiXNlY3AyNTZrMaEDmoWSi8hcsRpQf2eJsNUx-sqv6fH4btmo2HsAzZFAKnKDdGNwgiQGg3VkcIIkBg"
1224+
]
1225+
"#;
1226+
1227+
let conf: Config = toml::from_str(reth_toml).unwrap();
1228+
assert_eq!(conf.peers.bootnodes_v5.len(), 1);
1229+
1230+
let expected_enr = "enr:-J64QBwRIWAco7lv6jImSOjPU_W266lHXzpAS5YOh7WmgTyBZkgLgOwo_mxKJq3wz2XRbsoBItbv1dCyjIoNq67mFguGAYrTxM42gmlkgnY0gmlwhBLSsHKHb3BzdGFja4S0lAUAiXNlY3AyNTZrMaEDmoWSi8hcsRpQf2eJsNUx-sqv6fH4btmo2HsAzZFAKnKDdGNwgiQGg3VkcIIkBg";
1231+
let bootnode = Discv5BootNode::Enr(expected_enr.to_string());
1232+
assert!(conf.peers.bootnodes_v5.contains(&bootnode));
1233+
}
1234+
1235+
#[test]
1236+
fn test_bootnodes_v5_config_mixed() {
1237+
let reth_toml = r#"
1238+
[peers]
1239+
bootnodes_v5 = [
1240+
"enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303",
1241+
"enr:-J64QBwRIWAco7lv6jImSOjPU_W266lHXzpAS5YOh7WmgTyBZkgLgOwo_mxKJq3wz2XRbsoBItbv1dCyjIoNq67mFguGAYrTxM42gmlkgnY0gmlwhBLSsHKHb3BzdGFja4S0lAUAiXNlY3AyNTZrMaEDmoWSi8hcsRpQf2eJsNUx-sqv6fH4btmo2HsAzZFAKnKDdGNwgiQGg3VkcIIkBg"
1242+
]
1243+
"#;
1244+
1245+
let conf: Config = toml::from_str(reth_toml).unwrap();
1246+
assert_eq!(conf.peers.bootnodes_v5.len(), 2);
1247+
1248+
let enode_node = TrustedPeer::from_str(
1249+
"enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303",
1250+
)
1251+
.unwrap();
1252+
let enode_bootnode = Discv5BootNode::Enode(enode_node);
1253+
assert!(conf.peers.bootnodes_v5.contains(&enode_bootnode));
1254+
1255+
let enr_string = "enr:-J64QBwRIWAco7lv6jImSOjPU_W266lHXzpAS5YOh7WmgTyBZkgLgOwo_mxKJq3wz2XRbsoBItbv1dCyjIoNq67mFguGAYrTxM42gmlkgnY0gmlwhBLSsHKHb3BzdGFja4S0lAUAiXNlY3AyNTZrMaEDmoWSi8hcsRpQf2eJsNUx-sqv6fH4btmo2HsAzZFAKnKDdGNwgiQGg3VkcIIkBg";
1256+
let enr_bootnode = Discv5BootNode::Enr(enr_string.to_string());
1257+
assert!(conf.peers.bootnodes_v5.contains(&enr_bootnode));
1258+
}
1259+
1260+
#[test]
1261+
fn test_bootnodes_v4_and_v5_separate_config() {
1262+
let reth_toml = r#"
1263+
[peers]
1264+
bootnodes_v4 = [
1265+
"enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303"
1266+
]
1267+
bootnodes_v5 = [
1268+
"enode://22a8232c3abc76a16ae9d6c3b164f98775fe226f0917b0ca871128a74a8e9630b458460865bab457221f1d448dd9791d24c4e5d88786180ac185df813a68d4de@3.209.45.79:30303"
1269+
]
1270+
"#;
1271+
1272+
let conf: Config = toml::from_str(reth_toml).unwrap();
1273+
assert_eq!(conf.peers.bootnodes_v4.len(), 1);
1274+
assert_eq!(conf.peers.bootnodes_v5.len(), 1);
1275+
1276+
let v4_node = TrustedPeer::from_str(
1277+
"enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303",
1278+
)
1279+
.unwrap();
1280+
let v5_node = TrustedPeer::from_str(
1281+
"enode://22a8232c3abc76a16ae9d6c3b164f98775fe226f0917b0ca871128a74a8e9630b458460865bab457221f1d448dd9791d24c4e5d88786180ac185df813a68d4de@3.209.45.79:30303",
1282+
)
1283+
.unwrap();
1284+
let v5_bootnode = Discv5BootNode::Enode(v5_node.clone());
1285+
1286+
assert!(conf.peers.bootnodes_v4.contains(&v4_node));
1287+
assert!(conf.peers.bootnodes_v5.contains(&v5_bootnode));
1288+
assert!(!conf.peers.bootnodes_v4.contains(&v5_node));
1289+
}
1290+
1291+
#[test]
1292+
fn test_bootnodes_default_empty() {
1293+
let conf: Config = Config::default();
1294+
assert!(conf.peers.bootnodes_v4.is_empty());
1295+
assert!(conf.peers.bootnodes_v5.is_empty());
1296+
}
11671297
}

crates/net/network-types/src/peers/config.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::{
44
collections::HashSet,
55
io::{self, ErrorKind},
66
path::Path,
7+
str::FromStr,
78
time::Duration,
89
};
910

@@ -13,6 +14,37 @@ use tracing::info;
1314

1415
use crate::{BackoffKind, ReputationChangeWeights};
1516

17+
/// A bootnode for Discv5 discovery that can be either an enode (TrustedPeer) or an ENR string.
18+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20+
#[cfg_attr(feature = "serde", serde(untagged))]
21+
pub enum Discv5BootNode {
22+
/// An enode URL (enode://...)
23+
Enode(TrustedPeer),
24+
/// An ENR string (enr:...)
25+
Enr(String),
26+
}
27+
28+
impl FromStr for Discv5BootNode {
29+
type Err = <TrustedPeer as FromStr>::Err;
30+
31+
fn from_str(s: &str) -> Result<Self, Self::Err> {
32+
// Check if it's an ENR (starts with "enr:")
33+
if s.starts_with("enr:") {
34+
Ok(Discv5BootNode::Enr(s.to_string()))
35+
} else {
36+
// Try to parse as enode
37+
TrustedPeer::from_str(s).map(Discv5BootNode::Enode)
38+
}
39+
}
40+
}
41+
42+
impl From<TrustedPeer> for Discv5BootNode {
43+
fn from(peer: TrustedPeer) -> Self {
44+
Discv5BootNode::Enode(peer)
45+
}
46+
}
47+
1648
/// Maximum number of available slots for outbound sessions.
1749
pub const DEFAULT_MAX_COUNT_PEERS_OUTBOUND: u32 = 100;
1850

@@ -172,6 +204,18 @@ pub struct PeersConfig {
172204
/// IPs within the specified CIDR ranges will be allowed.
173205
#[cfg_attr(feature = "serde", serde(skip))]
174206
pub ip_filter: IpFilter,
207+
/// Bootnodes for Discv4 discovery (enode:// format).
208+
///
209+
/// Similar to geth's BootstrapNodes. These nodes are used to bootstrap the Discv4
210+
/// discovery protocol.
211+
#[cfg_attr(feature = "serde", serde(default))]
212+
pub bootnodes_v4: Vec<TrustedPeer>,
213+
/// Bootnodes for Discv5 discovery (enode:// or enr: format).
214+
///
215+
/// Similar to geth's BootstrapNodesV5. These nodes are used to bootstrap the Discv5
216+
/// discovery protocol. Can be either enode URLs (enode://...) or ENR strings (enr:...).
217+
#[cfg_attr(feature = "serde", serde(default))]
218+
pub bootnodes_v5: Vec<Discv5BootNode>,
175219
}
176220

177221
impl Default for PeersConfig {
@@ -191,6 +235,8 @@ impl Default for PeersConfig {
191235
max_backoff_count: 5,
192236
incoming_ip_throttle_duration: INBOUND_IP_THROTTLE_DURATION,
193237
ip_filter: IpFilter::default(),
238+
bootnodes_v4: Default::default(),
239+
bootnodes_v5: Default::default(),
194240
}
195241
}
196242
}
@@ -314,6 +360,18 @@ impl PeersConfig {
314360
self
315361
}
316362

363+
/// Sets the bootnodes for Discv4 discovery.
364+
pub fn with_bootnodes_v4(mut self, bootnodes: Vec<TrustedPeer>) -> Self {
365+
self.bootnodes_v4 = bootnodes;
366+
self
367+
}
368+
369+
/// Sets the bootnodes for Discv5 discovery.
370+
pub fn with_bootnodes_v5(mut self, bootnodes: Vec<Discv5BootNode>) -> Self {
371+
self.bootnodes_v5 = bootnodes;
372+
self
373+
}
374+
317375
/// Returns settings for testing
318376
#[cfg(any(test, feature = "test-utils"))]
319377
pub fn test() -> Self {

crates/net/network-types/src/peers/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pub mod kind;
44
pub mod reputation;
55
pub mod state;
66

7-
pub use config::{ConnectionsConfig, PeersConfig};
7+
pub use config::{ConnectionsConfig, Discv5BootNode, PeersConfig};
88
pub use reputation::{Reputation, ReputationChange, ReputationChangeKind, ReputationChangeWeights};
99

1010
use alloy_eip2124::ForkId;

crates/net/network/src/peers.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ impl PeersManager {
111111
max_backoff_count,
112112
incoming_ip_throttle_duration,
113113
ip_filter,
114+
bootnodes_v4: _,
115+
bootnodes_v5: _,
114116
} = config;
115117
let (manager_tx, handle_rx) = mpsc::unbounded_channel();
116118
let now = Instant::now();

crates/node/core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ reth-discv5.workspace = true
3333
reth-net-nat.workspace = true
3434
reth-net-banlist.workspace = true
3535
reth-network-peers.workspace = true
36+
reth-network-types.workspace = true
3637
reth-prune-types.workspace = true
3738
reth-stages-types.workspace = true
3839
reth-ethereum-forks.workspace = true

0 commit comments

Comments
 (0)