diff --git a/Cargo.lock b/Cargo.lock index c3448a2..88157e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -759,6 +759,7 @@ dependencies = [ "serde_json", "tempfile", "tokio", + "toml", ] [[package]] @@ -942,9 +943,9 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" -version = "2.5.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mime" @@ -1329,6 +1330,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1511,6 +1521,47 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "tower-service" version = "0.3.3" @@ -1869,6 +1920,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" diff --git a/Cargo.toml b/Cargo.toml index 9a3295e..5ed45a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ chrono = { version = "0.4", default-features = false, features = ["clock"] } rand = "0.4" serde_json = { version = "1.0" } serde = { version = "1.0", features = ["derive"] } +toml = { version = "0.8" } tokio = { version = "1", features = [ "io-util", "macros", "rt", "rt-multi-thread", "sync", "net", "time", "io-std" ] } reqwest = { version = "0.11", features = ["tokio-native-tls", "stream"] } futures-util = "0.3" diff --git a/README.md b/README.md index bef9867..15f5ca9 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,13 @@ cd ldk-sample cargo run ``` The only CLI argument is the storage directory path. All configuration is read from -`/.ldk/config.json`. +`/.ldk/config.toml`. -Use `config.example.json` in the repo root as a template for your config file. +Use `config.example.toml` in the repo root as a template for your config file. ## Configuration -Config is loaded from `/.ldk/config.json` and strictly validated. Unknown +Config is loaded from `/.ldk/config.toml` and strictly validated. Unknown fields cause an error (`deny_unknown_fields`). Required sections: `bitcoind`. @@ -29,32 +29,33 @@ Network options: `mainnet`, `testnet`, `regtest`, `signet` (default is `testnet` ### Example config -```json -{ - "bitcoind": { - "rpc_host": "127.0.0.1", - "rpc_port": 8332, - "rpc_username": "your_rpc_user", - "rpc_password": "your_rpc_password" - }, - "network": "testnet", - "ldk": { - "peer_listening_port": 9735, - "announced_node_name": "MyLDKNode", - "announced_listen_addr": [] - }, - "rapid_gossip_sync": { - "enabled": true, - "url": "https://rapidsync.lightningdevkit.org/snapshot/", - "interval_hours": 6 - }, - "probing": { - "interval_sec": 300, - "peers": ["02abc123...@1.2.3.4:9735"], - "amount_msats": [1000, 10000, 100000, 1000000], - "timeout_sec": 60 - } -} +```toml +network = "testnet" + +[bitcoind] +rpc_host = "127.0.0.1" +rpc_port = 8332 +rpc_username = "your_rpc_user" +rpc_password = "your_rpc_password" + +# [ldk] +# peer_listening_port = 9735 +# announced_node_name = "MyLDKNode" +# announced_listen_addr = [] + +[rapid_gossip_sync] +enabled = true +url = "https://rapidsync.lightningdevkit.org/snapshot/" +interval_hours = 6 + +[probing] +interval_sec = 300 +peers = ["02abc123...@1.2.3.4:9735"] +amount_msats = [1000, 10000, 100000, 1000000] +timeout_sec = 60 + +probe_delay_sec = 1 +peer_delay_sec = 2 ``` ### Key options diff --git a/config.example.json b/config.example.json deleted file mode 100644 index 84738a7..0000000 --- a/config.example.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "bitcoind": { - "rpc_host": "127.0.0.1", - "rpc_port": 8332, - "rpc_username": "your_rpc_user", - "rpc_password": "your_rpc_password" - }, - "network": "testnet", - "ldk": { - "peer_listening_port": 9735, - "announced_node_name": "MyLDKNode", - "announced_listen_addr": [] - }, - "rapid_gossip_sync": { - "enabled": true, - "url": "https://rapidsync.lightningdevkit.org/snapshot/", - "interval_hours": 6 - }, - "probing": { - "interval_sec": 300, - "peers": [ - "02abc123...@1.2.3.4:9735" - ], - "amount_msats": [1000, 10000, 100000, 1000000], - "timeout_sec": 60 - } -} diff --git a/config.example.toml b/config.example.toml new file mode 100644 index 0000000..1a2e45f --- /dev/null +++ b/config.example.toml @@ -0,0 +1,27 @@ +[bitcoind] +rpc_host = "127.0.0.1" +rpc_port = 8332 +rpc_username = "your_rpc_user" +rpc_password = "your_rpc_password" + +network = "testnet" + +[ldk] +peer_listening_port = 9735 +announced_node_name = "MyLDKNode" +announced_listen_addr = [] + +[rapid_gossip_sync] +enabled = true +url = "https://rapidsync.lightningdevkit.org/snapshot/" +interval_hours = 6 + +[probing] +interval_sec = 300 +peers = [ + "02abc123...@1.2.3.4:9735" +] +amount_msats = [1000, 10000, 100000, 1000000] +timeout_sec = 60 +probe_delay_sec = 1 +peer_delay_sec = 2 diff --git a/prober_config.json.example b/prober_config.json.example index 55bd1bd..0105357 100644 --- a/prober_config.json.example +++ b/prober_config.json.example @@ -5,5 +5,7 @@ "037d8e050899fa0732fdd6fc1e0d5d8d685c2093932a7e10dc5e6fab8a34ed1c43" ], "probe_amount_msats": [1000, 10000000, 100000000, 500000000], - "probe_timeout_sec": 3 + "probe_timeout_sec": 3, + "probe_delay_sec": 1, + "peer_delay_sec": 2 } diff --git a/src/args.rs b/src/args.rs index 990fab1..a084ff9 100644 --- a/src/args.rs +++ b/src/args.rs @@ -8,7 +8,7 @@ pub(crate) fn parse_startup_args() -> Result { println!("Usage: {} ", args[0]); println!(); println!( - "The config.json file should be located at /.ldk/config.json", + "The config.toml file should be located at /.ldk/config.toml", ); crate::config::print_config_help(); return Err(()); diff --git a/src/cli.rs b/src/cli.rs index 4711ec1..4c7cfd0 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -35,13 +35,15 @@ use std::time::Duration; use tokio::io::{AsyncBufReadExt, BufReader}; -/// Probing configuration passed from config.json +/// Probing configuration passed from config.toml #[derive(Clone)] pub(crate) struct ProbingConfig { pub(crate) interval_sec: u64, pub(crate) peers: Vec, pub(crate) amount_msats: Vec, pub(crate) timeout_sec: u64, + pub(crate) probe_delay_sec: u64, + pub(crate) peer_delay_sec: u64, } pub(crate) struct LdkUserInfo { diff --git a/src/config.rs b/src/config.rs index 3b0ff70..2682cdc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -63,6 +63,10 @@ pub struct ProbingConfig { pub amount_msats: Vec, #[serde(default = "default_probe_timeout")] pub timeout_sec: u64, + #[serde(default = "default_probe_delay")] + pub probe_delay_sec: u64, + #[serde(default = "default_peer_delay")] + pub peer_delay_sec: u64, } // Default functions @@ -86,6 +90,14 @@ fn default_probe_timeout() -> u64 { 60 } +fn default_probe_delay() -> u64 { + 1 +} + +fn default_peer_delay() -> u64 { + 2 +} + impl Default for RapidGossipSyncConfig { fn default() -> Self { Self { enabled: true, url: None, interval_hours: 6 } @@ -94,14 +106,14 @@ impl Default for RapidGossipSyncConfig { impl NodeConfig { pub fn load(ldk_data_dir: &str) -> Result { - let config_path = format!("{}/config.json", ldk_data_dir); + let config_path = format!("{}/config.toml", ldk_data_dir); if !Path::new(&config_path).exists() { return Err(ConfigError::FileNotFound(config_path)); } let content = fs::read_to_string(&config_path) .map_err(|e| ConfigError::ParseError(format!("Failed to read config: {}", e)))?; - let config: NodeConfig = serde_json::from_str(&content) - .map_err(|e| ConfigError::ParseError(format!("Invalid JSON: {}", e)))?; + let config: NodeConfig = toml::from_str(&content) + .map_err(|e| ConfigError::ParseError(format!("Invalid TOML: {}", e)))?; config.validate()?; Ok(config) } @@ -177,6 +189,8 @@ impl NodeConfig { peers: p.peers, amount_msats: p.amount_msats, timeout_sec: p.timeout_sec, + probe_delay_sec: p.probe_delay_sec, + peer_delay_sec: p.peer_delay_sec, }); LdkUserInfo { bitcoind_rpc_username: self.bitcoind.rpc_username, @@ -200,33 +214,33 @@ pub fn print_config_help() { println!("ERROR: Config file not found or invalid."); println!(); println!( - "Please create a config.json file in your LDK data directory with the following structure:" + "Please create a config.toml file in your LDK data directory with the following structure:" ); println!(); println!( - r#"{{ - "bitcoind": {{ - "rpc_host": "127.0.0.1", - "rpc_port": 8332, - "rpc_username": "your_rpc_user", - "rpc_password": "your_rpc_password" - }}, - "network": "testnet", - "ldk": {{ - "peer_listening_port": 9735, - "announced_node_name": "MyNode", - "announced_listen_addr": [] - }}, - "rapid_gossip_sync": {{ - "enabled": true, - "interval_hours": 6 - }}, - "probing": {{ - "interval_sec": 300, - "peers": [], - "amount_msats": [1000, 10000, 100000], - "timeout_sec": 60 - }} -}}"# + r#"[bitcoind] +rpc_host = "127.0.0.1" +rpc_port = 8332 +rpc_username = "your_rpc_user" +rpc_password = "your_rpc_password" + +network = "testnet" + +[ldk] +peer_listening_port = 9735 +announced_node_name = "MyNode" +announced_listen_addr = [] + +[rapid_gossip_sync] +enabled = true +interval_hours = 6 + +[probing] +interval_sec = 300 +peers = [] +amount_msats = [1000, 10000, 100000] +timeout_sec = 60 +probe_delay_sec = 1 +peer_delay_sec = 2"# ); } diff --git a/src/main.rs b/src/main.rs index bd45c21..831613f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1308,25 +1308,29 @@ async fn start_ldk() { let probing_tracker = Arc::clone(&probe_tracker); if let Some(probe_config) = probing_config { if probe_config.peers.is_empty() { - println!("WARNING: probing.peers is empty in config.json. Probing disabled."); + println!("WARNING: probing.peers is empty in config.toml. Probing disabled."); } else if probe_config.amount_msats.is_empty() { - println!("WARNING: probing.amount_msats is empty in config.json. Probing disabled."); + println!("WARNING: probing.amount_msats is empty in config.toml. Probing disabled."); } else { // Sort amounts ascending (smallest to largest) let mut sorted_amounts = probe_config.amount_msats.clone(); sorted_amounts.sort(); let probe_timeout = Duration::from_secs(probe_config.timeout_sec); + let probe_delay = Duration::from_secs(probe_config.probe_delay_sec); + let peer_delay = Duration::from_secs(probe_config.peer_delay_sec); // Log startup configuration lightning::log_info!( &*probing_logger, - "Probing started: {} peers, {} amounts {:?} msat, interval={}s, timeout={}s", + "Probing started: {} peers, {} amounts {:?} msat, interval={}s, timeout={}s, probe_delay={}s, peer_delay={}s", probe_config.peers.len(), sorted_amounts.len(), sorted_amounts, probe_config.interval_sec, - probe_config.timeout_sec + probe_config.timeout_sec, + probe_config.probe_delay_sec, + probe_config.peer_delay_sec ); tokio::spawn(async move { @@ -1336,7 +1340,8 @@ async fn start_ldk() { interval.tick().await; // Probe each peer with amounts from smallest to largest - for peer in &probe_config.peers { + let peer_count = probe_config.peers.len(); + for (peer_idx, peer) in probe_config.peers.iter().enumerate() { let peer_short = truncate_pubkey(peer); 'amounts: for &amount in &sorted_amounts { // Send the probe and get the payment hash @@ -1373,7 +1378,8 @@ async fn start_ldk() { amount, hash ); - // Probe succeeded, continue to next amount + // Probe succeeded, sleep before next amount + tokio::time::sleep(probe_delay).await; }, Ok(Ok(ProbeOutcome::Failed)) => { lightning::log_warn!( @@ -1383,6 +1389,7 @@ async fn start_ldk() { amount, hash ); + tokio::time::sleep(probe_delay).await; break 'amounts; }, Ok(Err(_)) => { @@ -1393,6 +1400,7 @@ async fn start_ldk() { amount, hash ); + tokio::time::sleep(probe_delay).await; break 'amounts; }, Err(_) => { @@ -1409,6 +1417,7 @@ async fn start_ldk() { .unwrap() .pending_probes .remove(&hash); + tokio::time::sleep(probe_delay).await; break 'amounts; }, } @@ -1419,9 +1428,15 @@ async fn start_ldk() { peer_short, amount ); + tokio::time::sleep(probe_delay).await; break 'amounts; } } + + // Sleep between peers (except after the last peer) + if peer_idx < peer_count - 1 { + tokio::time::sleep(peer_delay).await; + } } } });