Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
radicle: Explorer and preferred seed configuration
cloudhead committed 2 years ago
commit 59a08f21a44538d824cb866e5dfe50f29206c0d0
parent 3ad97fde0b88869d744569a38291521ddb5b1cf0
5 files changed +103 -27
modified radicle-httpd/src/api.rs
@@ -239,7 +239,7 @@ pub fn announce_refs(mut node: Node, rid: Id) -> Result<(), Error> {
    match node.announce_refs(rid) {
        Ok(_) => Ok(()),
        Err(e) if e.is_connection_err() => Ok(()),
-
        Err(e) => return Err(e.into()),
+
        Err(e) => Err(e.into()),
    }
}

modified radicle-httpd/src/test.rs
@@ -11,7 +11,6 @@ use serde_json::Value;
use time::OffsetDateTime;
use tower::ServiceExt;

-
use radicle::cli;
use radicle::cob::issue::Issues;
use radicle::cob::patch::{MergeTarget, Patches};
use radicle::crypto::ssh::keystore::MemorySigner;
@@ -69,10 +68,7 @@ pub fn profile(home: &Path, seed: [u8; 32]) -> radicle::Profile {
        storage,
        keystore,
        public_key: keypair.pk.into(),
-
        config: profile::Config {
-
            node: node::Config::new(alias),
-
            cli: cli::Config::default(),
-
        },
+
        config: profile::Config::new(alias),
    }
}

modified radicle-node/src/test/environment.rs
@@ -114,6 +114,7 @@ impl Environment {
        let config = profile::Config {
            node: node::Config::new(alias.clone()),
            cli: cli::Config { hints: false },
+
            public_explorer: profile::Explorer::default(),
        };
        config.write(&home.config()).unwrap();

modified radicle/src/node/config.rs
@@ -12,6 +12,34 @@ use crate::node::{Address, Alias, NodeId};
/// Target number of peers to maintain connections to.
pub const TARGET_OUTBOUND_PEERS: usize = 8;

+
/// Configured public seeds.
+
pub mod seeds {
+
    use std::str::FromStr;
+

+
    use super::{ConnectAddress, PeerAddr};
+
    use once_cell::sync::Lazy;
+

+
    /// The radicle public community seed node.
+
    pub static RADICLE_COMMUNITY_NODE: Lazy<ConnectAddress> = Lazy::new(|| {
+
        // SAFETY: `ConnectAddress` is known at compile time.
+
        #[allow(clippy::unwrap_used)]
+
        PeerAddr::from_str(
+
            "z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776",
+
        )
+
        .unwrap()
+
        .into()
+
    });
+

+
    /// The radicle team node.
+
    pub static RADICLE_TEAM_NODE: Lazy<ConnectAddress> = Lazy::new(|| {
+
        // SAFETY: `ConnectAddress` is known at compile time.
+
        #[allow(clippy::unwrap_used)]
+
        PeerAddr::from_str("z6MksmpU5b1dS7oaqF2bHXhQi1DWy2hB7Mh9CuN7y1DN6QSz@seed.radicle.xyz:8776")
+
            .unwrap()
+
            .into()
+
    });
+
}
+

/// Peer-to-peer network.
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -24,22 +52,13 @@ pub enum Network {
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",
-
                ),
+
                ("seed.radicle.garden", seeds::RADICLE_COMMUNITY_NODE.clone()),
+
                ("seed.radicle.xyz", seeds::RADICLE_TEAM_NODE.clone()),
            ]
            .into_iter()
-
            // SAFETY: These are valid addresses.
-
            .map(|(a, s)| (Alias::new(a), PeerAddr::from_str(s).unwrap().into()))
+
            .map(|(a, s)| (Alias::new(a), s))
            .collect(),

            Self::Test => vec![],
