From e14923ca054333437d172690a61044a903840360 Mon Sep 17 00:00:00 2001 From: G8XSU <3442979+G8XSU@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:13:52 -0800 Subject: [PATCH] Add file based Config support. --- ldk-server/ldk-server.config | 22 ++++++ ldk-server/src/main.rs | 64 +++++----------- ldk-server/src/util/config.rs | 136 ++++++++++++++++++++++++++++++++++ ldk-server/src/util/mod.rs | 1 + 4 files changed, 176 insertions(+), 47 deletions(-) create mode 100644 ldk-server/ldk-server.config create mode 100644 ldk-server/src/util/config.rs diff --git a/ldk-server/ldk-server.config b/ldk-server/ldk-server.config new file mode 100644 index 0000000..b44dc48 --- /dev/null +++ b/ldk-server/ldk-server.config @@ -0,0 +1,22 @@ +{ + // The addresses on which the lightning node will listen for incoming connections. + "listening_address": "localhost:3001", + + // The Bitcoin network to use. + "network": "regtest", + + // The address on which LDK Server will accept incoming requests. + "rest_service_address": "127.0.0.1:3002", + + // The path where the underlying LDK and BDK persist their data. + "storage_dir_path": "/tmp", + + // Bitcoin Core's RPC endpoint. + "bitcoind_rpc_address": "127.0.0.1:8332", + + // Bitcoin Core's RPC user. + "bitcoind_rpc_user": "bitcoind-testuser", + + // Bitcoin Core's RPC password. + "bitcoind_rpc_password": "bitcoind-testpassword" +} diff --git a/ldk-server/src/main.rs b/ldk-server/src/main.rs index c75c446..6d31341 100644 --- a/ldk-server/src/main.rs +++ b/ldk-server/src/main.rs @@ -5,8 +5,6 @@ mod util; use crate::service::NodeService; -use ldk_node::bitcoin::Network; -use ldk_node::lightning::ln::msgs::SocketAddress; use ldk_node::{Builder, Event, LogLevel}; use tokio::net::TcpListener; @@ -15,65 +13,36 @@ use tokio::signal::unix::SignalKind; use hyper::server::conn::http1; use hyper_util::rt::TokioIo; +use crate::util::config::load_config; use ldk_node::config::Config; -use std::net::SocketAddr; -use std::str::FromStr; +use std::path::Path; use std::sync::Arc; fn main() { let args: Vec = std::env::args().collect(); - if args.len() < 8 { - eprintln!( - "Usage: {} storage_path listening_addr rest_svc_addr network bitcoind_rpc_addr bitcoind_rpc_user bitcoind_rpc_password", - args[0] - ); + if args.len() < 2 { + eprintln!("Usage: {} config_path", args[0]); std::process::exit(-1); } - let mut config = Config::default(); - config.storage_dir_path = args[1].clone(); - config.log_level = LogLevel::Trace; + let mut ldk_node_config = Config::default(); + let config_file = load_config(Path::new(&args[1])).expect("Invalid configuration file."); - config.listening_addresses = match SocketAddress::from_str(&args[2]) { - Ok(addr) => Some(vec![addr]), - Err(_) => { - eprintln!("Failed to parse listening_addr: {}", args[2]); - std::process::exit(-1); - }, - }; - - let rest_svc_addr = match SocketAddr::from_str(&args[3]) { - Ok(addr) => addr, - Err(_) => { - eprintln!("Failed to parse rest_svc_addr: {}", args[3]); - std::process::exit(-1); - }, - }; + ldk_node_config.log_level = LogLevel::Trace; + ldk_node_config.storage_dir_path = config_file.storage_dir_path; + ldk_node_config.listening_addresses = Some(vec![config_file.listening_addr]); + ldk_node_config.network = config_file.network; - config.network = match Network::from_str(&args[4]) { - Ok(network) => network, - Err(_) => { - eprintln!("Unsupported network: {}. Use 'bitcoin', 'testnet', 'regtest', 'signet', 'regtest'.", args[4]); - std::process::exit(-1); - }, - }; - - let mut builder = Builder::from_config(config); + let mut builder = Builder::from_config(ldk_node_config); - let bitcoind_rpc_addr = match SocketAddr::from_str(&args[5]) { - Ok(addr) => addr, - Err(_) => { - eprintln!("Failed to parse bitcoind_rpc_addr: {}", args[3]); - std::process::exit(-1); - }, - }; + let bitcoind_rpc_addr = config_file.bitcoind_rpc_addr; builder.set_chain_source_bitcoind_rpc( bitcoind_rpc_addr.ip().to_string(), bitcoind_rpc_addr.port(), - args[6].clone(), - args[7].clone(), + config_file.bitcoind_rpc_user, + config_file.bitcoind_rpc_password, ); let runtime = match tokio::runtime::Builder::new_multi_thread().enable_all().build() { @@ -116,8 +85,9 @@ fn main() { }, }; let event_node = Arc::clone(&node); - let rest_svc_listener = - TcpListener::bind(rest_svc_addr).await.expect("Failed to bind listening port"); + let rest_svc_listener = TcpListener::bind(config_file.rest_service_addr) + .await + .expect("Failed to bind listening port"); loop { tokio::select! { event = event_node.next_event_async() => { diff --git a/ldk-server/src/util/config.rs b/ldk-server/src/util/config.rs new file mode 100644 index 0000000..5c6e7b3 --- /dev/null +++ b/ldk-server/src/util/config.rs @@ -0,0 +1,136 @@ +use ldk_node::bitcoin::Network; +use ldk_node::lightning::ln::msgs::SocketAddress; +use serde::{Deserialize, Serialize}; +use std::net::SocketAddr; +use std::path::Path; +use std::str::FromStr; +use std::{fs, io}; + +/// Configuration for LDK Server. +#[derive(PartialEq, Eq, Debug)] +pub struct Config { + pub listening_addr: SocketAddress, + pub network: Network, + pub rest_service_addr: SocketAddr, + pub storage_dir_path: String, + pub bitcoind_rpc_addr: SocketAddr, + pub bitcoind_rpc_user: String, + pub bitcoind_rpc_password: String, +} + +impl TryFrom for Config { + type Error = io::Error; + + fn try_from(json_config: JsonConfig) -> io::Result { + let listening_addr = + SocketAddress::from_str(&json_config.listening_address).map_err(|e| { + io::Error::new( + io::ErrorKind::InvalidInput, + format!("Invalid listening address configured: {}", e), + ) + })?; + let rest_service_addr = + SocketAddr::from_str(&json_config.rest_service_address).map_err(|e| { + io::Error::new( + io::ErrorKind::InvalidInput, + format!("Invalid rest service address configured: {}", e), + ) + })?; + + let bitcoind_rpc_addr = + SocketAddr::from_str(&json_config.bitcoind_rpc_address).map_err(|e| { + io::Error::new( + io::ErrorKind::InvalidInput, + format!("Invalid bitcoind RPC address configured: {}", e), + ) + })?; + + Ok(Config { + listening_addr, + network: json_config.network, + rest_service_addr, + storage_dir_path: json_config.storage_dir_path, + bitcoind_rpc_addr, + bitcoind_rpc_user: json_config.bitcoind_rpc_user, + bitcoind_rpc_password: json_config.bitcoind_rpc_password, + }) + } +} + +/// Configuration loaded from a JSON file. +#[derive(Deserialize, Serialize)] +pub struct JsonConfig { + listening_address: String, + network: Network, + rest_service_address: String, + storage_dir_path: String, + bitcoind_rpc_address: String, + bitcoind_rpc_user: String, + bitcoind_rpc_password: String, +} + +/// Loads the configuration from a JSON file at the given path. +pub fn load_config>(config_path: P) -> io::Result { + let file_contents = fs::read_to_string(config_path.as_ref()).map_err(|e| { + io::Error::new( + e.kind(), + format!("Failed to read config file '{}': {}", config_path.as_ref().display(), e), + ) + })?; + + let json_string = remove_json_comments(file_contents.as_str()); + let json_config: JsonConfig = serde_json::from_str(&json_string).map_err(|e| { + io::Error::new( + io::ErrorKind::InvalidData, + format!("Config file contains invalid JSON format: {}", e), + ) + })?; + Ok(Config::try_from(json_config)?) +} + +fn remove_json_comments(s: &str) -> String { + s.lines() + .map(|line| if let Some(pos) = line.find("//") { &line[..pos] } else { line }) + .collect::>() + .join("\n") +} + +#[cfg(test)] +mod tests { + use super::*; + use ldk_node::{bitcoin::Network, lightning::ln::msgs::SocketAddress}; + use std::str::FromStr; + + #[test] + fn test_read_json_config_from_file() { + let storage_path = std::env::temp_dir(); + let config_file_name = "config.json"; + + let json_config = r#"{ + "listening_address": "localhost:3001", + "network": "regtest", + "rest_service_address": "127.0.0.1:3002", + "storage_dir_path": "/tmp", + "bitcoind_rpc_address":"127.0.0.1:8332", // comment-1 + "bitcoind_rpc_user": "bitcoind-testuser", + "bitcoind_rpc_password": "bitcoind-testpassword", + "unknown_key": "random-value" + // comment-2 + }"#; + + fs::write(storage_path.join(config_file_name), json_config).unwrap(); + + assert_eq!( + load_config(storage_path.join(config_file_name)).unwrap(), + Config { + listening_addr: SocketAddress::from_str("localhost:3001").unwrap(), + network: Network::Regtest, + rest_service_addr: SocketAddr::from_str("127.0.0.1:3002").unwrap(), + storage_dir_path: "/tmp".to_string(), + bitcoind_rpc_addr: SocketAddr::from_str("127.0.0.1:8332").unwrap(), + bitcoind_rpc_user: "bitcoind-testuser".to_string(), + bitcoind_rpc_password: "bitcoind-testpassword".to_string(), + } + ) + } +} diff --git a/ldk-server/src/util/mod.rs b/ldk-server/src/util/mod.rs index 8f7d1a3..02789f0 100644 --- a/ldk-server/src/util/mod.rs +++ b/ldk-server/src/util/mod.rs @@ -1 +1,2 @@ +pub(crate) mod config; pub(crate) mod proto_adapter;