use serde::{Deserialize, Serialize};
use super::IdentityConfig;
use crate::mmp::{DEFAULT_LOG_INTERVAL_SECS, DEFAULT_OWD_WINDOW_SIZE, MmpConfig, MmpMode};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LimitsConfig {
#[serde(default = "LimitsConfig::default_max_connections")]
pub max_connections: usize,
#[serde(default = "LimitsConfig::default_max_peers")]
pub max_peers: usize,
#[serde(default = "LimitsConfig::default_max_links")]
pub max_links: usize,
#[serde(default = "LimitsConfig::default_max_pending_inbound")]
pub max_pending_inbound: usize,
}
impl Default for LimitsConfig {
fn default() -> Self {
Self {
max_connections: 256,
max_peers: 128,
max_links: 256,
max_pending_inbound: 1000,
}
}
}
impl LimitsConfig {
fn default_max_connections() -> usize {
256
}
fn default_max_peers() -> usize {
128
}
fn default_max_links() -> usize {
256
}
fn default_max_pending_inbound() -> usize {
1000
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RateLimitConfig {
#[serde(default = "RateLimitConfig::default_handshake_burst")]
pub handshake_burst: u32,
#[serde(default = "RateLimitConfig::default_handshake_rate")]
pub handshake_rate: f64,
#[serde(default = "RateLimitConfig::default_handshake_timeout_secs")]
pub handshake_timeout_secs: u64,
#[serde(default = "RateLimitConfig::default_handshake_resend_interval_ms")]
pub handshake_resend_interval_ms: u64,
#[serde(default = "RateLimitConfig::default_handshake_resend_backoff")]
pub handshake_resend_backoff: f64,
#[serde(default = "RateLimitConfig::default_handshake_max_resends")]
pub handshake_max_resends: u32,
}
impl Default for RateLimitConfig {
fn default() -> Self {
Self {
handshake_burst: 100,
handshake_rate: 10.0,
handshake_timeout_secs: 30,
handshake_resend_interval_ms: 1000,
handshake_resend_backoff: 2.0,
handshake_max_resends: 5,
}
}
}
impl RateLimitConfig {
fn default_handshake_burst() -> u32 {
100
}
fn default_handshake_rate() -> f64 {
10.0
}
fn default_handshake_timeout_secs() -> u64 {
30
}
fn default_handshake_resend_interval_ms() -> u64 {
1000
}
fn default_handshake_resend_backoff() -> f64 {
2.0
}
fn default_handshake_max_resends() -> u32 {
5
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetryConfig {
#[serde(default = "RetryConfig::default_max_retries")]
pub max_retries: u32,
#[serde(default = "RetryConfig::default_base_interval_secs")]
pub base_interval_secs: u64,
#[serde(default = "RetryConfig::default_max_backoff_secs")]
pub max_backoff_secs: u64,
}
impl Default for RetryConfig {
fn default() -> Self {
Self {
max_retries: 5,
base_interval_secs: 5,
max_backoff_secs: 300,
}
}
}
impl RetryConfig {
fn default_max_retries() -> u32 {
5
}
fn default_base_interval_secs() -> u64 {
5
}
fn default_max_backoff_secs() -> u64 {
300
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheConfig {
#[serde(default = "CacheConfig::default_coord_size")]
pub coord_size: usize,
#[serde(default = "CacheConfig::default_coord_ttl_secs")]
pub coord_ttl_secs: u64,
#[serde(default = "CacheConfig::default_identity_size")]
pub identity_size: usize,
}
impl Default for CacheConfig {
fn default() -> Self {
Self {
coord_size: 50_000,
coord_ttl_secs: 300,
identity_size: 10_000,
}
}
}
impl CacheConfig {
fn default_coord_size() -> usize {
50_000
}
fn default_coord_ttl_secs() -> u64 {
300
}
fn default_identity_size() -> usize {
10_000
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiscoveryConfig {
#[serde(default = "DiscoveryConfig::default_ttl")]
pub ttl: u8,
#[serde(default = "DiscoveryConfig::default_attempt_timeouts_secs")]
pub attempt_timeouts_secs: Vec<u64>,
#[serde(default = "DiscoveryConfig::default_recent_expiry_secs")]
pub recent_expiry_secs: u64,
#[serde(default = "DiscoveryConfig::default_backoff_base_secs")]
pub backoff_base_secs: u64,
#[serde(default = "DiscoveryConfig::default_backoff_max_secs")]
pub backoff_max_secs: u64,
#[serde(default = "DiscoveryConfig::default_forward_min_interval_secs")]
pub forward_min_interval_secs: u64,
#[serde(default = "DiscoveryConfig::default_nostr")]
pub nostr: NostrDiscoveryConfig,
#[serde(default = "DiscoveryConfig::default_lan")]
pub lan: crate::discovery::lan::LanDiscoveryConfig,
}
impl Default for DiscoveryConfig {
fn default() -> Self {
Self {
ttl: 64,
attempt_timeouts_secs: vec![1, 2, 4, 8],
recent_expiry_secs: 10,
backoff_base_secs: 0,
backoff_max_secs: 0,
forward_min_interval_secs: 2,
nostr: NostrDiscoveryConfig::default(),
lan: crate::discovery::lan::LanDiscoveryConfig::default(),
}
}
}
impl DiscoveryConfig {
fn default_ttl() -> u8 {
64
}
fn default_attempt_timeouts_secs() -> Vec<u64> {
vec![1, 2, 4, 8]
}
fn default_recent_expiry_secs() -> u64 {
10
}
fn default_backoff_base_secs() -> u64 {
0
}
fn default_backoff_max_secs() -> u64 {
0
}
fn default_forward_min_interval_secs() -> u64 {
2
}
fn default_nostr() -> NostrDiscoveryConfig {
NostrDiscoveryConfig::default()
}
fn default_lan() -> crate::discovery::lan::LanDiscoveryConfig {
crate::discovery::lan::LanDiscoveryConfig::default()
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum NostrDiscoveryPolicy {
Disabled,
#[default]
ConfiguredOnly,
Open,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct NostrDiscoveryConfig {
#[serde(default)]
pub enabled: bool,
#[serde(default = "NostrDiscoveryConfig::default_advertise")]
pub advertise: bool,
#[serde(default = "NostrDiscoveryConfig::default_advert_relays")]
pub advert_relays: Vec<String>,
#[serde(default = "NostrDiscoveryConfig::default_dm_relays")]
pub dm_relays: Vec<String>,
#[serde(default = "NostrDiscoveryConfig::default_stun_servers")]
pub stun_servers: Vec<String>,
#[serde(default)]
pub share_local_candidates: bool,
#[serde(default = "NostrDiscoveryConfig::default_app")]
pub app: String,
#[serde(default = "NostrDiscoveryConfig::default_signal_ttl_secs")]
pub signal_ttl_secs: u64,
#[serde(default)]
pub policy: NostrDiscoveryPolicy,
#[serde(default = "NostrDiscoveryConfig::default_open_discovery_max_pending")]
pub open_discovery_max_pending: usize,
#[serde(default = "NostrDiscoveryConfig::default_max_concurrent_incoming_offers")]
pub max_concurrent_incoming_offers: usize,
#[serde(default = "NostrDiscoveryConfig::default_advert_cache_max_entries")]
pub advert_cache_max_entries: usize,
#[serde(default = "NostrDiscoveryConfig::default_seen_sessions_max_entries")]
pub seen_sessions_max_entries: usize,
#[serde(default = "NostrDiscoveryConfig::default_attempt_timeout_secs")]
pub attempt_timeout_secs: u64,
#[serde(default = "NostrDiscoveryConfig::default_replay_window_secs")]
pub replay_window_secs: u64,
#[serde(default = "NostrDiscoveryConfig::default_punch_start_delay_ms")]
pub punch_start_delay_ms: u64,
#[serde(default = "NostrDiscoveryConfig::default_punch_interval_ms")]
pub punch_interval_ms: u64,
#[serde(default = "NostrDiscoveryConfig::default_punch_duration_ms")]
pub punch_duration_ms: u64,
#[serde(default = "NostrDiscoveryConfig::default_advert_ttl_secs")]
pub advert_ttl_secs: u64,
#[serde(default = "NostrDiscoveryConfig::default_advert_refresh_secs")]
pub advert_refresh_secs: u64,
#[serde(default = "NostrDiscoveryConfig::default_startup_sweep_delay_secs")]
pub startup_sweep_delay_secs: u64,
#[serde(default = "NostrDiscoveryConfig::default_startup_sweep_max_age_secs")]
pub startup_sweep_max_age_secs: u64,
#[serde(default = "NostrDiscoveryConfig::default_failure_streak_threshold")]
pub failure_streak_threshold: u32,
#[serde(default = "NostrDiscoveryConfig::default_extended_cooldown_secs")]
pub extended_cooldown_secs: u64,
#[serde(default = "NostrDiscoveryConfig::default_warn_log_interval_secs")]
pub warn_log_interval_secs: u64,
#[serde(default = "NostrDiscoveryConfig::default_failure_state_max_entries")]
pub failure_state_max_entries: usize,
#[serde(default = "NostrDiscoveryConfig::default_protocol_mismatch_cooldown_secs")]
pub protocol_mismatch_cooldown_secs: u64,
}
impl Default for NostrDiscoveryConfig {
fn default() -> Self {
Self {
enabled: false,
advertise: Self::default_advertise(),
advert_relays: Self::default_advert_relays(),
dm_relays: Self::default_dm_relays(),
stun_servers: Self::default_stun_servers(),
share_local_candidates: false,
app: Self::default_app(),
signal_ttl_secs: Self::default_signal_ttl_secs(),
policy: NostrDiscoveryPolicy::default(),
open_discovery_max_pending: Self::default_open_discovery_max_pending(),
max_concurrent_incoming_offers: Self::default_max_concurrent_incoming_offers(),
advert_cache_max_entries: Self::default_advert_cache_max_entries(),
seen_sessions_max_entries: Self::default_seen_sessions_max_entries(),
attempt_timeout_secs: Self::default_attempt_timeout_secs(),
replay_window_secs: Self::default_replay_window_secs(),
punch_start_delay_ms: Self::default_punch_start_delay_ms(),
punch_interval_ms: Self::default_punch_interval_ms(),
punch_duration_ms: Self::default_punch_duration_ms(),
advert_ttl_secs: Self::default_advert_ttl_secs(),
advert_refresh_secs: Self::default_advert_refresh_secs(),
startup_sweep_delay_secs: Self::default_startup_sweep_delay_secs(),
startup_sweep_max_age_secs: Self::default_startup_sweep_max_age_secs(),
failure_streak_threshold: Self::default_failure_streak_threshold(),
extended_cooldown_secs: Self::default_extended_cooldown_secs(),
warn_log_interval_secs: Self::default_warn_log_interval_secs(),
failure_state_max_entries: Self::default_failure_state_max_entries(),
protocol_mismatch_cooldown_secs: Self::default_protocol_mismatch_cooldown_secs(),
}
}
}
impl NostrDiscoveryConfig {
fn default_advertise() -> bool {
true
}
fn default_advert_relays() -> Vec<String> {
vec![
"wss://relay.damus.io".to_string(),
"wss://nos.lol".to_string(),
"wss://offchain.pub".to_string(),
]
}
fn default_dm_relays() -> Vec<String> {
vec![
"wss://relay.damus.io".to_string(),
"wss://nos.lol".to_string(),
"wss://offchain.pub".to_string(),
]
}
fn default_stun_servers() -> Vec<String> {
vec![
"stun:stun.l.google.com:19302".to_string(),
"stun:stun.cloudflare.com:3478".to_string(),
"stun:global.stun.twilio.com:3478".to_string(),
]
}
fn default_app() -> String {
"fips-overlay-v1".to_string()
}
fn default_signal_ttl_secs() -> u64 {
120
}
fn default_open_discovery_max_pending() -> usize {
64
}
fn default_max_concurrent_incoming_offers() -> usize {
16
}
fn default_advert_cache_max_entries() -> usize {
2048
}
fn default_seen_sessions_max_entries() -> usize {
2048
}
fn default_attempt_timeout_secs() -> u64 {
10
}
fn default_replay_window_secs() -> u64 {
300
}
fn default_punch_start_delay_ms() -> u64 {
2_000
}
fn default_punch_interval_ms() -> u64 {
200
}
fn default_punch_duration_ms() -> u64 {
10_000
}
fn default_advert_ttl_secs() -> u64 {
3_600
}
fn default_advert_refresh_secs() -> u64 {
1_800
}
fn default_startup_sweep_delay_secs() -> u64 {
5
}
fn default_startup_sweep_max_age_secs() -> u64 {
3_600
}
fn default_failure_streak_threshold() -> u32 {
5
}
fn default_extended_cooldown_secs() -> u64 {
1_800
}
fn default_warn_log_interval_secs() -> u64 {
300
}
fn default_failure_state_max_entries() -> usize {
4_096
}
fn default_protocol_mismatch_cooldown_secs() -> u64 {
86_400
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TreeConfig {
#[serde(default = "TreeConfig::default_announce_min_interval_ms")]
pub announce_min_interval_ms: u64,
#[serde(default = "TreeConfig::default_parent_hysteresis")]
pub parent_hysteresis: f64,
#[serde(default = "TreeConfig::default_hold_down_secs")]
pub hold_down_secs: u64,
#[serde(default = "TreeConfig::default_reeval_interval_secs")]
pub reeval_interval_secs: u64,
#[serde(default = "TreeConfig::default_flap_threshold")]
pub flap_threshold: u32,
#[serde(default = "TreeConfig::default_flap_window_secs")]
pub flap_window_secs: u64,
#[serde(default = "TreeConfig::default_flap_dampening_secs")]
pub flap_dampening_secs: u64,
}
impl Default for TreeConfig {
fn default() -> Self {
Self {
announce_min_interval_ms: 500,
parent_hysteresis: 0.2,
hold_down_secs: 30,
reeval_interval_secs: 60,
flap_threshold: 4,
flap_window_secs: 60,
flap_dampening_secs: 120,
}
}
}
impl TreeConfig {
fn default_announce_min_interval_ms() -> u64 {
500
}
fn default_parent_hysteresis() -> f64 {
0.2
}
fn default_hold_down_secs() -> u64 {
30
}
fn default_reeval_interval_secs() -> u64 {
60
}
fn default_flap_threshold() -> u32 {
4
}
fn default_flap_window_secs() -> u64 {
60
}
fn default_flap_dampening_secs() -> u64 {
120
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoutingConfig {
#[serde(default)]
pub mode: RoutingMode,
#[serde(default = "RoutingConfig::default_learned_ttl_secs")]
pub learned_ttl_secs: u64,
#[serde(default = "RoutingConfig::default_max_learned_routes_per_dest")]
pub max_learned_routes_per_dest: usize,
#[serde(default = "RoutingConfig::default_learned_fallback_explore_interval")]
pub learned_fallback_explore_interval: u64,
}
impl Default for RoutingConfig {
fn default() -> Self {
Self {
mode: RoutingMode::default(),
learned_ttl_secs: 300,
max_learned_routes_per_dest: 4,
learned_fallback_explore_interval: 16,
}
}
}
impl RoutingConfig {
fn default_learned_ttl_secs() -> u64 {
300
}
fn default_max_learned_routes_per_dest() -> usize {
4
}
fn default_learned_fallback_explore_interval() -> u64 {
16
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum RoutingMode {
#[default]
Tree,
ReplyLearned,
}
impl std::fmt::Display for RoutingMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RoutingMode::Tree => write!(f, "tree"),
RoutingMode::ReplyLearned => write!(f, "reply_learned"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BloomConfig {
#[serde(default = "BloomConfig::default_update_debounce_ms")]
pub update_debounce_ms: u64,
#[serde(default = "BloomConfig::default_max_inbound_fpr")]
pub max_inbound_fpr: f64,
}
impl Default for BloomConfig {
fn default() -> Self {
Self {
update_debounce_ms: 500,
max_inbound_fpr: 0.05,
}
}
}
impl BloomConfig {
fn default_update_debounce_ms() -> u64 {
500
}
fn default_max_inbound_fpr() -> f64 {
0.05
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionConfig {
#[serde(default = "SessionConfig::default_ttl")]
pub default_ttl: u8,
#[serde(default = "SessionConfig::default_pending_packets_per_dest")]
pub pending_packets_per_dest: usize,
#[serde(default = "SessionConfig::default_pending_max_destinations")]
pub pending_max_destinations: usize,
#[serde(default = "SessionConfig::default_idle_timeout_secs")]
pub idle_timeout_secs: u64,
#[serde(default = "SessionConfig::default_coords_warmup_packets")]
pub coords_warmup_packets: u8,
#[serde(default = "SessionConfig::default_coords_response_interval_ms")]
pub coords_response_interval_ms: u64,
}
impl Default for SessionConfig {
fn default() -> Self {
Self {
default_ttl: 64,
pending_packets_per_dest: 16,
pending_max_destinations: 256,
idle_timeout_secs: 90,
coords_warmup_packets: 5,
coords_response_interval_ms: 2000,
}
}
}
impl SessionConfig {
fn default_ttl() -> u8 {
64
}
fn default_pending_packets_per_dest() -> usize {
16
}
fn default_pending_max_destinations() -> usize {
256
}
fn default_idle_timeout_secs() -> u64 {
90
}
fn default_coords_warmup_packets() -> u8 {
5
}
fn default_coords_response_interval_ms() -> u64 {
2000
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionMmpConfig {
#[serde(default)]
pub mode: MmpMode,
#[serde(default = "SessionMmpConfig::default_log_interval_secs")]
pub log_interval_secs: u64,
#[serde(default = "SessionMmpConfig::default_owd_window_size")]
pub owd_window_size: usize,
}
impl Default for SessionMmpConfig {
fn default() -> Self {
Self {
mode: MmpMode::default(),
log_interval_secs: DEFAULT_LOG_INTERVAL_SECS,
owd_window_size: DEFAULT_OWD_WINDOW_SIZE,
}
}
}
impl SessionMmpConfig {
fn default_log_interval_secs() -> u64 {
DEFAULT_LOG_INTERVAL_SECS
}
fn default_owd_window_size() -> usize {
DEFAULT_OWD_WINDOW_SIZE
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ControlConfig {
#[serde(default = "ControlConfig::default_enabled")]
pub enabled: bool,
#[serde(default = "ControlConfig::default_socket_path")]
pub socket_path: String,
}
impl Default for ControlConfig {
fn default() -> Self {
Self {
enabled: true,
socket_path: Self::default_socket_path(),
}
}
}
impl ControlConfig {
fn default_enabled() -> bool {
true
}
fn default_socket_path() -> String {
#[cfg(unix)]
{
super::resolve_default_socket("control.sock")
}
#[cfg(windows)]
{
"21210".to_string()
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BuffersConfig {
#[serde(default = "BuffersConfig::default_packet_channel")]
pub packet_channel: usize,
#[serde(default = "BuffersConfig::default_tun_channel")]
pub tun_channel: usize,
#[serde(default = "BuffersConfig::default_dns_channel")]
pub dns_channel: usize,
}
impl Default for BuffersConfig {
fn default() -> Self {
Self {
packet_channel: 1024,
tun_channel: 1024,
dns_channel: 64,
}
}
}
impl BuffersConfig {
fn default_packet_channel() -> usize {
1024
}
fn default_tun_channel() -> usize {
1024
}
fn default_dns_channel() -> usize {
64
}
}
const DEFAULT_REKEY_AFTER_MESSAGES: u64 = 1 << 48;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RekeyConfig {
#[serde(default = "RekeyConfig::default_enabled")]
pub enabled: bool,
#[serde(default = "RekeyConfig::default_after_secs")]
pub after_secs: u64,
#[serde(default = "RekeyConfig::default_after_messages")]
pub after_messages: u64,
}
impl Default for RekeyConfig {
fn default() -> Self {
Self {
enabled: true,
after_secs: 120,
after_messages: DEFAULT_REKEY_AFTER_MESSAGES,
}
}
}
impl RekeyConfig {
fn default_enabled() -> bool {
true
}
fn default_after_secs() -> u64 {
120
}
fn default_after_messages() -> u64 {
DEFAULT_REKEY_AFTER_MESSAGES
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EcnConfig {
#[serde(default = "EcnConfig::default_enabled")]
pub enabled: bool,
#[serde(default = "EcnConfig::default_loss_threshold")]
pub loss_threshold: f64,
#[serde(default = "EcnConfig::default_etx_threshold")]
pub etx_threshold: f64,
}
impl Default for EcnConfig {
fn default() -> Self {
Self {
enabled: true,
loss_threshold: 0.05,
etx_threshold: 3.0,
}
}
}
impl EcnConfig {
fn default_enabled() -> bool {
true
}
fn default_loss_threshold() -> f64 {
0.05
}
fn default_etx_threshold() -> f64 {
3.0
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeConfig {
#[serde(default)]
pub identity: IdentityConfig,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub leaf_only: bool,
#[serde(default = "NodeConfig::default_tick_interval_secs")]
pub tick_interval_secs: u64,
#[serde(default = "NodeConfig::default_base_rtt_ms")]
pub base_rtt_ms: u64,
#[serde(default = "NodeConfig::default_heartbeat_interval_secs")]
pub heartbeat_interval_secs: u64,
#[serde(default = "NodeConfig::default_link_dead_timeout_secs")]
pub link_dead_timeout_secs: u64,
#[serde(default = "NodeConfig::default_fast_link_dead_timeout_secs")]
pub fast_link_dead_timeout_secs: u64,
#[serde(default)]
pub limits: LimitsConfig,
#[serde(default)]
pub rate_limit: RateLimitConfig,
#[serde(default)]
pub retry: RetryConfig,
#[serde(default)]
pub cache: CacheConfig,
#[serde(default)]
pub discovery: DiscoveryConfig,
#[serde(default)]
pub tree: TreeConfig,
#[serde(default)]
pub routing: RoutingConfig,
#[serde(default)]
pub bloom: BloomConfig,
#[serde(default)]
pub session: SessionConfig,
#[serde(default)]
pub buffers: BuffersConfig,
#[serde(default)]
pub control: ControlConfig,
#[serde(default)]
pub mmp: MmpConfig,
#[serde(default)]
pub session_mmp: SessionMmpConfig,
#[serde(default)]
pub ecn: EcnConfig,
#[serde(default)]
pub rekey: RekeyConfig,
#[serde(default = "NodeConfig::default_system_files_enabled")]
pub system_files_enabled: bool,
#[serde(default)]
pub log_level: Option<String>,
}
impl Default for NodeConfig {
fn default() -> Self {
Self {
identity: IdentityConfig::default(),
leaf_only: false,
tick_interval_secs: 1,
base_rtt_ms: 100,
heartbeat_interval_secs: 10,
link_dead_timeout_secs: 30,
fast_link_dead_timeout_secs: 5,
limits: LimitsConfig::default(),
rate_limit: RateLimitConfig::default(),
retry: RetryConfig::default(),
cache: CacheConfig::default(),
discovery: DiscoveryConfig::default(),
tree: TreeConfig::default(),
routing: RoutingConfig::default(),
bloom: BloomConfig::default(),
session: SessionConfig::default(),
buffers: BuffersConfig::default(),
control: ControlConfig::default(),
mmp: MmpConfig::default(),
session_mmp: SessionMmpConfig::default(),
ecn: EcnConfig::default(),
rekey: RekeyConfig::default(),
system_files_enabled: true,
log_level: None,
}
}
}
impl NodeConfig {
pub fn log_level(&self) -> tracing::Level {
match self
.log_level
.as_deref()
.map(|s| s.to_lowercase())
.as_deref()
{
Some("trace") => tracing::Level::TRACE,
Some("debug") => tracing::Level::DEBUG,
Some("warn") | Some("warning") => tracing::Level::WARN,
Some("error") => tracing::Level::ERROR,
_ => tracing::Level::INFO,
}
}
fn default_tick_interval_secs() -> u64 {
1
}
fn default_base_rtt_ms() -> u64 {
100
}
fn default_heartbeat_interval_secs() -> u64 {
10
}
fn default_link_dead_timeout_secs() -> u64 {
30
}
fn default_fast_link_dead_timeout_secs() -> u64 {
5
}
fn default_system_files_enabled() -> bool {
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ecn_config_defaults() {
let c = EcnConfig::default();
assert!(c.enabled);
assert!((c.loss_threshold - 0.05).abs() < 1e-9);
assert!((c.etx_threshold - 3.0).abs() < 1e-9);
}
#[test]
fn test_rekey_config_defaults() {
let c = RekeyConfig::default();
assert!(c.enabled);
assert_eq!(c.after_secs, 120);
assert_eq!(c.after_messages, 1 << 48);
}
#[test]
fn test_rekey_config_partial_yaml_uses_defaults() {
let yaml = "after_secs: 30\n";
let c: RekeyConfig = serde_yaml::from_str(yaml).unwrap();
assert!(c.enabled);
assert_eq!(c.after_secs, 30);
assert_eq!(c.after_messages, 1 << 48);
}
#[test]
fn test_routing_config_defaults() {
let c = RoutingConfig::default();
assert_eq!(c.mode, RoutingMode::Tree);
assert_eq!(c.learned_ttl_secs, 300);
assert_eq!(c.max_learned_routes_per_dest, 4);
assert_eq!(c.learned_fallback_explore_interval, 16);
}
#[test]
fn test_routing_config_yaml() {
let yaml = "mode: reply_learned\nlearned_ttl_secs: 120\nmax_learned_routes_per_dest: 2\nlearned_fallback_explore_interval: 8\n";
let c: RoutingConfig = serde_yaml::from_str(yaml).unwrap();
assert_eq!(c.mode, RoutingMode::ReplyLearned);
assert_eq!(c.learned_ttl_secs, 120);
assert_eq!(c.max_learned_routes_per_dest, 2);
assert_eq!(c.learned_fallback_explore_interval, 8);
}
#[test]
fn test_ecn_config_yaml_roundtrip() {
let yaml = "loss_threshold: 0.10\netx_threshold: 2.5\nenabled: false\n";
let c: EcnConfig = serde_yaml::from_str(yaml).unwrap();
assert!(!c.enabled);
assert!((c.loss_threshold - 0.10).abs() < 1e-9);
assert!((c.etx_threshold - 2.5).abs() < 1e-9);
}
#[test]
fn test_ecn_config_partial_yaml() {
let yaml = "loss_threshold: 0.02\n";
let c: EcnConfig = serde_yaml::from_str(yaml).unwrap();
assert!(c.enabled); assert!((c.loss_threshold - 0.02).abs() < 1e-9);
assert!((c.etx_threshold - 3.0).abs() < 1e-9); }
#[test]
fn test_nostr_discovery_startup_sweep_defaults() {
let c = NostrDiscoveryConfig::default();
assert_eq!(c.startup_sweep_delay_secs, 5);
assert_eq!(c.startup_sweep_max_age_secs, 3_600);
}
#[test]
fn test_nostr_discovery_startup_sweep_yaml_override() {
let yaml = "enabled: true\npolicy: open\nstartup_sweep_delay_secs: 10\nstartup_sweep_max_age_secs: 1800\n";
let c: NostrDiscoveryConfig = serde_yaml::from_str(yaml).unwrap();
assert!(c.enabled);
assert_eq!(c.policy, NostrDiscoveryPolicy::Open);
assert_eq!(c.startup_sweep_delay_secs, 10);
assert_eq!(c.startup_sweep_max_age_secs, 1_800);
}
#[test]
fn test_nostr_discovery_startup_sweep_partial_yaml_uses_defaults() {
let yaml = "enabled: true\nstartup_sweep_delay_secs: 30\n";
let c: NostrDiscoveryConfig = serde_yaml::from_str(yaml).unwrap();
assert_eq!(c.startup_sweep_delay_secs, 30);
assert_eq!(c.startup_sweep_max_age_secs, 3_600);
}
#[test]
fn test_log_level_parser() {
let cases: &[(Option<&str>, tracing::Level)] = &[
(Some("trace"), tracing::Level::TRACE),
(Some("debug"), tracing::Level::DEBUG),
(Some("warn"), tracing::Level::WARN),
(Some("warning"), tracing::Level::WARN),
(Some("error"), tracing::Level::ERROR),
(Some("info"), tracing::Level::INFO),
(None, tracing::Level::INFO),
(Some("TRACE"), tracing::Level::TRACE),
(Some("Debug"), tracing::Level::DEBUG),
(Some("Warning"), tracing::Level::WARN),
(Some("WARN"), tracing::Level::WARN),
(Some("ERROR"), tracing::Level::ERROR),
(Some("INFO"), tracing::Level::INFO),
(Some("verbose"), tracing::Level::INFO),
(Some("nonsense"), tracing::Level::INFO),
(Some(""), tracing::Level::INFO),
];
for (input, expected) in cases {
let cfg = NodeConfig {
log_level: input.map(|s| s.to_string()),
..NodeConfig::default()
};
assert_eq!(
cfg.log_level(),
*expected,
"input {:?} should map to {:?}",
input,
expected
);
}
}
#[cfg(windows)]
#[test]
fn test_default_socket_path_windows() {
let config = ControlConfig::default();
let port: u16 = config
.socket_path
.parse()
.expect("should be a valid port number");
assert_eq!(port, 21210);
}
}