bootstrap: Add IPv6, IPv4, Onion addresses

This should allow bootstrapping when DNS is not available or a
connection via Tor is desired.
This commit is contained in:
Lorenz Leutgeb 2025-08-18 17:18:21 +02:00
parent 6a1b13bb66
commit 0200e84aea
3 changed files with 69 additions and 33 deletions

View File

@ -250,7 +250,7 @@ fn rad_config() {
let mut environment = Environment::new(); let mut environment = Environment::new();
let alias = Alias::new("alice"); let alias = Alias::new("alice");
let profile = environment.profile_with(profile::Config { let profile = environment.profile_with(profile::Config {
preferred_seeds: vec![RADICLE_NODE_BOOTSTRAP_IRIS.clone()], preferred_seeds: vec![RADICLE_NODE_BOOTSTRAP_IRIS.clone()[0].clone()],
..profile::Config::new(alias) ..profile::Config::new(alias)
}); });
let working = tempfile::tempdir().unwrap(); let working = tempfile::tempdir().unwrap();

View File

@ -185,19 +185,21 @@ impl Runtime {
if config.connect.is_empty() && stores.addresses().is_empty()? { if config.connect.is_empty() && stores.addresses().is_empty()? {
log::info!(target: "node", "Address book is empty. Adding bootstrap nodes.."); log::info!(target: "node", "Address book is empty. Adding bootstrap nodes..");
for (alias, version, addr) in config.network.bootstrap() { for (alias, version, addrs) in config.network.bootstrap() {
let (id, addr) = addr.into(); for addr in addrs {
let (id, addr) = addr.into();
stores.addresses_mut().insert( stores.addresses_mut().insert(
&id, &id,
version, version,
radicle::node::Features::SEED, radicle::node::Features::SEED,
&alias, &alias,
0, 0,
&UserAgent::default(), &UserAgent::default(),
clock.into(), clock.into(),
[node::KnownAddress::new(addr, address::Source::Bootstrap)], [node::KnownAddress::new(addr, address::Source::Bootstrap)],
)?; )?;
}
} }
log::info!(target: "node", "{} nodes added to address book", stores.addresses().len()?); log::info!(target: "node", "{} nodes added to address book", stores.addresses().len()?);
} }

View File

@ -20,27 +20,60 @@ pub const DEFAULT_WORKERS: usize = 8;
/// Configured public seeds. /// Configured public seeds.
pub mod seeds { pub mod seeds {
use std::str::FromStr; use std::{
net::{Ipv4Addr, Ipv6Addr},
str::FromStr,
sync::LazyLock,
};
use super::{ConnectAddress, PeerAddr}; use cyphernet::addr::{tor::OnionAddrV3, HostName, NetAddr};
use std::sync::LazyLock;
use super::{ConnectAddress, NodeId, PeerAddr};
/// A helper to generate many connect addresses for a node, using port 8776.
fn to_connect_addresses(id: NodeId, hostnames: Vec<HostName>) -> Vec<ConnectAddress> {
hostnames
.into_iter()
.map(|hostname| PeerAddr::new(id, NetAddr::new(hostname, 8776).into()).into())
.collect()
}
/// A public Radicle seed node for the community. /// A public Radicle seed node for the community.
pub static RADICLE_NODE_BOOTSTRAP_IRIS: LazyLock<ConnectAddress> = LazyLock::new(|| { pub static RADICLE_NODE_BOOTSTRAP_IRIS: LazyLock<Vec<ConnectAddress>> = LazyLock::new(|| {
// SAFETY: `ConnectAddress` is known at compile time. to_connect_addresses(
#[allow(clippy::unwrap_used)] #[allow(clippy::unwrap_used)] // Value is manually verified.
PeerAddr::from_str("z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@iris.radicle.xyz:8776") NodeId::from_str("z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7").unwrap(),
.unwrap() vec![
.into() HostName::Dns("iris.radicle.xyz".to_owned()),
Ipv6Addr::new(0x2a01, 0x4f9, 0xc010, 0xdfaa, 0, 0, 0, 1).into(),
Ipv4Addr::new(95, 217, 156, 6).into(),
#[allow(clippy::unwrap_used)] // Value is manually verified.
OnionAddrV3::from_str(
"irisradizskwweumpydlj4oammoshkxxjur3ztcmo7cou5emc6s5lfid.onion",
)
.unwrap()
.into(),
],
)
}); });
/// A public Radicle seed node for the community. /// A public Radicle seed node for the community.
pub static RADICLE_NODE_BOOTSTRAP_ROSA: LazyLock<ConnectAddress> = LazyLock::new(|| { pub static RADICLE_NODE_BOOTSTRAP_ROSA: LazyLock<Vec<ConnectAddress>> = LazyLock::new(|| {
// SAFETY: `ConnectAddress` is known at compile time. to_connect_addresses(
#[allow(clippy::unwrap_used)] #[allow(clippy::unwrap_used)] // Value is manually verified.
PeerAddr::from_str("z6Mkmqogy2qEM2ummccUthFEaaHvyYmYBYh3dbe9W4ebScxo@rosa.radicle.xyz:8776") NodeId::from_str("z6Mkmqogy2qEM2ummccUthFEaaHvyYmYBYh3dbe9W4ebScxo").unwrap(),
.unwrap() vec![
.into() HostName::Dns("rosa.radicle.xyz".to_owned()),
Ipv6Addr::new(0x2a01, 0x4ff, 0xf0, 0xabd3, 0, 0, 0, 1).into(),
Ipv4Addr::new(5, 161, 85, 124).into(),
#[allow(clippy::unwrap_used)] // Value is manually verified.
OnionAddrV3::from_str(
"rosarad5bxgdlgjnzzjygnsxrwxmoaj4vn7xinlstwglxvyt64jlnhyd.onion",
)
.unwrap()
.into(),
],
)
}); });
} }
@ -56,7 +89,7 @@ pub enum Network {
impl Network { impl Network {
/// Bootstrap nodes for this network. /// Bootstrap nodes for this network.
pub fn bootstrap(&self) -> Vec<(Alias, ProtocolVersion, ConnectAddress)> { pub fn bootstrap(&self) -> Vec<(Alias, ProtocolVersion, Vec<ConnectAddress>)> {
match self { match self {
Self::Main => [ Self::Main => [
( (
@ -79,10 +112,11 @@ impl Network {
/// Public seeds for this network. /// Public seeds for this network.
pub fn public_seeds(&self) -> Vec<ConnectAddress> { pub fn public_seeds(&self) -> Vec<ConnectAddress> {
match self { match self {
Self::Main => vec![ Self::Main => {
seeds::RADICLE_NODE_BOOTSTRAP_IRIS.clone(), let mut result = seeds::RADICLE_NODE_BOOTSTRAP_IRIS.clone();
seeds::RADICLE_NODE_BOOTSTRAP_ROSA.clone(), result.extend(seeds::RADICLE_NODE_BOOTSTRAP_ROSA.clone());
], result
}
Self::Test => vec![], Self::Test => vec![],
} }
} }