Skip to content

Commit ecfe4cc

Browse files
authored
Merge pull request #28 from G8XSU/file-config
Add file based Config support.
2 parents eed8ba5 + e14923c commit ecfe4cc

File tree

4 files changed

+176
-47
lines changed

4 files changed

+176
-47
lines changed

ldk-server/ldk-server.config

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
// The addresses on which the lightning node will listen for incoming connections.
3+
"listening_address": "localhost:3001",
4+
5+
// The Bitcoin network to use.
6+
"network": "regtest",
7+
8+
// The address on which LDK Server will accept incoming requests.
9+
"rest_service_address": "127.0.0.1:3002",
10+
11+
// The path where the underlying LDK and BDK persist their data.
12+
"storage_dir_path": "/tmp",
13+
14+
// Bitcoin Core's RPC endpoint.
15+
"bitcoind_rpc_address": "127.0.0.1:8332",
16+
17+
// Bitcoin Core's RPC user.
18+
"bitcoind_rpc_user": "bitcoind-testuser",
19+
20+
// Bitcoin Core's RPC password.
21+
"bitcoind_rpc_password": "bitcoind-testpassword"
22+
}

ldk-server/src/main.rs

Lines changed: 17 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ mod util;
55

66
use crate::service::NodeService;
77

8-
use ldk_node::bitcoin::Network;
9-
use ldk_node::lightning::ln::msgs::SocketAddress;
108
use ldk_node::{Builder, Event, LogLevel};
119

1210
use tokio::net::TcpListener;
@@ -15,65 +13,36 @@ use tokio::signal::unix::SignalKind;
1513
use hyper::server::conn::http1;
1614
use hyper_util::rt::TokioIo;
1715

16+
use crate::util::config::load_config;
1817
use ldk_node::config::Config;
19-
use std::net::SocketAddr;
20-
use std::str::FromStr;
18+
use std::path::Path;
2119
use std::sync::Arc;
2220

