Skip to content

Commit 96de892

Browse files
authored
Merge pull request #62 from G8XSU/25-3-20-toml
Add TOML-based configuration support.
2 parents a4ade5c + cf9c94b commit 96de892

File tree

5 files changed

+165
-67
lines changed

5 files changed

+165
-67
lines changed

Cargo.lock

Lines changed: 56 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ We welcome your feedback and contributions to help shape the future of LDK Serve
3636

3737

3838
### Configuration
39-
Refer `./ldk-server/ldk-server.config` to see available configuration options.
39+
Refer `./ldk-server/ldk-server-config.toml` to see available configuration options.
4040

4141
### Building
4242
```

ldk-server/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ edition = "2021"
66
[dependencies]
77
ldk-node = { git = "https://github.com/lightningdevkit/ldk-node.git", rev = "f0338d19256615088fabab2b6927d478ae3ec1a1" }
88
serde = { version = "1.0.203", default-features = false, features = ["derive"] }
9-
serde_json = { version = "1.0.118", default-features = false }
109
hyper = { version = "1", default-features = false, features = ["server", "http1"] }
1110
http-body-util = { version = "0.1", default-features = false }
1211
hyper-util = { version = "0.1", default-features = false, features = ["server-graceful"] }
@@ -18,6 +17,7 @@ hex = { package = "hex-conservative", version = "0.2.1", default-features = fals
1817
rusqlite = { version = "0.31.0", features = ["bundled"] }
1918
rand = { version = "0.8.5", default-features = false }
2019
async-trait = { version = "0.1.85", default-features = false }
20+
toml = { version = "0.8.9", default-features = false, features = ["parse"] }
2121

2222
# Required for RabittMQ based EventPublisher. Only enabled for `events-rabbitmq` feature.
2323
lapin = { version = "2.4.0", features = ["rustls"], default-features = false, optional = true }

ldk-server/ldk-server-config.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Lightning node settings
2+
[node]
3+
network = "regtest" # Bitcoin network to use
4+
listening_address = "localhost:3001" # Lightning node listening address
5+
rest_service_address = "127.0.0.1:3002" # LDK Server REST address
6+
7+
# Storage settings
8+
[storage.disk]
9+
dir_path = "/tmp/ldk-server/" # Path for LDK and BDK data persistence
10+
11+
# Bitcoin Core settings
12+
[bitcoind]
13+
rpc_address = "127.0.0.1:18444" # RPC endpoint
14+
rpc_user = "polaruser" # RPC username
15+
rpc_password = "polarpass" # RPC password
16+
17+
# RabbitMQ settings (only required if using events-rabbitmq feature)
18+
[rabbitmq]
19+
connection_string = "" # RabbitMQ connection string
20+
exchange_name = "" # RabbitMQ exchange name

ldk-server/src/util/config.rs

Lines changed: 87 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -20,72 +20,101 @@ pub struct Config {
2020
pub rabbitmq_exchange_name: String,
2121
}
2222

23-
impl TryFrom<JsonConfig> for Config {
23+
impl TryFrom<TomlConfig> for Config {
2424
type Error = io::Error;
2525

26-
fn try_from(json_config: JsonConfig) -> io::Result<Self> {
26+
fn try_from(toml_config: TomlConfig) -> io::Result<Self> {
2727
let listening_addr =
28-
SocketAddress::from_str(&json_config.listening_address).map_err(|e| {
28+
SocketAddress::from_str(&toml_config.node.listening_address).map_err(|e| {
2929
io::Error::new(
3030
io::ErrorKind::InvalidInput,
3131
format!("Invalid listening address configured: {}", e),
3232
)
3333
})?;
34-
let rest_service_addr =
35-
SocketAddr::from_str(&json_config.rest_service_address).map_err(|e| {
34+
let rest_service_addr = SocketAddr::from_str(&toml_config.node.rest_service_address)
35+
.map_err(|e| {
3636
io::Error::new(
3737
io::ErrorKind::InvalidInput,
3838
format!("Invalid rest service address configured: {}", e),
3939
)
4040
})?;
41-
4241
let bitcoind_rpc_addr =
43-
SocketAddr::from_str(&json_config.bitcoind_rpc_address).map_err(|e| {
42+
SocketAddr::from_str(&toml_config.bitcoind.rpc_address).map_err(|e| {
4443
io::Error::new(
4544
io::ErrorKind::InvalidInput,
4645
format!("Invalid bitcoind RPC address configured: {}", e),
4746
)
4847
})?;
4948

50-
#[cfg(feature = "events-rabbitmq")]
51-
if json_config.rabbitmq_connection_string.as_deref().map_or(true, |s| s.is_empty())
52-
|| json_config.rabbitmq_exchange_name.as_deref().map_or(true, |s| s.is_empty())
53-
{
54-
return Err(io::Error::new(
55-
io::ErrorKind::InvalidInput,
56-
"Both `rabbitmq_connection_string` and `rabbitmq_exchange_name` must be configured if enabling `events-rabbitmq` feature.".to_string(),
57-
));
58-
}
49+
let (rabbitmq_connection_string, rabbitmq_exchange_name) = {
50+
let rabbitmq = toml_config.rabbitmq.unwrap_or(RabbitmqConfig {
51+
connection_string: String::new(),
52+
exchange_name: String::new(),
53+
});
54+
#[cfg(feature = "events-rabbitmq")]
55+
if rabbitmq.connection_string.is_empty() || rabbitmq.exchange_name.is_empty() {
56+
return Err(io::Error::new(
57+
io::ErrorKind::InvalidInput,
58+
"Both `rabbitmq.connection_string` and `rabbitmq.exchange_name` must be configured if enabling `events-rabbitmq` feature.".to_string(),
59+
));
60+
}
61+
(rabbitmq.connection_string, rabbitmq.exchange_name)
62+
};
5963

6064
Ok(Config {
6165
listening_addr,
62-
network: json_config.network,
66+
network: toml_config.node.network,
6367
rest_service_addr,
64-
storage_dir_path: json_config.storage_dir_path,
68+
storage_dir_path: toml_config.storage.disk.dir_path,
6569
bitcoind_rpc_addr,
66-
bitcoind_rpc_user: json_config.bitcoind_rpc_user,
67-
bitcoind_rpc_password: json_config.bitcoind_rpc_password,
68-
rabbitmq_connection_string: json_config.rabbitmq_connection_string.unwrap_or_default(),
69-
rabbitmq_exchange_name: json_config.rabbitmq_exchange_name.unwrap_or_default(),
70+
bitcoind_rpc_user: toml_config.bitcoind.rpc_user,
71+
bitcoind_rpc_password: toml_config.bitcoind.rpc_password,
72+
rabbitmq_connection_string,
73+
rabbitmq_exchange_name,
7074
})
7175
}
7276
}
7377

74-
/// Configuration loaded from a JSON file.
78+
/// Configuration loaded from a TOML file.
7579
#[derive(Deserialize, Serialize)]
76-
pub struct JsonConfig {
77-
listening_address: String,
80+
pub struct TomlConfig {
81+
node: NodeConfig,
82+
storage: StorageConfig,
83+
bitcoind: BitcoindConfig,
84+
rabbitmq: Option<RabbitmqConfig>,
85+
}
86+
87+
#[derive(Deserialize, Serialize)]
88+
struct NodeConfig {
7889
network: Network,
90+
listening_address: String,
7991
rest_service_address: String,
80-
storage_dir_path: String,
81-
bitcoind_rpc_address: String,
82-
bitcoind_rpc_user: String,
83-
bitcoind_rpc_password: String,
84-
rabbitmq_connection_string: Option<String>,
85-
rabbitmq_exchange_name: Option<String>,
8692
}
8793

88-
/// Loads the configuration from a JSON file at the given path.
94+
#[derive(Deserialize, Serialize)]
95+
struct StorageConfig {
96+
disk: DiskConfig,
97+
}
98+
99+
#[derive(Deserialize, Serialize)]
100+
struct DiskConfig {
101+
dir_path: String,
102+
}
103+
104+
#[derive(Deserialize, Serialize)]
105+
struct BitcoindConfig {
106+
rpc_address: String,
107+
rpc_user: String,
108+
rpc_password: String,
109+
}
110+
111+
#[derive(Deserialize, Serialize)]
112+
struct RabbitmqConfig {
113+
connection_string: String,
114+
exchange_name: String,
115+
}
116+
117+
/// Loads the configuration from a TOML file at the given path.
89118
pub fn load_config<P: AsRef<Path>>(config_path: P) -> io::Result<Config> {
90119
let file_contents = fs::read_to_string(config_path.as_ref()).map_err(|e| {
91120
io::Error::new(
@@ -94,21 +123,13 @@ pub fn load_config<P: AsRef<Path>>(config_path: P) -> io::Result<Config> {
94123
)
95124
})?;
96125

97-
let json_string = remove_json_comments(file_contents.as_str());
98-
let json_config: JsonConfig = serde_json::from_str(&json_string).map_err(|e| {
126+
let toml_config: TomlConfig = toml::from_str(&file_contents).map_err(|e| {
99127
io::Error::new(
100128
io::ErrorKind::InvalidData,
101-
format!("Config file contains invalid JSON format: {}", e),
129+
format!("Config file contains invalid TOML format: {}", e),
102130
)
103131
})?;
104-
Ok(Config::try_from(json_config)?)
105-
}
106-
107-
fn remove_json_comments(s: &str) -> String {
108-
s.lines()
109-
.map(|line| if let Some(pos) = line.find("//") { &line[..pos] } else { line })
110-
.collect::<Vec<&str>>()
111-
.join("\n")
132+
Ok(Config::try_from(toml_config)?)
112133
}
113134

114135
#[cfg(test)]
@@ -118,25 +139,30 @@ mod tests {
118139
use std::str::FromStr;
119140

120141
#[test]
121-
fn test_read_json_config_from_file() {
142+
fn test_read_toml_config_from_file() {
122143
let storage_path = std::env::temp_dir();
123-
let config_file_name = "config.json";
124-
125-
let json_config = r#"{
126-
"listening_address": "localhost:3001",
127-
"network": "regtest",
128-
"rest_service_address": "127.0.0.1:3002",
129-
"storage_dir_path": "/tmp",
130-
"bitcoind_rpc_address":"127.0.0.1:8332", // comment-1
131-
"bitcoind_rpc_user": "bitcoind-testuser",
132-
"bitcoind_rpc_password": "bitcoind-testpassword",
133-
"rabbitmq_connection_string": "rabbitmq_connection_string",
134-
"rabbitmq_exchange_name": "rabbitmq_exchange_name",
135-
"unknown_key": "random-value"
136-
// comment-2
137-
}"#;
138-
139-
fs::write(storage_path.join(config_file_name), json_config).unwrap();
144+
let config_file_name = "config.toml";
145+
146+
let toml_config = r#"
147+
[node]
148+
network = "regtest"
149+
listening_address = "localhost:3001"
150+
rest_service_address = "127.0.0.1:3002"
151+
152+
[storage.disk]
153+
dir_path = "/tmp"
154+
155+
[bitcoind]
156+
rpc_address = "127.0.0.1:8332" # RPC endpoint
157+
rpc_user = "bitcoind-testuser"
158+
rpc_password = "bitcoind-testpassword"
159+
160+
[rabbitmq]
161+
connection_string = "rabbitmq_connection_string"
162+
exchange_name = "rabbitmq_exchange_name"
163+
"#;
164+
165+
fs::write(storage_path.join(config_file_name), toml_config).unwrap();
140166

141167
assert_eq!(
142168
load_config(storage_path.join(config_file_name)).unwrap(),

0 commit comments

Comments
 (0)