synor/crates/synor-network/src/config.rs
Gulshan Yadav b22c1b89f0 feat: Phase 7 production readiness improvements
- Add SYNOR_BOOTSTRAP_PEERS env var for runtime seed node configuration
- Implement secrets provider abstraction for faucet wallet key security
  (supports file-based secrets in /run/secrets for production)
- Create WASM crypto crate foundation for web wallet (Ed25519, BIP-39)
- Add DEPLOYMENT.md guide for testnet deployment
- Add SECURITY_AUDIT_SCOPE.md for external security audit preparation
- Document seed node deployment process in synor-network

Security improvements:
- Faucet now auto-detects /run/secrets for secure key storage
- CORS already defaults to specific origins (https://faucet.synor.cc)
- Bootstrap peers now configurable at runtime without recompilation
2026-01-08 07:21:14 +05:30

334 lines
10 KiB
Rust

//! Network configuration.
use crate::{ChainId, DEFAULT_PORT, MAX_PEERS};
use libp2p::Multiaddr;
use std::time::Duration;
/// Network configuration.
#[derive(Clone, Debug)]
pub struct NetworkConfig {
/// Chain identifier.
pub chain_id: ChainId,
/// Listen addresses.
pub listen_addresses: Vec<Multiaddr>,
/// Bootstrap peers.
pub bootstrap_peers: Vec<Multiaddr>,
/// Maximum number of inbound connections.
pub max_inbound: usize,
/// Maximum number of outbound connections.
pub max_outbound: usize,
/// Enable mDNS for local peer discovery.
pub enable_mdns: bool,
/// Enable Kademlia DHT.
pub enable_kad: bool,
/// Connection idle timeout.
pub idle_timeout: Duration,
/// Ping interval.
pub ping_interval: Duration,
/// Gossipsub configuration.
pub gossipsub: GossipsubConfig,
/// Sync configuration.
pub sync: SyncConfig,
/// External address (for NAT traversal).
pub external_address: Option<Multiaddr>,
/// Node name for identification.
pub node_name: Option<String>,
}
impl Default for NetworkConfig {
fn default() -> Self {
NetworkConfig {
chain_id: ChainId::Mainnet,
listen_addresses: vec![
format!("/ip4/0.0.0.0/tcp/{}", DEFAULT_PORT)
.parse()
.unwrap(),
format!("/ip6/::/tcp/{}", DEFAULT_PORT).parse().unwrap(),
],
bootstrap_peers: Vec::new(),
max_inbound: MAX_PEERS / 2,
max_outbound: MAX_PEERS / 2,
enable_mdns: true,
enable_kad: true,
idle_timeout: Duration::from_secs(30),
ping_interval: Duration::from_secs(15),
gossipsub: GossipsubConfig::default(),
sync: SyncConfig::default(),
external_address: None,
node_name: None,
}
}
}
impl NetworkConfig {
/// Creates a configuration for mainnet.
pub fn mainnet() -> Self {
NetworkConfig {
chain_id: ChainId::Mainnet,
bootstrap_peers: mainnet_bootstrap_peers(),
enable_mdns: false,
..Default::default()
}
}
/// Creates a configuration for testnet.
pub fn testnet() -> Self {
NetworkConfig {
chain_id: ChainId::Testnet,
bootstrap_peers: testnet_bootstrap_peers(),
listen_addresses: vec![format!("/ip4/0.0.0.0/tcp/{}", DEFAULT_PORT + 1000)
.parse()
.unwrap()],
..Default::default()
}
}
/// Creates a configuration for local development.
pub fn devnet() -> Self {
NetworkConfig {
chain_id: ChainId::Devnet,
enable_mdns: true,
enable_kad: false,
max_inbound: 10,
max_outbound: 10,
..Default::default()
}
}
/// Total maximum peers.
pub fn max_peers(&self) -> usize {
self.max_inbound + self.max_outbound
}
/// Adds a bootstrap peer.
pub fn with_bootstrap_peer(mut self, addr: Multiaddr) -> Self {
self.bootstrap_peers.push(addr);
self
}
/// Sets the listen address.
pub fn with_listen_address(mut self, addr: Multiaddr) -> Self {
self.listen_addresses = vec![addr];
self
}
/// Sets the node name.
pub fn with_node_name(mut self, name: impl Into<String>) -> Self {
self.node_name = Some(name.into());
self
}
}
/// GossipSub configuration.
#[derive(Clone, Debug)]
pub struct GossipsubConfig {
/// Heartbeat interval.
pub heartbeat_interval: Duration,
/// Target mesh size.
pub mesh_n: usize,
/// Minimum mesh size.
pub mesh_n_low: usize,
/// Maximum mesh size.
pub mesh_n_high: usize,
/// Number of lazy peers to send gossip.
pub gossip_lazy: usize,
/// Fanout time-to-live.
pub fanout_ttl: Duration,
/// History length for gossip.
pub history_length: usize,
/// History gossip length.
pub history_gossip: usize,
/// Duplicate cache time.
pub duplicate_cache_time: Duration,
/// Message validation mode.
pub validate_messages: bool,
}
impl Default for GossipsubConfig {
fn default() -> Self {
GossipsubConfig {
heartbeat_interval: Duration::from_secs(1),
mesh_n: 6,
mesh_n_low: 4,
mesh_n_high: 12,
gossip_lazy: 6,
fanout_ttl: Duration::from_secs(60),
history_length: 5,
history_gossip: 3,
duplicate_cache_time: Duration::from_secs(60),
validate_messages: true,
}
}
}
/// Synchronization configuration.
#[derive(Clone, Debug)]
pub struct SyncConfig {
/// Maximum concurrent block downloads.
pub max_concurrent_downloads: usize,
/// Block request timeout.
pub request_timeout: Duration,
/// Maximum blocks per request.
pub max_blocks_per_request: usize,
/// Sync batch size.
pub batch_size: usize,
/// Time to wait before retrying failed requests.
pub retry_delay: Duration,
/// Maximum retries for failed requests.
pub max_retries: u32,
/// Header-first sync depth.
pub headers_first_depth: u64,
}
impl Default for SyncConfig {
fn default() -> Self {
SyncConfig {
max_concurrent_downloads: 16,
request_timeout: Duration::from_secs(30),
max_blocks_per_request: 100,
batch_size: 500,
retry_delay: Duration::from_secs(5),
max_retries: 3,
headers_first_depth: 10000,
}
}
}
/// Returns mainnet bootstrap peers.
fn mainnet_bootstrap_peers() -> Vec<Multiaddr> {
// Mainnet bootstrap nodes - will be populated after mainnet launch
// Format: /dns4/<hostname>/tcp/<port>/p2p/<peer_id>
vec![
// Example (uncomment when deployed):
// "/dns4/seed1.synor.cc/tcp/16511/p2p/12D3KooW...".parse().unwrap(),
// "/dns4/seed2.synor.cc/tcp/16511/p2p/12D3KooW...".parse().unwrap(),
// "/dns4/seed3.synor.cc/tcp/16511/p2p/12D3KooW...".parse().unwrap(),
]
}
/// Returns testnet bootstrap peers.
///
/// # Seed Node Deployment Process
///
/// To add a new seed node:
///
/// 1. **Deploy synord on a server** with a static IP/hostname:
/// ```bash
/// synord --network testnet --rpc-host 0.0.0.0
/// ```
///
/// 2. **Get the peer ID** from startup logs:
/// ```
/// INFO synor_network::service: Local peer ID: 12D3KooWAbCdEfGhIjKlMnOpQrStUvWxYz123456789
/// ```
///
/// 3. **Add the full multiaddr** (hostname + port + peer_id):
/// ```text
/// /dns4/testnet-seed1.synor.cc/tcp/17511/p2p/12D3KooWAbCdEfGhIjKlMnOpQrStUvWxYz123456789
/// ```
///
/// # Runtime Configuration
///
/// Instead of hardcoding, operators can use the `SYNOR_BOOTSTRAP_PEERS` environment
/// variable at the synord application level (comma-separated multiaddrs).
fn testnet_bootstrap_peers() -> Vec<Multiaddr> {
// Testnet bootstrap nodes - add peer IDs when seed nodes are deployed
// Format: /dns4/<hostname>/tcp/<port>/p2p/<peer_id>
//
// NOTE: Seeds are configured empty here because peer IDs are only known
// after deployment. Use SYNOR_BOOTSTRAP_PEERS env var or config file
// to specify bootstrap peers at runtime.
let seeds: &[&str] = &[
// North America (seed1.synor.cc) - uncomment after deployment:
// "/dns4/testnet-seed1.synor.cc/tcp/17511/p2p/12D3KooW...",
// Europe (seed2.synor.cc) - uncomment after deployment:
// "/dns4/testnet-seed2.synor.cc/tcp/17511/p2p/12D3KooW...",
// Asia (seed3.synor.cc) - uncomment after deployment:
// "/dns4/testnet-seed3.synor.cc/tcp/17511/p2p/12D3KooW...",
];
seeds.iter().filter_map(|s| s.parse().ok()).collect()
}
/// Returns devnet bootstrap peers for local development.
///
/// For local multi-node testing, start the first node and note its peer ID,
/// then configure other nodes to bootstrap from it.
fn devnet_bootstrap_peers() -> Vec<Multiaddr> {
// Local devnet typically uses mDNS for discovery
// Add local bootstrap peers here if needed for testing without mDNS
vec![]
}
/// Testnet network parameters.
pub mod testnet_params {
/// Testnet chain ID.
pub const CHAIN_ID: u64 = 1;
/// Target block time in milliseconds.
pub const BLOCK_TIME_MS: u64 = 100;
/// GHOSTDAG K parameter.
pub const GHOSTDAG_K: u32 = 18;
/// Default P2P port for testnet.
pub const DEFAULT_PORT: u16 = 17511;
/// Default RPC port for testnet.
pub const RPC_PORT: u16 = 17110;
/// Default WebSocket port for testnet.
pub const WS_PORT: u16 = 17111;
}
/// Mainnet network parameters.
pub mod mainnet_params {
/// Mainnet chain ID.
pub const CHAIN_ID: u64 = 0;
/// Target block time in milliseconds.
pub const BLOCK_TIME_MS: u64 = 1000;
/// GHOSTDAG K parameter.
pub const GHOSTDAG_K: u32 = 18;
/// Default P2P port for mainnet.
pub const DEFAULT_PORT: u16 = 16511;
/// Default RPC port for mainnet.
pub const RPC_PORT: u16 = 16110;
/// Default WebSocket port for mainnet.
pub const WS_PORT: u16 = 16111;
}
/// Devnet network parameters for local development and testing.
pub mod devnet_params {
/// Devnet chain ID.
pub const CHAIN_ID: u64 = 2;
/// Target block time in milliseconds (faster for testing).
pub const BLOCK_TIME_MS: u64 = 50;
/// GHOSTDAG K parameter (smaller for faster consensus).
pub const GHOSTDAG_K: u32 = 8;
/// Default P2P port for devnet.
pub const DEFAULT_PORT: u16 = 18511;
/// Default RPC port for devnet.
pub const RPC_PORT: u16 = 18110;
/// Default WebSocket port for devnet.
pub const WS_PORT: u16 = 18111;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = NetworkConfig::default();
assert_eq!(config.chain_id, ChainId::Mainnet);
assert!(config.max_peers() > 0);
}
#[test]
fn test_testnet_config() {
let config = NetworkConfig::testnet();
assert_eq!(config.chain_id, ChainId::Testnet);
}
#[test]
fn test_devnet_config() {
let config = NetworkConfig::devnet();
assert!(config.enable_mdns);
assert!(!config.enable_kad);
}
}