use std::collections::HashSet;
use std::net;
use std::ops::Deref;
use cyphernet::addr::PeerAddr;
use localtime::LocalDuration;
use serde_json as json;
use crate::node;
use crate::node::policy::{Scope, SeedingPolicy};
use crate::node::{Address, Alias, NodeId};
pub type ProtocolVersion = u8;
pub const DEFAULT_WORKERS: usize = 8;
pub mod seeds {
use std::str::FromStr;
use super::{ConnectAddress, PeerAddr};
use once_cell::sync::Lazy;
pub static RADICLE_COMMUNITY_NODE: Lazy<ConnectAddress> = Lazy::new(|| {
#[allow(clippy::unwrap_used)]
PeerAddr::from_str(
"z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776",
)
.unwrap()
.into()
});
pub static RADICLE_ASH_NODE: Lazy<ConnectAddress> = Lazy::new(|| {
#[allow(clippy::unwrap_used)]
PeerAddr::from_str(
"z6Mkmqogy2qEM2ummccUthFEaaHvyYmYBYh3dbe9W4ebScxo@ash.radicle.garden:8776",
)
.unwrap()
.into()
});
pub static RADICLE_TEAM_NODE: Lazy<ConnectAddress> = Lazy::new(|| {
#[allow(clippy::unwrap_used)]
PeerAddr::from_str("z6MksmpU5b1dS7oaqF2bHXhQi1DWy2hB7Mh9CuN7y1DN6QSz@seed.radicle.xyz:8776")
.unwrap()
.into()
});
}
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Network {
#[default]
Main,
Test,
}
impl Network {
pub fn bootstrap(&self) -> Vec<(Alias, ProtocolVersion, ConnectAddress)> {
match self {
Self::Main => [
("seed.radicle.garden", seeds::RADICLE_COMMUNITY_NODE.clone()),
("seed.radicle.xyz", seeds::RADICLE_TEAM_NODE.clone()),
]
.into_iter()
.map(|(a, s)| (Alias::new(a), 1, s))
.collect(),
Self::Test => vec![],
}
}
pub fn public_seeds(&self) -> Vec<ConnectAddress> {
match self {
Self::Main => vec![
seeds::RADICLE_COMMUNITY_NODE.clone(),
seeds::RADICLE_ASH_NODE.clone(),
],
Self::Test => vec![],
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Limits {
pub routing_max_size: usize,
#[serde(with = "crate::serde_ext::localtime::duration")]
pub routing_max_age: LocalDuration,
#[serde(with = "crate::serde_ext::localtime::duration")]
pub gossip_max_age: LocalDuration,
pub fetch_concurrency: usize,
pub max_open_files: usize,
#[serde(default)]
pub rate: RateLimits,
#[serde(default)]
pub connection: ConnectionLimits,
}
impl Default for Limits {
fn default() -> Self {
Self {
routing_max_size: 1000,
routing_max_age: LocalDuration::from_mins(7 * 24 * 60), gossip_max_age: LocalDuration::from_mins(2 * 7 * 24 * 60), fetch_concurrency: 1,
max_open_files: 4096,
rate: RateLimits::default(),
connection: ConnectionLimits::default(),
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ConnectionLimits {
pub inbound: usize,
pub outbound: usize,
}
impl Default for ConnectionLimits {
fn default() -> Self {
Self {
inbound: 128,
outbound: 16,
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RateLimit {
pub fill_rate: f64,
pub capacity: usize,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RateLimits {
pub inbound: RateLimit,
pub outbound: RateLimit,
}
impl Default for RateLimits {
fn default() -> Self {
Self {
inbound: RateLimit {
fill_rate: 5.0,
capacity: 1024,
},
outbound: RateLimit {
fill_rate: 10.0,
capacity: 2048,
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash)]
#[serde(transparent)]
pub struct ConnectAddress(#[serde(with = "crate::serde_ext::string")] PeerAddr<NodeId, Address>);
impl From<PeerAddr<NodeId, Address>> for ConnectAddress {
fn from(value: PeerAddr<NodeId, Address>) -> Self {
Self(value)
}
}
impl From<ConnectAddress> for (NodeId, Address) {
fn from(value: ConnectAddress) -> Self {
(value.0.id, value.0.addr)
}
}
impl From<(NodeId, Address)> for ConnectAddress {
fn from((id, addr): (NodeId, Address)) -> Self {
Self(PeerAddr { id, addr })
}
}
impl From<ConnectAddress> for Address {
fn from(value: ConnectAddress) -> Self {
value.0.addr
}
}
impl Deref for ConnectAddress {
type Target = PeerAddr<NodeId, Address>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase", tag = "type")]
pub enum PeerConfig {
Static,
Dynamic,
}
impl Default for PeerConfig {
fn default() -> Self {
Self::Dynamic
}
}
#[derive(Debug, Copy, Clone, Default, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Relay {
Always,
Never,
#[default]
Auto,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase", tag = "mode")]
pub enum AddressConfig {
Proxy {
address: net::SocketAddr,
},
Forward,
}
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase", tag = "default")]
pub enum DefaultSeedingPolicy {
Allow {
#[serde(default)]
scope: Scope,
},
#[default]
Block,
}
impl DefaultSeedingPolicy {
pub fn is_allow(&self) -> bool {
matches!(self, Self::Allow { .. })
}
pub fn permissive() -> Self {
Self::Allow { scope: Scope::All }
}
}
impl From<DefaultSeedingPolicy> for SeedingPolicy {
fn from(policy: DefaultSeedingPolicy) -> Self {
match policy {
DefaultSeedingPolicy::Block => Self::Block,
DefaultSeedingPolicy::Allow { scope } => Self::Allow { scope },
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Config {
pub alias: Alias,
#[serde(default)]
pub listen: Vec<net::SocketAddr>,
#[serde(default)]
pub peers: PeerConfig,
#[serde(default)]
pub connect: HashSet<ConnectAddress>,
#[serde(default)]
pub external_addresses: Vec<Address>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub proxy: Option<net::SocketAddr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub onion: Option<AddressConfig>,
#[serde(default)]
pub network: Network,
#[serde(default = "defaults::log")]
#[serde(with = "crate::serde_ext::string")]
pub log: log::Level,
#[serde(default, deserialize_with = "crate::serde_ext::ok_or_default")]
pub relay: Relay,
#[serde(default)]
pub limits: Limits,
#[serde(default = "defaults::workers")]
pub workers: usize,
#[serde(default)]
pub seeding_policy: DefaultSeedingPolicy,
#[serde(flatten, skip_serializing)]
pub extra: json::Map<String, json::Value>,
}
impl Config {
pub fn test(alias: Alias) -> Self {
Self {
network: Network::Test,
..Self::new(alias)
}
}
pub fn new(alias: Alias) -> Self {
Self {
alias,
peers: PeerConfig::default(),
listen: vec![],
connect: HashSet::default(),
external_addresses: vec![],
network: Network::default(),
proxy: None,
onion: None,
relay: Relay::default(),
limits: Limits::default(),
workers: DEFAULT_WORKERS,
log: defaults::log(),
seeding_policy: DefaultSeedingPolicy::default(),
extra: json::Map::default(),
}
}
pub fn peer(&self, id: &NodeId) -> Option<&Address> {
self.connect
.iter()
.find(|ca| &ca.id == id)
.map(|ca| &ca.addr)
}
pub fn peers(&self) -> impl Iterator<Item = NodeId> + '_ {
self.connect.iter().cloned().map(|p| p.id)
}
pub fn is_persistent(&self, id: &NodeId) -> bool {
self.peer(id).is_some()
}
pub fn is_relay(&self) -> bool {
match self.relay {
Relay::Auto => !self.external_addresses.is_empty(),
Relay::Never => false,
Relay::Always => true,
}
}
pub fn features(&self) -> node::Features {
node::Features::SEED
}
}
mod defaults {
pub fn workers() -> usize {
super::DEFAULT_WORKERS
}
pub fn log() -> log::Level {
log::Level::Info
}
}