Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions e2e-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ impl LdkServerHandle {
let p2p_port = find_available_port();

let (rpc_host, rpc_port_num, rpc_user, rpc_password) = bitcoind.rpc_details();
let rpc_address = format!("{rpc_host}:{rpc_port_num}");

let exchange_name = format!("e2e_test_exchange_{rest_port}");

Expand All @@ -122,7 +121,8 @@ alias = "e2e-test-node"
dir_path = "{storage_dir}"

[bitcoind]
rpc_address = "{rpc_address}"
rpc_host = "{rpc_host}"
rpc_port = {rpc_port_num}
rpc_user = "{rpc_user}"
rpc_password = "{rpc_password}"

Expand Down
9 changes: 2 additions & 7 deletions ldk-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,8 @@ fn main() {
}

match config_file.chain_source {
ChainSource::Rpc { rpc_address, rpc_user, rpc_password } => {
builder.set_chain_source_bitcoind_rpc(
rpc_address.ip().to_string(),
rpc_address.port(),
rpc_user,
rpc_password,
);
ChainSource::Rpc { rpc_host, rpc_port, rpc_user, rpc_password } => {
builder.set_chain_source_bitcoind_rpc(rpc_host, rpc_port, rpc_user, rpc_password);
Comment on lines -155 to +156
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Up to you people to make the call, I'd prefer parsing a single <hostname>:<port> string rather than splitting that setting into two parameters. This is what I do in VSS, it optimizes for fewer config params. That setting is logically a single thing right?

Copy link
Contributor Author

@Anyitechs Anyitechs Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That setting is logically a single thing right?

I agree it is. The problem was that we parse this setting using SocketAddr and that has a limitation of only supporting IP addresses. In containerized environments where the IP address might change if the container restarts, the user will have to update this setting each time it restarts. But supporting hostname simplifies things, because you only have to map the setting to a hostname and not worry if/when the IP changes.

An alternative to using SocketAddr (without splitting) is the ToSocketAddrs trait which supports hostname but it's sync and blocking.

UPDATE:

Missed "I'd prefer parsing a single <hostname>:<port>" from the comment earlier. Will drop the split.

},
ChainSource::Electrum { server_url } => {
builder.set_chain_source_electrum(server_url, None);
Expand Down
94 changes: 57 additions & 37 deletions ldk-server/src/util/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pub struct TlsConfig {

#[derive(Debug, PartialEq, Eq)]
pub enum ChainSource {
Rpc { rpc_address: SocketAddr, rpc_user: String, rpc_password: String },
Rpc { rpc_host: String, rpc_port: u16, rpc_user: String, rpc_password: String },
Electrum { server_url: String },
Esplora { server_url: String },
}
Expand All @@ -79,7 +79,8 @@ struct ConfigBuilder {
storage_dir_path: Option<String>,
electrum_url: Option<String>,
esplora_url: Option<String>,
bitcoind_rpc_addr: Option<String>,
bitcoind_rpc_host: Option<String>,
bitcoind_rpc_port: Option<u16>,
bitcoind_rpc_user: Option<String>,
bitcoind_rpc_password: Option<String>,
rabbitmq_connection_string: Option<String>,
Expand Down Expand Up @@ -108,7 +109,8 @@ impl ConfigBuilder {
}

if let Some(bitcoind) = toml.bitcoind {
self.bitcoind_rpc_addr = bitcoind.rpc_address.or(self.bitcoind_rpc_addr.clone());
self.bitcoind_rpc_host = bitcoind.rpc_host.or(self.bitcoind_rpc_host.clone());
self.bitcoind_rpc_port = bitcoind.rpc_port.or(self.bitcoind_rpc_port);
self.bitcoind_rpc_user = bitcoind.rpc_user.or(self.bitcoind_rpc_user.clone());
self.bitcoind_rpc_password =
bitcoind.rpc_password.or(self.bitcoind_rpc_password.clone());
Expand Down Expand Up @@ -166,8 +168,12 @@ impl ConfigBuilder {
self.alias = Some(node_alias.clone());
}

if let Some(bitcoind_rpc_address) = &args.bitcoind_rpc_address {
self.bitcoind_rpc_addr = Some(bitcoind_rpc_address.clone());
if let Some(bitcoind_rpc_host) = &args.bitcoind_rpc_host {
self.bitcoind_rpc_host = Some(bitcoind_rpc_host.clone());
}

if let Some(bitcoind_rpc_port) = &args.bitcoind_rpc_port {
self.bitcoind_rpc_port = Some(*bitcoind_rpc_port);
}

if let Some(bitcoind_rpc_user) = &args.bitcoind_rpc_user {
Expand Down Expand Up @@ -236,7 +242,8 @@ impl ConfigBuilder {
})
.transpose()?;

let rpc_configured = self.bitcoind_rpc_addr.is_some()
let rpc_configured = self.bitcoind_rpc_host.is_some()
|| self.bitcoind_rpc_port.is_some()
|| self.bitcoind_rpc_user.is_some()
|| self.bitcoind_rpc_password.is_some();
let electrum_configured = self.electrum_url.is_some();
Expand All @@ -255,13 +262,11 @@ impl ConfigBuilder {
}

let chain_source = if rpc_configured {
let rpc_address = self
.bitcoind_rpc_addr
.ok_or_else(|| missing_field_err("bitcoind_rpc_address"))?
.parse::<SocketAddr>()
.map_err(|e| {
io::Error::new(io::ErrorKind::InvalidInput, format!("Invalid RPC addr: {}", e))
})?;
let rpc_host =
self.bitcoind_rpc_host.ok_or_else(|| missing_field_err("bitcoind_rpc_host"))?;

let rpc_port =
self.bitcoind_rpc_port.ok_or_else(|| missing_field_err("bitcoind_rpc_port"))?;

let rpc_user =
self.bitcoind_rpc_user.ok_or_else(|| missing_field_err("bitcoind_rpc_user"))?;
Expand All @@ -270,7 +275,7 @@ impl ConfigBuilder {
.bitcoind_rpc_password
.ok_or_else(|| missing_field_err("bitcoind_rpc_password"))?;

ChainSource::Rpc { rpc_address, rpc_user, rpc_password }
ChainSource::Rpc { rpc_host, rpc_port, rpc_user, rpc_password }
} else if let Some(url) = self.electrum_url {
ChainSource::Electrum { server_url: url }
} else if let Some(url) = self.esplora_url {
Expand Down Expand Up @@ -386,7 +391,8 @@ struct DiskConfig {

#[derive(Deserialize, Serialize)]
struct BitcoindConfig {
rpc_address: Option<String>,
rpc_host: Option<String>,
rpc_port: Option<u16>,
rpc_user: Option<String>,
rpc_password: Option<String>,
}
Expand Down Expand Up @@ -517,10 +523,17 @@ pub struct ArgsConfig {

#[arg(
long,
env = "LDK_SERVER_BITCOIND_RPC_ADDRESS",
help = "The underlying Bitcoin node RPC address."
env = "LDK_SERVER_BITCOIND_RPC_HOST",
help = "The underlying Bitcoin node RPC host."
)]
bitcoind_rpc_host: Option<String>,

#[arg(
long,
env = "LDK_SERVER_BITCOIND_RPC_PORT",
help = "The underlying Bitcoin node RPC port."
)]
bitcoind_rpc_address: Option<String>,
bitcoind_rpc_port: Option<u16>,

#[arg(
long,
Expand Down Expand Up @@ -625,7 +638,8 @@ mod tests {
file = "/var/log/ldk-server.log"

[bitcoind]
rpc_address = "127.0.0.1:8332"
rpc_host = "127.0.0.1"
rpc_port = 8332
rpc_user = "bitcoind-testuser"
rpc_password = "bitcoind-testpassword"

Expand All @@ -652,7 +666,8 @@ mod tests {
node_listening_addresses: Some(vec!["localhost:3008".to_string()]),
node_announcement_addresses: Some(vec!["54.3.7.81:3001".to_string()]),
node_rest_service_address: Some(String::from("127.0.0.1:3009")),
bitcoind_rpc_address: Some(String::from("127.0.1.9:18443")),
bitcoind_rpc_host: Some(String::from("127.0.1.9")),
bitcoind_rpc_port: Some(18443),
bitcoind_rpc_user: Some(String::from("bitcoind-testuser_cli")),
bitcoind_rpc_password: Some(String::from("bitcoind-testpassword_cli")),
storage_dir_path: Some(String::from("/tmp_cli")),
Expand All @@ -668,7 +683,8 @@ mod tests {
node_announcement_addresses: None,
node_rest_service_address: None,
node_alias: None,
bitcoind_rpc_address: None,
bitcoind_rpc_host: None,
bitcoind_rpc_port: None,
bitcoind_rpc_user: None,
bitcoind_rpc_password: None,
storage_dir_path: None,
Expand Down Expand Up @@ -717,7 +733,8 @@ mod tests {
hosts: vec!["example.com".to_string(), "ldk-server.local".to_string()],
}),
chain_source: ChainSource::Rpc {
rpc_address: SocketAddr::from_str("127.0.0.1:8332").unwrap(),
rpc_host: "127.0.0.1".to_string(),
rpc_port: 8332,
rpc_user: "bitcoind-testuser".to_string(),
rpc_password: "bitcoind-testpassword".to_string(),
},
Expand Down Expand Up @@ -826,7 +843,8 @@ mod tests {
file = "/var/log/ldk-server.log"

[bitcoind]
rpc_address = "127.0.0.1:8332" # RPC endpoint
rpc_host = "127.0.0.1"
rpc_port = 8332
rpc_user = "bitcoind-testuser"
rpc_password = "bitcoind-testpassword"

Expand All @@ -849,11 +867,13 @@ mod tests {
fs::write(storage_path.join(config_file_name), toml_config).unwrap();
let config = load_config(&args_config).unwrap();

let ChainSource::Rpc { rpc_address, rpc_user, rpc_password } = config.chain_source else {
let ChainSource::Rpc { rpc_host, rpc_port, rpc_user, rpc_password } = config.chain_source
else {
panic!("unexpected chain source");
};

assert_eq!(rpc_address, SocketAddr::from_str("127.0.0.1:8332").unwrap());
assert_eq!(rpc_host, "127.0.0.1");
assert_eq!(rpc_port, 8332);
assert_eq!(rpc_user, "bitcoind-testuser");
assert_eq!(rpc_password, "bitcoind-testpassword");

Expand All @@ -880,7 +900,8 @@ mod tests {
file = "/var/log/ldk-server.log"

[bitcoind]
rpc_address = "127.0.0.1:8332" # RPC endpoint
rpc_host = "127.0.0.1"
rpc_port = 8332
rpc_user = "bitcoind-testuser"
rpc_password = "bitcoind-testpassword"

Expand Down Expand Up @@ -924,7 +945,8 @@ mod tests {
rest_service_address = "127.0.0.1:3002"

[bitcoind]
rpc_address = "127.0.0.1:8332" # RPC endpoint
rpc_host = "127.0.0.1"
rpc_port = 8332
rpc_user = "bitcoind-testuser"
rpc_password = "bitcoind-testpassword"

Expand Down Expand Up @@ -988,7 +1010,8 @@ 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!("rpc_host", missing_field_msg("bitcoind_rpc_host"));
validate_missing!("rpc_port", missing_field_msg("bitcoind_rpc_port"));
validate_missing!("rest_service_address =", missing_field_msg("rest_service_address"));
validate_missing!("network =", missing_field_msg("network"));
}
Expand Down Expand Up @@ -1026,10 +1049,8 @@ mod tests {
storage_dir_path: Some(args_config.storage_dir_path.unwrap()),
tls_config: None,
chain_source: ChainSource::Rpc {
rpc_address: SocketAddr::from_str(
args_config.bitcoind_rpc_address.as_deref().unwrap(),
)
.unwrap(),
rpc_host: args_config.bitcoind_rpc_host.unwrap(),
rpc_port: args_config.bitcoind_rpc_port.unwrap(),
rpc_user: args_config.bitcoind_rpc_user.unwrap(),
rpc_password: args_config.bitcoind_rpc_password.unwrap(),
},
Expand Down Expand Up @@ -1069,7 +1090,8 @@ mod tests {

validate_missing!(bitcoind_rpc_password, missing_field_msg("bitcoind_rpc_password"));
validate_missing!(bitcoind_rpc_user, missing_field_msg("bitcoind_rpc_user"));
validate_missing!(bitcoind_rpc_address, missing_field_msg("bitcoind_rpc_address"));
validate_missing!(bitcoind_rpc_host, missing_field_msg("bitcoind_rpc_host"));
validate_missing!(bitcoind_rpc_port, missing_field_msg("bitcoind_rpc_port"));
validate_missing!(node_network, missing_field_msg("network"));
validate_missing!(node_rest_service_address, missing_field_msg("rest_service_address"));
}
Expand Down Expand Up @@ -1114,10 +1136,8 @@ mod tests {
hosts: vec!["example.com".to_string(), "ldk-server.local".to_string()],
}),
chain_source: ChainSource::Rpc {
rpc_address: SocketAddr::from_str(
args_config.bitcoind_rpc_address.as_deref().unwrap(),
)
.unwrap(),
rpc_host: args_config.bitcoind_rpc_host.unwrap(),
rpc_port: args_config.bitcoind_rpc_port.unwrap(),
rpc_user: args_config.bitcoind_rpc_user.unwrap(),
rpc_password: args_config.bitcoind_rpc_password.unwrap(),
},
Expand Down