Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
node: Populate address book with bootstrap nodes
cloudhead committed 2 years ago
commit e88839851919c731b6d0d0eaf361bafc2b7e34ec
parent 5654f08841828e3b724032d57d3a09703f0463ef
8 files changed +154 -96
modified radicle-cli/tests/commands.rs
@@ -243,8 +243,8 @@ fn rad_node() {
    logger::init(log::Level::Debug);

    let mut environment = Environment::new();
-
    let alice = environment.node(Config::new(Alias::new("alice")));
-
    let bob = environment.node(Config::new(Alias::new("bob")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));
    let working = tempfile::tempdir().unwrap();

    let alice = alice.spawn();
@@ -411,7 +411,7 @@ fn rad_rm() {
#[test]
fn rad_track() {
    let mut environment = Environment::new();
-
    let alice = environment.node(Config::new(Alias::new("alice")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
    let working = tempfile::tempdir().unwrap();
    let alice = alice.spawn();

@@ -429,8 +429,8 @@ fn rad_clone() {
    logger::init(log::Level::Debug);

    let mut environment = Environment::new();
-
    let mut alice = environment.node(Config::new(Alias::new("alice")));
-
    let bob = environment.node(Config::new(Alias::new("bob")));
+
    let mut alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));
    let working = environment.tmp().join("working");

    // Setup a test project.
@@ -451,9 +451,9 @@ fn rad_clone_all() {
    logger::init(log::Level::Debug);

    let mut environment = Environment::new();
-
    let mut alice = environment.node(Config::new(Alias::new("alice")));
-
    let bob = environment.node(Config::new(Alias::new("bob")));
-
    let eve = environment.node(Config::new(Alias::new("eve")));
+
    let mut alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));
+
    let eve = environment.node(Config::test(Alias::new("eve")));
    let working = environment.tmp().join("working");

    // Setup a test project.
@@ -490,7 +490,7 @@ fn rad_clone_all() {
#[test]
fn rad_self() {
    let mut environment = Environment::new();
-
    let alice = environment.node(Config::new(Alias::new("alice")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
    let working = environment.tmp().join("working");

    test("examples/rad-self.md", working, Some(&alice.home), []).unwrap();
@@ -501,7 +501,7 @@ fn rad_clone_unknown() {
    logger::init(log::Level::Debug);

    let mut environment = Environment::new();
-
    let alice = environment.node(Config::new(Alias::new("alice")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
    let working = environment.tmp().join("working");

    let alice = alice.spawn();
@@ -520,8 +520,8 @@ fn rad_init_sync_and_clone() {
    logger::init(log::Level::Debug);

    let mut environment = Environment::new();
-
    let alice = environment.node(Config::new(Alias::new("alice")));
-
    let bob = environment.node(Config::new(Alias::new("bob")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));
    let working = environment.tmp().join("working");

    let alice = alice.spawn();
@@ -561,8 +561,8 @@ fn rad_init_sync_and_clone() {
fn rad_fetch() {
    let mut environment = Environment::new();
    let working = environment.tmp().join("working");
-
    let alice = environment.node(Config::new(Alias::new("alice")));
-
    let bob = environment.node(Config::new(Alias::new("bob")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));

    let mut alice = alice.spawn();
    let bob = bob.spawn();
@@ -595,8 +595,8 @@ fn rad_fetch() {
fn rad_fork() {
    let mut environment = Environment::new();
    let working = environment.tmp().join("working");
-
    let alice = environment.node(Config::new(Alias::new("alice")));
-
    let bob = environment.node(Config::new(Alias::new("bob")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));

    let mut alice = alice.spawn();
    let bob = bob.spawn();
@@ -638,7 +638,7 @@ fn test_clone_without_seeds() {
    logger::init(log::Level::Debug);

    let mut environment = Environment::new();
-
    let mut alice = environment.node(Config::new(Alias::new("alice")));
+
    let mut alice = environment.node(Config::test(Alias::new("alice")));
    let working = environment.tmp().join("working");
    let rid = alice.project("heartwood", "Radicle Heartwood Protocol & Stack");
    let mut alice = alice.spawn();
@@ -662,8 +662,8 @@ fn test_cob_replication() {

    let mut environment = Environment::new();
    let working = tempfile::tempdir().unwrap();
-
    let mut alice = environment.node(Config::new(Alias::new("alice")));
-
    let bob = environment.node(Config::new(Alias::new("bob")));
+
    let mut alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));

    let rid = alice.project("heartwood", "");

@@ -722,8 +722,8 @@ fn test_cob_replication() {
fn test_cob_deletion() {
    let mut environment = Environment::new();
    let working = tempfile::tempdir().unwrap();
-
    let mut alice = environment.node(Config::new(Alias::new("alice")));
-
    let bob = environment.node(Config::new(Alias::new("bob")));
+
    let mut alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));

    let rid = alice.project("heartwood", "");

@@ -776,9 +776,9 @@ fn rad_sync() {

    let mut environment = Environment::new();
    let working = environment.tmp().join("working");
-
    let alice = environment.node(Config::new(Alias::new("alice")));
-
    let bob = environment.node(Config::new(Alias::new("bob")));
-
    let eve = environment.node(Config::new(Alias::new("eve")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));
+
    let eve = environment.node(Config::test(Alias::new("eve")));
    let acme = Id::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();

    fixtures::repository(working.join("acme"));
@@ -820,12 +820,12 @@ fn rad_sync() {
//
fn test_replication_via_seed() {
    let mut environment = Environment::new();
-
    let alice = environment.node(Config::new(Alias::new("alice")));
-
    let bob = environment.node(Config::new(Alias::new("bob")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));
    let seed = environment.node(Config {
        policy: Policy::Track,
        scope: Scope::All,
-
        ..Config::new(Alias::new("seed"))
+
        ..Config::test(Alias::new("seed"))
    });
    let working = environment.tmp().join("working");
    let rid = Id::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
@@ -905,8 +905,8 @@ fn test_replication_via_seed() {
#[test]
fn rad_remote() {
    let mut environment = Environment::new();
-
    let alice = environment.node(Config::new(Alias::new("alice")));
-
    let bob = environment.node(Config::new(Alias::new("bob")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));
    let working = environment.tmp().join("working");
    let home = alice.home.clone();
    let rid = Id::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
@@ -949,7 +949,7 @@ fn rad_merge_via_push() {
    logger::init(log::Level::Debug);

    let mut environment = Environment::new();
-
    let alice = environment.node(Config::new(Alias::new("alice")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
    let working = environment.tmp().join("working");

    fixtures::repository(working.join("alice"));
@@ -976,7 +976,7 @@ fn rad_merge_via_push() {
#[test]
fn rad_merge_after_update() {
    let mut environment = Environment::new();
-
    let alice = environment.node(Config::new(Alias::new("alice")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
    let working = environment.tmp().join("working");

    fixtures::repository(working.join("alice"));
@@ -1005,8 +1005,8 @@ fn rad_patch_pull_update() {
    logger::init(log::Level::Debug);

    let mut environment = Environment::new();
-
    let alice = environment.node(Config::new(Alias::new("alice")));
-
    let bob = environment.node(Config::new(Alias::new("bob")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));
    let working = environment.tmp().join("working");

    fixtures::repository(working.join("alice"));
@@ -1037,8 +1037,8 @@ fn framework_home() {
    logger::init(log::Level::Debug);

    let mut environment = Environment::new();
-
    let alice = environment.node(Config::new(Alias::new("alice")));
-
    let bob = environment.node(Config::new(Alias::new("bob")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));

    formula(&environment.tmp(), "examples/framework/home.md")
        .unwrap()
@@ -1061,8 +1061,8 @@ fn git_push_and_pull() {
    logger::init(log::Level::Debug);

    let mut environment = Environment::new();
-
    let alice = environment.node(Config::new(Alias::new("alice")));
-
    let bob = environment.node(Config::new(Alias::new("bob")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));
    let working = environment.tmp().join("working");

    fixtures::repository(working.join("alice"));
@@ -1113,8 +1113,8 @@ fn git_push_and_pull() {
#[test]
fn rad_workflow() {
    let mut environment = Environment::new();
-
    let alice = environment.node(Config::new(Alias::new("alice")));
-
    let bob = environment.node(Config::new(Alias::new("bob")));
+
    let alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));
    let working = environment.tmp().join("working");

    fixtures::repository(working.join("alice"));
modified radicle-node/src/runtime.rs
@@ -15,7 +15,9 @@ use reactor::Reactor;
use thiserror::Error;

use radicle::git;
+
use radicle::node;
use radicle::node::address;
+
use radicle::node::address::Store as _;
use radicle::node::Handle as _;
use radicle::node::{ADDRESS_DB_FILE, NODE_ANNOUNCEMENT_FILE, ROUTING_DB_FILE, TRACKING_DB_FILE};
use radicle::profile::Home;
@@ -138,7 +140,7 @@ impl Runtime {
        let tracking_db = node_dir.join(TRACKING_DB_FILE);

        log::info!(target: "node", "Opening address book {}..", address_db.display());
-
        let addresses = address::Book::open(address_db)?;
+
        let mut addresses = address::Book::open(address_db)?;

        log::info!(target: "node", "Opening routing table {}..", routing_db.display());
        let routing = routing::Table::open(routing_db)?;
@@ -176,6 +178,24 @@ impl Runtime {
                .expect("Runtime::init: unable to solve proof-of-work puzzle")
        };

+
        if config.connect.is_empty() && addresses.is_empty()? {
+
            log::info!(target: "node", "Address book is empty. Adding bootstrap nodes..");
+

+
            for (alias, addr) in config.network.bootstrap() {
+
                let (id, addr) = addr.into();
+

+
                addresses.insert(
+
                    &id,
+
                    radicle::node::Features::SEED,
+
                    alias,
+
                    0,
+
                    clock.as_secs(),
+
                    [node::KnownAddress::new(addr, address::Source::Bootstrap)],
+
                )?;
+
            }
+
            log::info!(target: "node", "{} nodes added to address book", addresses.len()?);
+
        }
+

        let emitter: Emitter<Event> = Default::default();
        let service = service::Service::new(
            config,
modified radicle-node/src/test/peer.rs
@@ -111,7 +111,7 @@ impl Default for Config<MockSigner> {
        let signer = MockSigner::new(&mut rng);

        Config {
-
            config: service::Config::new(Alias::from_str("mocky").unwrap()),
+
            config: service::Config::test(Alias::from_str("mocky").unwrap()),
            addrs: address::Book::memory().unwrap(),
            local_time: LocalTime::now(),
            policy: Policy::default(),
modified radicle-node/src/tests/e2e.rs
@@ -24,8 +24,8 @@ fn test_inventory_sync_basic() {

    let tmp = tempfile::tempdir().unwrap();

-
    let mut alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let mut bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
+
    let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));

    alice.project("alice", "");
    bob.project("bob", "");
@@ -48,9 +48,9 @@ fn test_inventory_sync_bridge() {

    let tmp = tempfile::tempdir().unwrap();

-
    let mut alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let mut bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
-
    let mut eve = Node::init(tmp.path(), Config::new(Alias::new("eve")));
+
    let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
+
    let mut eve = Node::init(tmp.path(), Config::test(Alias::new("eve")));

    alice.project("alice", "");
    bob.project("bob", "");
@@ -78,10 +78,10 @@ fn test_inventory_sync_ring() {

    let tmp = tempfile::tempdir().unwrap();

-
    let mut alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let mut bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
-
    let mut eve = Node::init(tmp.path(), Config::new(Alias::new("eve")));
-
    let mut carol = Node::init(tmp.path(), Config::new(Alias::new("carol")));
+
    let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
+
    let mut eve = Node::init(tmp.path(), Config::test(Alias::new("eve")));
+
    let mut carol = Node::init(tmp.path(), Config::test(Alias::new("carol")));

    alice.project("alice", "");
    bob.project("bob", "");
@@ -115,11 +115,11 @@ fn test_inventory_sync_star() {

    let tmp = tempfile::tempdir().unwrap();

-
    let mut alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let mut bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
-
    let mut eve = Node::init(tmp.path(), Config::new(Alias::new("eve")));
-
    let mut carol = Node::init(tmp.path(), Config::new(Alias::new("carol")));
-
    let mut dave = Node::init(tmp.path(), Config::new(Alias::new("dave")));
+
    let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
+
    let mut eve = Node::init(tmp.path(), Config::test(Alias::new("eve")));
+
    let mut carol = Node::init(tmp.path(), Config::test(Alias::new("carol")));
+
    let mut dave = Node::init(tmp.path(), Config::test(Alias::new("dave")));

    alice.project("alice", "");
    bob.project("bob", "");
@@ -147,8 +147,8 @@ fn test_replication() {
    logger::init(log::Level::Debug);

    let tmp = tempfile::tempdir().unwrap();
-
    let alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let mut bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
+
    let alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
    let acme = bob.project("acme", "");

    let mut alice = alice.spawn();
@@ -204,8 +204,8 @@ fn test_replication_no_delegates() {
    logger::init(log::Level::Debug);

    let tmp = tempfile::tempdir().unwrap();
-
    let alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let mut bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
+
    let alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));

    let acme = bob.project("acme", "");
    // Delete one of the signed refs.
@@ -237,8 +237,8 @@ fn test_replication_no_delegates() {
#[test]
fn test_replication_invalid() {
    let tmp = tempfile::tempdir().unwrap();
-
    let alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let mut bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
+
    let alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
    let carol = MockSigner::default();
    let acme = bob.project("acme", "");
    let repo = bob.storage.repository_mut(acme).unwrap();
@@ -290,8 +290,8 @@ fn test_migrated_clone() {
    logger::init(log::Level::Debug);

    let tmp = tempfile::tempdir().unwrap();
-
    let mut alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
+
    let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
    let acme = alice.project("acme", "");

    let mut alice = alice.spawn();
@@ -340,8 +340,8 @@ fn test_dont_fetch_owned_refs() {
    logger::init(log::Level::Debug);

    let tmp = tempfile::tempdir().unwrap();
-
    let mut alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
+
    let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
    let acme = alice.project("acme", "");

    let mut alice = alice.spawn();
@@ -367,8 +367,8 @@ fn test_fetch_trusted_remotes() {
    logger::init(log::Level::Debug);

    let tmp = tempfile::tempdir().unwrap();
-
    let mut alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
+
    let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
    let acme = alice.project("acme", "");
    let mut signers = Vec::with_capacity(5);
    {
@@ -422,8 +422,8 @@ fn test_missing_remote() {
    logger::init(log::Level::Debug);

    let tmp = tempfile::tempdir().unwrap();
-
    let mut alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
+
    let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
    let acme = alice.project("acme", "");

    let mut alice = alice.spawn();
@@ -451,8 +451,8 @@ fn test_fetch_preserve_owned_refs() {
    logger::init(log::Level::Debug);

    let tmp = tempfile::tempdir().unwrap();
-
    let mut alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
+
    let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
    let acme = alice.project("acme", "");
    let mut alice = alice.spawn();
    let mut bob = bob.spawn();
@@ -497,8 +497,8 @@ fn test_clone() {
    logger::init(log::Level::Debug);

    let tmp = tempfile::tempdir().unwrap();
-
    let alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let mut bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
+
    let alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
    let acme = bob.project("acme", "");

    let mut alice = alice.spawn();
@@ -555,8 +555,8 @@ fn test_fetch_up_to_date() {
    logger::init(log::Level::Debug);

    let tmp = tempfile::tempdir().unwrap();
-
    let alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let mut bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
+
    let alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
    let acme = bob.project("acme", "");

    let mut alice = alice.spawn();
@@ -585,8 +585,8 @@ fn test_large_fetch() {

    let env = Environment::new();
    let scale = env.scale();
-
    let mut alice = Node::init(&env.tmp(), Config::new(Alias::new("alice")));
-
    let bob = Node::init(&env.tmp(), Config::new(Alias::new("bob")));
+
    let mut alice = Node::init(&env.tmp(), Config::test(Alias::new("alice")));
+
    let bob = Node::init(&env.tmp(), Config::test(Alias::new("bob")));

    let tmp = tempfile::tempdir().unwrap();
    let (repo, _) = fixtures::repository(tmp.path());
@@ -632,14 +632,14 @@ fn test_concurrent_fetches() {
        &env.tmp(),
        service::Config {
            limits: limits.clone(),
-
            ..service::Config::new(Alias::new("alice"))
+
            ..service::Config::test(Alias::new("alice"))
        },
    );
    let mut bob = Node::init(
        &env.tmp(),
        service::Config {
            limits,
-
            ..service::Config::new(Alias::new("bob"))
+
            ..service::Config::test(Alias::new("bob"))
        },
    );

@@ -728,8 +728,8 @@ fn test_connection_crossing() {
    logger::init(log::Level::Debug);

    let tmp = tempfile::tempdir().unwrap();
-
    let alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
+
    let alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));

    let mut alice = alice.spawn();
    let mut bob = bob.spawn();
@@ -768,9 +768,9 @@ fn test_non_fastforward_sigrefs() {

    let tmp = tempfile::tempdir().unwrap();

-
    let alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let mut bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
-
    let eve = Node::init(tmp.path(), Config::new(Alias::new("eve")));
+
    let alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let mut bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
+
    let eve = Node::init(tmp.path(), Config::test(Alias::new("eve")));

    let rid = bob.project("acme", "");

@@ -820,9 +820,9 @@ fn test_outdated_sigrefs() {

    let tmp = tempfile::tempdir().unwrap();

-
    let mut alice = Node::init(tmp.path(), Config::new(Alias::new("alice")));
-
    let bob = Node::init(tmp.path(), Config::new(Alias::new("bob")));
-
    let eve = Node::init(tmp.path(), Config::new(Alias::new("eve")));
+
    let mut alice = Node::init(tmp.path(), Config::test(Alias::new("alice")));
+
    let bob = Node::init(tmp.path(), Config::test(Alias::new("bob")));
+
    let eve = Node::init(tmp.path(), Config::test(Alias::new("eve")));

    let rid = alice.project("acme", "");

modified radicle/src/node/address.rs
@@ -4,9 +4,8 @@ mod types;
pub use store::*;
pub use types::*;

-
use std::net;
-

use cyphernet::addr::HostName;
+
use std::net;

use crate::node::Address;

modified radicle/src/node/address/store.rs
@@ -310,7 +310,7 @@ impl TryFrom<&sql::Value> for Source {
        };
        match value {
            sql::Value::String(s) => match s.as_str() {
-
                "dns" => Ok(Source::Dns),
+
                "bootstrap" => Ok(Source::Bootstrap),
                "peer" => Ok(Source::Peer),
                "imported" => Ok(Source::Imported),
                _ => Err(err),
@@ -323,7 +323,7 @@ impl TryFrom<&sql::Value> for Source {
impl sql::BindableWithIndex for Source {
    fn bind<I: sql::ParameterIndex>(self, stmt: &mut sql::Statement<'_>, i: I) -> sql::Result<()> {
        match self {
-
            Self::Dns => "dns".bind(stmt, i),
+
            Self::Bootstrap => "bootstrap".bind(stmt, i),
            Self::Peer => "peer".bind(stmt, i),
            Self::Imported => "imported".bind(stmt, i),
        }
@@ -342,7 +342,7 @@ impl TryFrom<&sql::Value> for AddressType {
            sql::Value::String(s) => match s.as_str() {
                "ipv4" => Ok(AddressType::Ipv4),
                "ipv6" => Ok(AddressType::Ipv6),
-
                "hostname" => Ok(AddressType::Dns),
+
                "dns" => Ok(AddressType::Dns),
                "onion" => Ok(AddressType::Onion),
                _ => Err(err),
            },
@@ -592,7 +592,7 @@ mod test {
            let addr = net::SocketAddr::from((net::Ipv4Addr::from(ip), rng.u16(..)));
            let ka = KnownAddress {
                addr: addr.into(),
-
                source: Source::Dns,
+
                source: Source::Bootstrap,
                // TODO: Test times as well.
                last_success: None,
                last_attempt: None,
modified radicle/src/node/address/types.rs
@@ -121,8 +121,8 @@ impl KnownAddress {
pub enum Source {
    /// An address that was shared by another peer.
    Peer,
-
    /// An address that came from a DNS seed.
-
    Dns,
+
    /// An bootstrap node address.
+
    Bootstrap,
    /// An address that came from some source external to the system, eg.
    /// specified by the user or added directly to the address manager.
    Imported,
@@ -132,7 +132,7 @@ impl std::fmt::Display for Source {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Peer => write!(f, "Peer"),
-
            Self::Dns => write!(f, "DNS"),
+
            Self::Bootstrap => write!(f, "Bootstrap"),
            Self::Imported => write!(f, "Imported"),
        }
    }
modified radicle/src/node/config.rs
@@ -17,6 +17,32 @@ pub enum Network {
    Test,
}

+
impl Network {
+
    /// Bootstrap nodes for this network.
+
    pub fn bootstrap(&self) -> Vec<(Alias, ConnectAddress)> {
+
        use std::str::FromStr;
+

+
        match self {
+
            Self::Main => [
+
                (
+
                    "seed.radicle.garden",
+
                    "z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776",
+
                ),
+
                (
+
                    "seed.radicle.xyz",
+
                    "z6MksmpU5b1dS7oaqF2bHXhQi1DWy2hB7Mh9CuN7y1DN6QSz@seed.radicle.xyz:8776",
+
                ),
+
            ]
+
            .into_iter()
+
            // SAFETY: These are valid addresses.
+
            .map(|(a, s)| (Alias::new(a), PeerAddr::from_str(s).unwrap().into()))
+
            .collect(),
+

+
            Self::Test => vec![],
+
        }
+
    }
+
}
+

/// Configuration parameters defining attributes of minima and maxima.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -63,6 +89,12 @@ impl From<(NodeId, Address)> for ConnectAddress {
    }
}

+
impl From<ConnectAddress> for Address {
+
    fn from(value: ConnectAddress) -> Self {
+
        value.0.addr
+
    }
+
}
+

impl Deref for ConnectAddress {
    type Target = PeerAddr<NodeId, Address>;

@@ -95,6 +127,13 @@ pub struct Config {
}

impl Config {
+
    pub fn test(alias: Alias) -> Self {
+
        Self {
+
            network: Network::Test,
+
            ..Self::new(alias)
+
        }
+
    }
+

    pub fn new(alias: Alias) -> Self {
        Self {
            alias,