2321
fn main() {
2422
let args: Vec<String> = std::env::args().collect();
2523

26-
if args.len() < 8 {
27-
eprintln!(
28-
"Usage: {} storage_path listening_addr rest_svc_addr network bitcoind_rpc_addr bitcoind_rpc_user bitcoind_rpc_password",
29-
args[0]
30-
);
24+
if args.len() < 2 {
25+
eprintln!("Usage: {} config_path", args[0]);
3126
std::process::exit(-1);
3227
}
3328

34-
let mut config = Config::default();
35-
config.storage_dir_path = args[1].clone();
36-
config.log_level = LogLevel::Trace;
29+
let mut ldk_node_config = Config::default();
30+
let config_file = load_config(Path::new(&args[1])).expect("Invalid configuration file.");
3731

38-
config.listening_addresses = match SocketAddress::from_str(&args[2]) {
39-
Ok(addr) => Some(vec![addr]),
40-
Err(_) => {
41-
eprintln!("Failed to parse listening_addr: {}", args[2]);
42-
std::process::exit(-1);
43-
},
44-
};
45-
46-
let rest_svc_addr = match SocketAddr::from_str(&args[3]) {
47-
Ok(addr) => addr,
48-
Err(_) => {
49-
eprintln!("Failed to parse rest_svc_addr: {}", args[3]);
50-
std::process::exit(-1);
51-
},
52-
};
32+
ldk_node_config.log_level = LogLevel::Trace;
33+
ldk_node_config.storage_dir_path = config_file.storage_dir_path;
34+
ldk_node_config.listening_addresses = Some(vec![config_file.listening_addr]);
35+
ldk_node_config.network = config_file.network;
5336

54-
config.network = match Network::from_str(&args[4]) {
55-
Ok(network) => network,
56-
Err(_) => {
57-
eprintln!("Unsupported network: {}. Use 'bitcoin', 'testnet', 'regtest', 'signet', 'regtest'.", args[4]);
58-
std::process::exit(-1);
59-
},
60-
};
61-
62-
let mut builder = Builder::from_config(config);
37+
let mut builder = Builder::from_config(ldk_node_config);
6338

64-
let bitcoind_rpc_addr = match SocketAddr::from_str(&args[5]) {
65-
Ok(addr) => addr,
66-
Err(_) => {
67-
eprintln!("Failed to parse bitcoind_rpc_addr: {}", args[3]);
68-
std::process::exit(-1);
69-
},
70-
};
39+
let bitcoind_rpc_addr = config_file.bitcoind_rpc_addr;
7140

7241
builder.set_chain_source_bitcoind_rpc(
7342
bitcoind_rpc_addr.ip().to_string(),
7443
bitcoind_rpc_addr.port(),
75-
args[6].clone(),
76-
args[7].clone(),
44+
config_file.bitcoind_rpc_user,
45+
config_file.bitcoind_rpc_password,
7746
);
7847

7948
let runtime = match tokio::runtime::Builder::new_multi_thread().enable_all().build() {
@@ -116,8 +85,9 @@ fn main() {
11685
},
11786
};
11887
let event_node = Arc::clone(&node);
119-
let rest_svc_listener =
120-
TcpListener::bind(rest_svc_addr).await.expect("Failed to bind listening port");
88+
let rest_svc_listener = TcpListener::bind(config_file.rest_service_addr)
89+
.await
90+
.expect("Failed to bind listening port");
12191
loop {
12292
tokio::select! {
12393
event = event_node.next_event_async() => {

ldk-server/src/util/config.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
use ldk_node::bitcoin::Network;
2+
use ldk_node::lightning::ln::msgs::SocketAddress;
3+
use serde::{Deserialize, Serialize};
4+
use std::net::SocketAddr;
5+
use std::path::Path;
6+
use std::str::FromStr;
7+
use std::{fs, io};
8+
9+
/// Configuration for LDK Server.
10+
#[derive(PartialEq, Eq, Debug)]
11+
pub struct Config {
12+
pub listening_addr: SocketAddress,
13+
pub network: Network,
14+
pub rest_service_addr: SocketAddr,
15+
pub storage_dir_path: String,
16+
pub bitcoind_rpc_addr: SocketAddr,
17+
pub bitcoind_rpc_user: String,
18+
pub bitcoind_rpc_password: String,
19+
}
20+
21+
impl TryFrom<JsonConfig> for Config {
22+
type Error = io::Error;
23+
24+
fn try_from(json_config: JsonConfig) -> io::Result<Self> {
25+
let listening_addr =
26+
SocketAddress::from_str(&json_config.listening_address).map_err(|e| {
27+
io::Error::new(
28+
io::ErrorKind::InvalidInput,
29+
format!("Invalid listening address configured: {}", e),
30+
)
31+
})?;
32+
let rest_service_addr =
33+
SocketAddr::from_str(&json_config.rest_service_address).map_err(|e| {
34+
io::Error::new(
35+
io::ErrorKind::InvalidInput,
36+
format!("Invalid rest service address configured: {}", e),
37+
)
38+
})?;
39+
40+
let bitcoind_rpc_addr =
41+
SocketAddr::from_str(&json_config.bitcoind_rpc_address).map_err(|e| {
42+
io::Error::new(
43+
io::ErrorKind::InvalidInput,
44+
format!("Invalid bitcoind RPC address configured: {}", e),
45+
)
46+
})?;
47+
48+
Ok(Config {
49+
listening_addr,
50+
network: json_config.network,
51+
rest_service_addr,
52+
storage_dir_path: json_config.storage_dir_path,
53+
bitcoind_rpc_addr,
54+
bitcoind_rpc_user: json_config.bitcoind_rpc_user,
55+
bitcoind_rpc_password: json_config.bitcoind_rpc_password,
56+
})
57+
}
58+
}
59+
60+
/// Configuration loaded from a JSON file.
61+
#[derive(Deserialize, Serialize)]
62+
pub struct JsonConfig {
63+
listening_address: String,
64+
network: Network,
65+
rest_service_address: String,
66+
storage_dir_path: String,
67+
bitcoind_rpc_address: String,
68+
bitcoind_rpc_user: String,
69+
bitcoind_rpc_password: String,
70+
}
71+
72+
/// Loads the configuration from a JSON file at the given path.
73+
pub fn load_config<P: AsRef<Path>>(config_path: P) -> io::Result<Config> {
74+
let file_contents = fs::read_to_string(config_path.as_ref()).map_err(|e| {
75+
io::Error::new(
76+
e.kind(),
77+
format!("Failed to read config file '{}': {}", config_path.as_ref().display(), e),
78+
)
79+
})?;
80+
81+
let json_string = remove_json_comments(file_contents.as_str());
82+
let json_config: JsonConfig = serde_json::from_str(&json_string).map_err(|e| {
83+
io::Error::new(
84+
io::ErrorKind::InvalidData,
85+
format!("Config file contains invalid JSON format: {}", e),
86+
)
87+
})?;
88+
Ok(Config::try_from(json_config)?)
89+
}
90+
91+
fn remove_json_comments(s: &str) -> String {
92+
s.lines()
93+
.map(|line| if let Some(pos) = line.find("//") { &line[..pos] } else { line })
94+
.collect::<Vec<&str>>()
95+
.join("\n")
96+
}
97+
98+
#[cfg(test)]
99+
mod tests {
100+
use super::*;
101+
use ldk_node::{bitcoin::Network, lightning::ln::msgs::SocketAddress};
102+
use std::str::FromStr;
103+
104+
#[test]
105+
fn test_read_json_config_from_file() {
106+
let storage_path = std::env::temp_dir();
107+
let config_file_name = "config.json";
108+
109+
let json_config = r#"{
110+
"listening_address": "localhost:3001",
111+
"network": "regtest",
112+
"rest_service_address": "127.0.0.1:3002",
113+
"storage_dir_path": "/tmp",
114+
"bitcoind_rpc_address":"127.0.0.1:8332", // comment-1
115+
"bitcoind_rpc_user": "bitcoind-testuser",
116+
"bitcoind_rpc_password": "bitcoind-testpassword",
117+
"unknown_key": "random-value"
118+
// comment-2
119+
}"#;
120+
121+
fs::write(storage_path.join(config_file_name), json_config).unwrap();
122+
123+
assert_eq!(
124+
load_config(storage_path.join(config_file_name)).unwrap(),
125+
Config {
126+
listening_addr: SocketAddress::from_str("localhost:3001").unwrap(),
127+
network: Network::Regtest,
128+
rest_service_addr: SocketAddr::from_str("127.0.0.1:3002").unwrap(),
129+
storage_dir_path: "/tmp".to_string(),
130+
bitcoind_rpc_addr: SocketAddr::from_str("127.0.0.1:8332").unwrap(),
131+
bitcoind_rpc_user: "bitcoind-testuser".to_string(),
132+
bitcoind_rpc_password: "bitcoind-testpassword".to_string(),
133+
}
134+
)
135+
}
136+
}

ldk-server/src/util/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
pub(crate) mod config;
12
pub(crate) mod proto_adapter;

0 commit comments

Comments
 (0)