- 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
334 lines
10 KiB
Rust
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);
|
|
}
|
|
}
|