From 50d7854ab072083e7f22c4eefe335d4dd5f98e4c Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Wed, 25 Mar 2026 12:32:30 +0100 Subject: [PATCH 1/2] feat(server): default the REST service address Default `rest_service_address` to `127.0.0.1:3536` when omitted from the server config. Closes: https://github.com/lightningdevkit/ldk-server/issues/160 Co-Authored-By: Claude Opus 4.6 (1M context) --- ldk-server/ldk-server-config.toml | 2 +- ldk-server/src/util/config.rs | 37 ++++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/ldk-server/ldk-server-config.toml b/ldk-server/ldk-server-config.toml index 87f8413e..48b6c7b1 100644 --- a/ldk-server/ldk-server-config.toml +++ b/ldk-server/ldk-server-config.toml @@ -3,7 +3,7 @@ network = "regtest" # Bitcoin network to use listening_addresses = ["localhost:3001"] # Lightning node listening addresses announcement_addresses = ["54.3.7.81:3001"] # Lightning node announcement addresses -rest_service_address = "127.0.0.1:3002" # LDK Server REST address +#rest_service_address = "127.0.0.1:3536" # LDK Server REST address (optional, defaults to 127.0.0.1:3536) alias = "ldk_server" # Lightning node alias #pathfinding_scores_source_url = "" # External Pathfinding Scores Source #rgs_server_url = "https://rapidsync.lightningdevkit.org/snapshot/v2/" # Optional: RGS URL for rapid gossip sync diff --git a/ldk-server/src/util/config.rs b/ldk-server/src/util/config.rs index c950987c..2a6976d3 100644 --- a/ldk-server/src/util/config.rs +++ b/ldk-server/src/util/config.rs @@ -21,6 +21,8 @@ use ldk_node::liquidity::LSPS2ServiceConfig; use log::LevelFilter; use serde::{Deserialize, Serialize}; +const DEFAULT_REST_SERVICE_ADDRESS: &str = "127.0.0.1:3536"; + #[cfg(not(test))] const DEFAULT_CONFIG_FILE: &str = "config.toml"; @@ -211,7 +213,7 @@ impl ConfigBuilder { let rest_service_addr = self .rest_service_address - .ok_or_else(|| missing_field_err("rest_service_address"))? + .unwrap_or_else(|| DEFAULT_REST_SERVICE_ADDRESS.to_string()) .parse::() .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; @@ -1109,7 +1111,6 @@ mod tests { validate_missing!("rpc_password", missing_field_msg("bitcoind_rpc_password")); validate_missing!("rpc_user", missing_field_msg("bitcoind_rpc_user")); validate_missing!("rpc_address", missing_field_msg("bitcoind_rpc_address")); - validate_missing!("rest_service_address =", missing_field_msg("rest_service_address")); validate_missing!("network =", missing_field_msg("network")); } @@ -1196,7 +1197,6 @@ mod tests { validate_missing!(bitcoind_rpc_user, missing_field_msg("bitcoind_rpc_user")); validate_missing!(bitcoind_rpc_address, missing_field_msg("bitcoind_rpc_address")); validate_missing!(node_network, missing_field_msg("network")); - validate_missing!(node_rest_service_address, missing_field_msg("rest_service_address")); } #[test] @@ -1309,4 +1309,35 @@ mod tests { let err = result.unwrap_err(); assert_eq!(err.kind(), io::ErrorKind::InvalidInput); } + + #[test] + #[cfg(not(feature = "experimental-lsps2-support"))] + #[cfg(not(feature = "events-rabbitmq"))] + fn test_default_rest_service_address() { + let storage_path = std::env::temp_dir(); + let config_file_name = "test_default_rest_service_address.toml"; + + // Config without rest_service_address + let toml_config = r#" + [node] + network = "regtest" + + [bitcoind] + rpc_address = "127.0.0.1:8332" + rpc_user = "bitcoind-testuser" + rpc_password = "bitcoind-testpassword" + "#; + + fs::write(storage_path.join(config_file_name), toml_config).unwrap(); + + let mut args_config = empty_args_config(); + args_config.config_file = + Some(storage_path.join(config_file_name).to_string_lossy().to_string()); + + let config = load_config(&args_config).unwrap(); + assert_eq!( + config.rest_service_addr, + SocketAddr::from_str(DEFAULT_REST_SERVICE_ADDRESS).unwrap() + ); + } } From caf35e1932dac59e9a9d8ab3e8fcd599829a0f11 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Wed, 25 Mar 2026 18:47:25 +0100 Subject: [PATCH 2/2] feat(cli): default the base URL to the REST service address Use the same default REST address in the CLI when config omits `node.rest_service_address` and when `--base-url` is not provided. --- ldk-server-cli/src/config.rs | 52 ++++++++++++++++++++++++++++++++++++ ldk-server-cli/src/main.rs | 18 +++++++------ 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/ldk-server-cli/src/config.rs b/ldk-server-cli/src/config.rs index d2249749..8cf0276b 100644 --- a/ldk-server-cli/src/config.rs +++ b/ldk-server-cli/src/config.rs @@ -14,6 +14,7 @@ use serde::{Deserialize, Serialize}; const DEFAULT_CONFIG_FILE: &str = "config.toml"; const DEFAULT_CERT_FILE: &str = "tls.crt"; const API_KEY_FILE: &str = "api_key"; +pub const DEFAULT_REST_SERVICE_ADDRESS: &str = "127.0.0.1:3536"; pub fn get_default_data_dir() -> Option { #[cfg(target_os = "macos")] @@ -66,6 +67,7 @@ pub struct TlsConfig { #[derive(Debug, Deserialize)] pub struct NodeConfig { + #[serde(default = "default_rest_service_address")] pub rest_service_address: String, network: String, } @@ -99,3 +101,53 @@ pub fn load_config(path: &PathBuf) -> Result { toml::from_str(&contents) .map_err(|e| format!("Failed to parse config file '{}': {}", path.display(), e)) } + +pub fn resolve_base_url(cli_base_url: Option, config: Option<&Config>) -> String { + cli_base_url + .or_else(|| config.map(|config| config.node.rest_service_address.clone())) + .unwrap_or_else(default_rest_service_address) +} + +fn default_rest_service_address() -> String { + DEFAULT_REST_SERVICE_ADDRESS.to_string() +} + +#[cfg(test)] +mod tests { + use super::{resolve_base_url, Config, DEFAULT_REST_SERVICE_ADDRESS}; + + #[test] + fn config_defaults_rest_service_address() { + let config: Config = toml::from_str( + r#" + [node] + network = "regtest" + "#, + ) + .unwrap(); + + assert_eq!(config.node.rest_service_address, DEFAULT_REST_SERVICE_ADDRESS); + } + + #[test] + fn resolve_base_url_uses_cli_arg_first() { + let config: Config = toml::from_str( + r#" + [node] + network = "regtest" + rest_service_address = "127.0.0.1:3002" + "#, + ) + .unwrap(); + + assert_eq!( + resolve_base_url(Some("127.0.0.1:4000".to_string()), Some(&config)), + "127.0.0.1:4000" + ); + } + + #[test] + fn resolve_base_url_falls_back_to_default() { + assert_eq!(resolve_base_url(None, None), DEFAULT_REST_SERVICE_ADDRESS); + } +} diff --git a/ldk-server-cli/src/main.rs b/ldk-server-cli/src/main.rs index 2c133a5e..89623d44 100644 --- a/ldk-server-cli/src/main.rs +++ b/ldk-server-cli/src/main.rs @@ -13,7 +13,8 @@ use clap::{CommandFactory, Parser, Subcommand}; use clap_complete::{generate, Shell}; use config::{ api_key_path_for_storage_dir, cert_path_for_storage_dir, get_default_api_key_path, - get_default_cert_path, get_default_config_path, load_config, + get_default_cert_path, get_default_config_path, load_config, resolve_base_url, + DEFAULT_REST_SERVICE_ADDRESS, }; use hex_conservative::DisplayHex; use ldk_server_client::client::LdkServerClient; @@ -79,7 +80,13 @@ const DEFAULT_DIR: &str = if cfg!(target_os = "macos") { override_usage = "ldk-server-cli [OPTIONS] " )] struct Cli { - #[arg(short, long, help = "Base URL of the server. If not provided, reads from config file")] + #[arg( + short, + long, + help = format!( + "Base URL of the server. Defaults to config file or {DEFAULT_REST_SERVICE_ADDRESS}" + ) + )] base_url: Option, #[arg(short, long, help = format!("API key for authentication. Defaults by reading {DEFAULT_DIR}/[network]/api_key"))] @@ -570,12 +577,7 @@ async fn main() { }); // Get base URL from argument then from config file - let base_url = - cli.base_url.or_else(|| config.as_ref().map(|c| c.node.rest_service_address.clone())) - .unwrap_or_else(|| { - eprintln!("Base URL not provided. Use --base-url or ensure config file exists at {DEFAULT_DIR}/config.toml"); - std::process::exit(1); - }); + let base_url = resolve_base_url(cli.base_url, config.as_ref()); // Get TLS cert path from argument, then from config tls.cert_path, then from storage dir, // then try default location.