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
8 changes: 8 additions & 0 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,11 @@ impl NodeBuilder {
return Err(BuildError::InvalidListeningAddresses);
}

#[cfg(not(test))]
if listening_addresses.iter().any(crate::config::has_port_zero) {
return Err(BuildError::InvalidListeningAddresses);
}

self.config.listening_addresses = Some(listening_addresses);
Ok(self)
}
Expand Down Expand Up @@ -1944,6 +1949,8 @@ fn build_with_store_internal(

let (stop_sender, _) = tokio::sync::watch::channel(());
let (background_processor_stop_sender, _) = tokio::sync::watch::channel(());
let last_bound_addresses: Arc<RwLock<Option<Vec<SocketAddress>>>> =
Arc::new(RwLock::new(None));
let is_running = Arc::new(RwLock::new(false));

let pathfinding_scores_sync_url = pathfinding_scores_sync_config.map(|c| c.url.clone());
Expand Down Expand Up @@ -1988,6 +1995,7 @@ fn build_with_store_internal(
peer_store,
payment_store,
lnurl_auth,
last_bound_addresses,
is_running,
node_metrics,
om_mailbox,
Expand Down
11 changes: 11 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,17 @@ pub(crate) fn may_announce_channel(config: &Config) -> Result<(), AnnounceError>
}
}

#[cfg_attr(test, allow(dead_code))]
pub(crate) fn has_port_zero(addr: &SocketAddress) -> bool {
match addr {
SocketAddress::TcpIpV4 { port, .. }
| SocketAddress::TcpIpV6 { port, .. }
| SocketAddress::OnionV3 { port, .. }
| SocketAddress::Hostname { port, .. } => *port == 0,
_ => false,
}
}