modified radicle/src/profile.rs
@@ -22,7 +22,7 @@ use crate::crypto::ssh::{keystore, Keystore, Passphrase};
use crate::crypto::{PublicKey, Signer};
use crate::node::{address, routing, tracking, Alias, AliasStore};
use crate::prelude::Did;
-
use crate::prelude::NodeId;
+
use crate::prelude::{Id, NodeId};
use crate::storage::git::transport;
use crate::storage::git::Storage;
use crate::{cli, git, node};
@@ -81,6 +81,55 @@ pub mod env {
}

#[derive(Debug, Error)]
+
pub enum ExplorerUrlError {
+
    #[error("invalid explorer URL {0:?}: unknown protocol")]
+
    UnknownProtocol(String),
+
    #[error("invalid explorer URL {0:?}: missing `$host` component")]
+
    MissingHost(String),
+
    #[error("invalid explorer URL {0:?}: missing `$rid` component")]
+
    MissingRid(String),
+
}
+

+
/// A public explorer, eg. `https://app.radicle.xyz`.
+
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
+
#[serde(transparent)]
+
pub struct Explorer(String);
+

+
impl Default for Explorer {
+
    fn default() -> Self {
+
        Self(String::from("https://app.radicle.xyz/nodes/$host/$rid"))
+
    }
+
}
+

+
impl Explorer {
+
    /// Get the explorer URL, filling in the host and RID.
+
    pub fn url(&self, host: &str, rid: &Id) -> String {
+
        self.0
+
            .replace("$host", host)
+
            .replace("$rid", rid.urn().as_str())
+
    }
+
}
+

+
impl FromStr for Explorer {
+
    type Err = ExplorerUrlError;
+

+
    fn from_str(s: &str) -> Result<Self, Self::Err> {
+
        let url = s.to_owned();
+

+
        if !url.starts_with("http://") && !url.starts_with("https://") {
+
            return Err(ExplorerUrlError::UnknownProtocol(url));
+
        }
+
        if !url.contains("$host") {
+
            return Err(ExplorerUrlError::MissingHost(url));
+
        }
+
        if !url.contains("$rid") {
+
            return Err(ExplorerUrlError::MissingRid(url));
+
        }
+
        Ok(Explorer(url))
+
    }
+
}
+

+
#[derive(Debug, Error)]
pub enum Error {
    #[error(transparent)]
    Io(#[from] io::Error),
@@ -119,15 +168,29 @@ pub struct Config {
    /// CLI configuration.
    #[serde(default)]
    pub cli: cli::Config,
+
    /// Public explorer. This is used for generating links.
+
    #[serde(default)]
+
    pub public_explorer: Explorer,
+
    /// Preferred seeds. These seeds will be used for explorer links
+
    /// and in other situations when a seed needs to be chosen.
+
    #[serde(default)]
+
    pub preferred_seeds: Vec<node::config::ConnectAddress>,
}

impl Config {
-
    /// Initialize a new configuration. Fails if the path already exists.
-
    pub fn init(alias: Alias, path: &Path) -> io::Result<Self> {
-
        let cfg = Self {
+
    /// Create a new, default configuration.
+
    pub fn new(alias: Alias) -> Self {
+
        Self {
            node: node::Config::new(alias),
            cli: cli::Config::default(),
-
        };
+
            public_explorer: Explorer::default(),
+
            preferred_seeds: vec![node::config::seeds::RADICLE_COMMUNITY_NODE.clone()],
+
        }
+
    }
+

+
    /// Initialize a new configuration. Fails if the path already exists.
+
    pub fn init(alias: Alias, path: &Path) -> io::Result<Self> {
+
        let cfg = Config::new(alias);
        cfg.write(path)?;

        Ok(cfg)
@@ -146,10 +209,7 @@ impl Config {
                let Ok(alias) = Alias::from_str(&user) else {
                    return Err(ConfigError::Io(path.to_owned(), e));
                };
-
                Ok(Config {
-
                    node: node::Config::new(alias),
-
                    cli: cli::Config::default(),
-
                })
+
                Ok(Config::new(alias))
            }
        }
    }