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 alias = Alias::new("alice");
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)
});
let working = tempfile::tempdir().unwrap();

View File

@ -185,19 +185,21 @@ impl Runtime {
if config.connect.is_empty() && stores.addresses().is_empty()? {
log::info!(target: "node", "Address book is empty. Adding bootstrap nodes..");
for (alias, version, addr) in config.network.bootstrap() {
let (id, addr) = addr.into();
for (alias, version, addrs) in config.network.bootstrap() {
for addr in addrs {
let (id, addr) = addr.into();
stores.addresses_mut().insert(
&id,
version,
radicle::node::Features::SEED,
&alias,
0,
&UserAgent::default(),
clock.into(),
[node::KnownAddress::new(addr, address::Source::Bootstrap)],
)?;
stores.addresses_mut().insert(
&id,
version,
radicle::node::Features::SEED,
&alias,
0,
&UserAgent::default(),
clock.into(),
[node::KnownAddress::new(addr, address::Source::Bootstrap)],
)?;
}
}
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.
pub mod seeds {
use std::str::FromStr;
use std::{
net::{Ipv4Addr, Ipv6Addr},
str::FromStr,
sync::LazyLock,
};
use super::{ConnectAddress, PeerAddr};
use std::sync::LazyLock;
use cyphernet::addr::{tor::OnionAddrV3, HostName, NetAddr};
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.
pub static RADICLE_NODE_BOOTSTRAP_IRIS: LazyLock<ConnectAddress> = LazyLock::new(|| {
// SAFETY: `ConnectAddress` is known at compile time.
#[allow(clippy::unwrap_used)]
PeerAddr::from_str("z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@iris.radicle.xyz:8776")
.unwrap()
.into()
pub static RADICLE_NODE_BOOTSTRAP_IRIS: LazyLock<Vec<ConnectAddress>> = LazyLock::new(|| {
to_connect_addresses(
#[allow(clippy::unwrap_used)] // Value is manually verified.
NodeId::from_str("z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7").unwrap(),
vec![
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.
pub static RADICLE_NODE_BOOTSTRAP_ROSA: LazyLock<ConnectAddress> = LazyLock::new(|| {
// SAFETY: `ConnectAddress` is known at compile time.
#[allow(clippy::unwrap_used)]
PeerAddr::from_str("z6Mkmqogy2qEM2ummccUthFEaaHvyYmYBYh3dbe9W4ebScxo@rosa.radicle.xyz:8776")
.unwrap()
.into()
pub static RADICLE_NODE_BOOTSTRAP_ROSA: LazyLock<Vec<ConnectAddress>> = LazyLock::new(|| {
to_connect_addresses(
#[allow(clippy::unwrap_used)] // Value is manually verified.
NodeId::from_str("z6Mkmqogy2qEM2ummccUthFEaaHvyYmYBYh3dbe9W4ebScxo").unwrap(),
vec![
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 {
/// Bootstrap nodes for this network.
pub fn bootstrap(&self) -> Vec<(Alias, ProtocolVersion, ConnectAddress)> {
pub fn bootstrap(&self) -> Vec<(Alias, ProtocolVersion, Vec<ConnectAddress>)> {
match self {
Self::Main => [
(
@ -79,10 +112,11 @@ impl Network {
/// Public seeds for this network.
pub fn public_seeds(&self) -> Vec<ConnectAddress> {
match self {
Self::Main => vec![
seeds::RADICLE_NODE_BOOTSTRAP_IRIS.clone(),
seeds::RADICLE_NODE_BOOTSTRAP_ROSA.clone(),
],
Self::Main => {
let mut result = seeds::RADICLE_NODE_BOOTSTRAP_IRIS.clone();
result.extend(seeds::RADICLE_NODE_BOOTSTRAP_ROSA.clone());
result
}
Self::Test => vec![],
}
}