pub(crate) fn default_user_config(config: &Config) -> UserConfig {
// Initialize the default config values.
//
Expand Down
55 changes: 49 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ pub struct Node {
peer_store: Arc<PeerStore<Arc<Logger>>>,
payment_store: Arc<PaymentStore>,
lnurl_auth: Arc<LnurlAuth>,
last_bound_addresses: Arc<RwLock<Option<Vec<SocketAddress>>>>,
is_running: Arc<RwLock<bool>>,
node_metrics: Arc<RwLock<NodeMetrics>>,
om_mailbox: Option<Arc<OnionMessageMailbox>>,
Expand Down Expand Up @@ -356,7 +357,14 @@ impl Node {
);
}

if let Some(listening_addresses) = &self.config.listening_addresses {
let effective_listening_addresses = self
.last_bound_addresses
.read()
.unwrap()
.clone()
.or_else(|| self.config.listening_addresses.clone());

if let Some(listening_addresses) = &effective_listening_addresses {
// Setup networking
let peer_manager_connection_handler = Arc::clone(&self.peer_manager);
let listening_logger = Arc::clone(&self.logger);
Expand All @@ -378,14 +386,31 @@ impl Node {
}

let logger = Arc::clone(&listening_logger);
let listeners = self.runtime.block_on(async move {
let (listeners, bound_addrs) = self.runtime.block_on(async move {
let mut listeners = Vec::new();
let mut bound_addrs = Vec::new();

// Try to bind to all addresses
for addr in &*bind_addrs {
match tokio::net::TcpListener::bind(addr).await {
Ok(listener) => {
log_trace!(logger, "Listener bound to {}", addr);
let local_addr = listener.local_addr().map_err(|e| {
log_error!(
logger,
"Failed to retrieve local address from listener: {}",
e
);
Error::InvalidSocketAddress
})?;
let socket_address = match local_addr {
std::net::SocketAddr::V4(a) => {
SocketAddress::TcpIpV4 { addr: a.ip().octets(), port: a.port() }
},
std::net::SocketAddr::V6(a) => {
SocketAddress::TcpIpV6 { addr: a.ip().octets(), port: a.port() }
},
};
log_info!(logger, "Listening on {}", socket_address);
bound_addrs.push(socket_address);
listeners.push(listener);
},
Err(e) => {
Expand All @@ -400,9 +425,11 @@ impl Node {
}
}

Ok(listeners)
Ok((listeners, bound_addrs))
})?;

*self.last_bound_addresses.write().unwrap() = Some(bound_addrs);

for listener in listeners {
let logger = Arc::clone(&listening_logger);
let peer_mgr = Arc::clone(&peer_manager_connection_handler);
Expand Down Expand Up @@ -475,6 +502,7 @@ impl Node {
let bcast_cm = Arc::clone(&self.channel_manager);
let bcast_pm = Arc::clone(&self.peer_manager);
let bcast_config = Arc::clone(&self.config);
let bcast_bound_addrs = Arc::clone(&self.last_bound_addresses);
let bcast_store = Arc::clone(&self.kv_store);
let bcast_logger = Arc::clone(&self.logger);
let bcast_node_metrics = Arc::clone(&self.node_metrics);
Expand Down Expand Up @@ -525,6 +553,8 @@ impl Node {

let addresses = if let Some(announcement_addresses) = bcast_config.announcement_addresses.clone() {
announcement_addresses
} else if let Some(bound_addresses) = bcast_bound_addrs.read().unwrap().clone() {
bound_addresses
} else if let Some(listening_addresses) = bcast_config.listening_addresses.clone() {
listening_addresses
} else {
Expand Down Expand Up @@ -842,15 +872,28 @@ impl Node {
}

/// Returns our own listening addresses.
///
/// If the node has been started, this returns the actual bound addresses (which may differ
/// from the configured addresses if port 0 was used). Otherwise, this returns the configured
/// addresses.
pub fn listening_addresses(&self) -> Option<Vec<SocketAddress>> {
self.config.listening_addresses.clone()
self.last_bound_addresses
.read()
.unwrap()
.clone()
.or_else(|| self.config.listening_addresses.clone())
}

/// Returns the addresses that the node will announce to the network.
///
/// Returns the configured announcement addresses if set, otherwise falls back to the
/// actual bound addresses (which may differ from configured addresses if port 0 was used),
/// or the configured listening addresses.
pub fn announcement_addresses(&self) -> Option<Vec<SocketAddress>> {
self.config
.announcement_addresses
.clone()
.or_else(|| self.last_bound_addresses.read().unwrap().clone())
.or_else(|| self.config.listening_addresses.clone())
}

Expand Down
13 changes: 1 addition & 12 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use std::collections::{HashMap, HashSet};
use std::env;
use std::future::Future;
use std::path::PathBuf;
use std::sync::atomic::{AtomicU16, Ordering};
use std::sync::{Arc, RwLock};
use std::time::Duration;

Expand Down Expand Up @@ -269,16 +268,6 @@ pub(crate) fn random_storage_path() -> PathBuf {
temp_path
}

static NEXT_PORT: AtomicU16 = AtomicU16::new(20000);

pub(crate) fn generate_listening_addresses() -> Vec<SocketAddress> {
let port = NEXT_PORT.fetch_add(2, Ordering::Relaxed);
vec![
SocketAddress::TcpIpV4 { addr: [127, 0, 0, 1], port },
SocketAddress::TcpIpV4 { addr: [127, 0, 0, 1], port: port + 1 },
]
}

pub(crate) fn random_node_alias() -> Option<NodeAlias> {
let mut rng = rng();
let rand_val = rng.random_range(0..1000);
Expand All @@ -302,7 +291,7 @@ pub(crate) fn random_config(anchor_channels: bool) -> TestConfig {
println!("Setting random LDK storage dir: {}", rand_dir.display());
node_config.storage_dir_path = rand_dir.to_str().unwrap().to_owned();

let listening_addresses = generate_listening_addresses();
let listening_addresses = vec![SocketAddress::TcpIpV4 { addr: [127, 0, 0, 1], port: 0 }];
println!("Setting LDK listening addresses: {:?}", listening_addresses);
node_config.listening_addresses = Some(listening_addresses);

Expand Down
27 changes: 14 additions & 13 deletions tests/integration_tests_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ use common::{
expect_channel_pending_event, expect_channel_ready_event, expect_channel_ready_events,
expect_event, expect_payment_claimable_event, expect_payment_received_event,
expect_payment_successful_event, expect_splice_pending_event, generate_blocks_and_wait,
generate_listening_addresses, open_channel, open_channel_push_amt, open_channel_with_all,
premine_and_distribute_funds, premine_blocks, prepare_rbf, random_chain_source, random_config,
setup_bitcoind_and_electrsd, setup_builder, setup_node, setup_two_nodes, splice_in_with_all,
wait_for_tx, TestChainSource, TestStoreType, TestSyncStore,
open_channel, open_channel_push_amt, open_channel_with_all, premine_and_distribute_funds,
premine_blocks, prepare_rbf, random_chain_source, random_config, setup_bitcoind_and_electrsd,
setup_builder, setup_node, setup_two_nodes, splice_in_with_all, wait_for_tx, TestChainSource,
TestStoreType, TestSyncStore,
};
use electrsd::corepc_node::Node as BitcoinD;
use electrsd::ElectrsD;
Expand All @@ -37,6 +37,7 @@ use ldk_node::payment::{
};
use ldk_node::{Builder, Event, NodeError};
use lightning::ln::channelmanager::PaymentId;
use lightning::ln::msgs::SocketAddress;
use lightning::routing::gossip::{NodeAlias, NodeId};
use lightning::routing::router::RouteParametersConfig;
use lightning_invoice::{Bolt11InvoiceDescription, Description};
Expand Down Expand Up @@ -1424,29 +1425,28 @@ async fn test_node_announcement_propagation() {
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
let chain_source = random_chain_source(&bitcoind, &electrsd);

// Node A will use both listening and announcement addresses
let mut config_a = random_config(true);
let node_a_alias_string = "ldk-node-a".to_string();
let mut node_a_alias_bytes = [0u8; 32];
node_a_alias_bytes[..node_a_alias_string.as_bytes().len()]
.copy_from_slice(node_a_alias_string.as_bytes());
let node_a_node_alias = Some(NodeAlias(node_a_alias_bytes));
let node_a_announcement_addresses = generate_listening_addresses();
config_a.node_config.node_alias = node_a_node_alias.clone();
config_a.node_config.listening_addresses = Some(generate_listening_addresses());
// Set explicit announcement addresses to verify they take priority over bound addresses.
let node_a_announcement_addresses = vec![
SocketAddress::TcpIpV4 { addr: [127, 0, 0, 1], port: 10001 },
SocketAddress::TcpIpV4 { addr: [127, 0, 0, 1], port: 10002 },
];
config_a.node_config.announcement_addresses = Some(node_a_announcement_addresses.clone());

// Node B will only use listening addresses
// Node B uses default config to verify that bound addresses are announced.
let mut config_b = random_config(true);
let node_b_alias_string = "ldk-node-b".to_string();
let mut node_b_alias_bytes = [0u8; 32];
node_b_alias_bytes[..node_b_alias_string.as_bytes().len()]
.copy_from_slice(node_b_alias_string.as_bytes());
let node_b_node_alias = Some(NodeAlias(node_b_alias_bytes));
let node_b_listening_addresses = generate_listening_addresses();
config_b.node_config.node_alias = node_b_node_alias.clone();
config_b.node_config.listening_addresses = Some(node_b_listening_addresses.clone());
config_b.node_config.announcement_addresses = None;

let node_a = setup_node(&chain_source, config_a);
let node_b = setup_node(&chain_source, config_b);
Expand Down Expand Up @@ -1505,10 +1505,11 @@ async fn test_node_announcement_propagation() {
#[cfg(feature = "uniffi")]
assert_eq!(node_b_announcement_info.alias, node_b_alias_string);

let node_b_announcement_addresses = node_b.announcement_addresses().unwrap();
#[cfg(not(feature = "uniffi"))]
assert_eq!(node_b_announcement_info.addresses(), &node_b_listening_addresses);
assert_eq!(node_b_announcement_info.addresses(), &node_b_announcement_addresses);
#[cfg(feature = "uniffi")]
assert_eq!(node_b_announcement_info.addresses, node_b_listening_addresses);
assert_eq!(node_b_announcement_info.addresses, node_b_announcement_addresses);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
Expand Down
Loading