reth_cli_commands/p2p/
bootnode.rs

1//! Standalone bootnode command
2
3use clap::Parser;
4use reth_discv4::{DiscoveryUpdate, Discv4, Discv4Config};
5use reth_discv5::{discv5::Event, Config, Discv5};
6use reth_net_nat::NatResolver;
7use reth_network_peers::NodeRecord;
8use std::{net::SocketAddr, str::FromStr};
9use tokio::select;
10use tokio_stream::StreamExt;
11use tracing::info;
12
13/// Satrt a discovery only bootnode.
14#[derive(Parser, Debug)]
15pub struct Command {
16    /// Listen address for the bootnode (default: ":30301").
17    #[arg(long, default_value = ":30301")]
18    pub addr: String,
19
20    /// Generate a new node key and save it to the specified file.
21    #[arg(long, default_value = "")]
22    pub gen_key: String,
23
24    /// Private key filename for the node.
25    #[arg(long, default_value = "")]
26    pub node_key: String,
27
28    /// NAT resolution method (any|none|upnp|publicip|extip:\<IP\>)
29    #[arg(long, default_value = "any")]
30    pub nat: NatResolver,
31
32    /// Run a v5 topic discovery bootnode.
33    #[arg(long)]
34    pub v5: bool,
35}
36
37impl Command {
38    /// Execute the bootnode command.
39    pub async fn execute(self) -> eyre::Result<()> {
40        info!("Bootnode started with config: {:?}", self);
41        let sk = reth_network::config::rng_secret_key();
42        let socket_addr = SocketAddr::from_str(&self.addr)?;
43        let local_enr = NodeRecord::from_secret_key(socket_addr, &sk);
44
45        let config = Discv4Config::builder().external_ip_resolver(Some(self.nat)).build();
46
47        let (_discv4, mut discv4_service) =
48            Discv4::bind(socket_addr, local_enr, sk, config).await?;
49
50        info!("Started discv4 at address:{:?}", socket_addr);
51
52        let mut discv4_updates = discv4_service.update_stream();
53        discv4_service.spawn();
54
55        // Optional discv5 update event listener if v5 is enabled
56        let mut discv5_updates = None;
57
58        if self.v5 {
59            info!("Starting discv5");
60            let config = Config::builder(socket_addr).build();
61            let (_discv5, updates, _local_enr_discv5) = Discv5::start(&sk, config).await?;
62            discv5_updates = Some(updates);
63        };
64
65        // event info loop for logging
66        loop {
67            select! {
68                //discv4 updates
69                update = discv4_updates.next() => {
70                    if let Some(update) = update {
71                        match update {
72                            DiscoveryUpdate::Added(record) => {
73                                info!("(Discv4) new peer added, peer_id={:?}", record.id);
74                            }
75                            DiscoveryUpdate::Removed(peer_id) => {
76                                info!("(Discv4) peer with peer-id={:?} removed", peer_id);
77                            }
78                            _ => {}
79                        }
80                    } else {
81                        info!("(Discv4) update stream ended.");
82                        break;
83                    }
84                }
85                //if discv5, discv5 update stream, else do nothing
86                update = async {
87                    if let Some(updates) = &mut discv5_updates {
88                        updates.recv().await
89                    } else {
90                        futures::future::pending().await
91                    }
92                } => {
93                    if let Some(update) = update {
94                     if let Event::SessionEstablished(enr, _) = update {
95                            info!("(Discv5) new peer added, peer_id={:?}", enr.id());
96                        }
97                    } else {
98                        info!("(Discv5) update stream ended.");
99                        break;
100                    }
101                }
102            }
103        }
104
105        Ok(())
106    }
107}