feat(cli): add bootnode command (#15347)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Udoagwa Franklin
2025-04-18 12:44:42 +01:00
committed by GitHub
parent eb8102c7c2
commit 792eeae8c0
4 changed files with 115 additions and 1 deletions

3
Cargo.lock generated
View File

@@ -7127,6 +7127,7 @@ dependencies = [
"reth-db-api",
"reth-db-common",
"reth-discv4",
"reth-discv5",
"reth-downloaders",
"reth-ecies",
"reth-eth-wire",
@@ -7135,6 +7136,7 @@ dependencies = [
"reth-evm",
"reth-exex",
"reth-fs-util",
"reth-net-nat",
"reth-network",
"reth-network-p2p",
"reth-network-peers",
@@ -7158,6 +7160,7 @@ dependencies = [
"serde",
"serde_json",
"tokio",
"tokio-stream",
"toml",
"tracing",
]

View File

@@ -27,6 +27,7 @@ reth-eth-wire.workspace = true
reth-evm.workspace = true
reth-exex.workspace = true
reth-fs-util.workspace = true
reth-net-nat.workspace = true
reth-network = { workspace = true, features = ["serde"] }
reth-network-p2p.workspace = true
reth-network-peers = { workspace = true, features = ["secp256k1"] }
@@ -47,6 +48,8 @@ reth-trie = { workspace = true, features = ["metrics"] }
reth-trie-db = { workspace = true, features = ["metrics"] }
reth-trie-common = { workspace = true, optional = true }
reth-primitives-traits.workspace = true
reth-discv4.workspace = true
reth-discv5.workspace = true
# ethereum
alloy-eips.workspace = true
@@ -68,6 +71,7 @@ serde_json.workspace = true
tracing.workspace = true
backon.workspace = true
secp256k1 = { workspace = true, features = ["global-context", "std", "recovery"] }
tokio-stream.workspace = true
# io
fdlimit.workspace = true
@@ -84,7 +88,6 @@ arbitrary = { workspace = true, optional = true }
proptest-arbitrary-interop = { workspace = true, optional = true }
[dev-dependencies]
reth-discv4.workspace = true
reth-ethereum-cli.workspace = true
[features]

View File

@@ -0,0 +1,107 @@
//! Standalone bootnode command
use clap::Parser;
use reth_discv4::{DiscoveryUpdate, Discv4, Discv4Config};
use reth_discv5::{discv5::Event, Config, Discv5};
use reth_net_nat::NatResolver;
use reth_network_peers::NodeRecord;
use std::{net::SocketAddr, str::FromStr};
use tokio::select;
use tokio_stream::StreamExt;
use tracing::info;
/// Satrt a discovery only bootnode.
#[derive(Parser, Debug)]
pub struct Command {
/// Listen address for the bootnode (default: ":30301").
#[arg(long, default_value = ":30301")]
pub addr: String,
/// Generate a new node key and save it to the specified file.
#[arg(long, default_value = "")]
pub gen_key: String,
/// Private key filename for the node.
#[arg(long, default_value = "")]
pub node_key: String,
/// NAT resolution method (any|none|upnp|publicip|extip:\<IP\>)
#[arg(long, default_value = "any")]
pub nat: NatResolver,
/// Run a v5 topic discovery bootnode.
#[arg(long)]
pub v5: bool,
}
impl Command {
/// Execute the bootnode command.
pub async fn execute(self) -> eyre::Result<()> {
info!("Bootnode started with config: {:?}", self);
let sk = reth_network::config::rng_secret_key();
let socket_addr = SocketAddr::from_str(&self.addr)?;
let local_enr = NodeRecord::from_secret_key(socket_addr, &sk);
let config = Discv4Config::builder().external_ip_resolver(Some(self.nat)).build();
let (_discv4, mut discv4_service) =
Discv4::bind(socket_addr, local_enr, sk, config).await?;
info!("Started discv4 at address:{:?}", socket_addr);
let mut discv4_updates = discv4_service.update_stream();
discv4_service.spawn();
// Optional discv5 update event listener if v5 is enabled
let mut discv5_updates = None;
if self.v5 {
info!("Starting discv5");
let config = Config::builder(socket_addr).build();
let (_discv5, updates, _local_enr_discv5) = Discv5::start(&sk, config).await?;
discv5_updates = Some(updates);
};
// event info loop for logging
loop {
select! {
//discv4 updates
update = discv4_updates.next() => {
if let Some(update) = update {
match update {
DiscoveryUpdate::Added(record) => {
info!("(Discv4) new peer added, peer_id={:?}", record.id);
}
DiscoveryUpdate::Removed(peer_id) => {
info!("(Discv4) peer with peer-id={:?} removed", peer_id);
}
_ => {}
}
} else {
info!("(Discv4) update stream ended.");
break;
}
}
//if discv5, discv5 update stream, else do nothing
update = async {
if let Some(updates) = &mut discv5_updates {
updates.recv().await
} else {
futures::future::pending().await
}
} => {
if let Some(update) = update {
if let Event::SessionEstablished(enr, _) = update {
info!("(Discv5) new peer added, peer_id={:?}", enr.id());
}
} else {
info!("(Discv5) update stream ended.");
break;
}
}
}
}
Ok(())
}
}

View File

@@ -16,6 +16,7 @@ use reth_node_core::{
utils::get_single_header,
};
pub mod bootnode;
mod rlpx;
/// `reth p2p` command