Skip to content

Commit

Permalink
Allow to listen and advertise on multiple SocketAddr
Browse files Browse the repository at this point in the history
  • Loading branch information
alexanderwiederin committed Nov 15, 2023
1 parent c7ab47c commit 18f567c
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ class AndroidLibTest {
val listenAddress1 = "127.0.0.1:2323"
val listenAddress2 = "127.0.0.1:2324"

val config1 = Config(tmpDir1, network, listenAddress1, 2048u)
val config2 = Config(tmpDir2, network, listenAddress2, 2048u)
val config1 = Config(tmpDir1, network, listOf(listenAddress1), 2048u)
val config2 = Config(tmpDir2, network, listOf(listenAddress2), 2048u)

val builder1 = Builder.fromConfig(config1)
val builder2 = Builder.fromConfig(config2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,15 @@ class LibraryTest {

val config1 = Config()
config1.storageDirPath = tmpDir1
config1.listeningAddress = listenAddress1
config1.listeningAddresses = listOf(listenAddress1)
config1.network = Network.REGTEST
config1.logLevel = LogLevel.TRACE

println("Config 1: $config1")

val config2 = Config()
config2.storageDirPath = tmpDir2
config2.listeningAddress = listenAddress2
config2.listeningAddresses = listOf(listenAddress2)
config2.network = Network.REGTEST
config2.logLevel = LogLevel.TRACE
println("Config 2: $config2")
Expand Down
8 changes: 5 additions & 3 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ dictionary Config {
string storage_dir_path = "/tmp/ldk_node/";
string? log_dir_path = null;
Network network = "Bitcoin";
SocketAddress? listening_address = null;
sequence<SocketAddress>? listening_addresses = null;
u32 default_cltv_expiry_delta = 144;
u64 onchain_wallet_sync_interval_secs = 80;
u64 wallet_sync_interval_secs = 30;
Expand All @@ -29,7 +29,8 @@ interface Builder {
void set_gossip_source_rgs(string rgs_server_url);
void set_storage_dir_path(string storage_dir_path);
void set_network(Network network);
void set_listening_address(SocketAddress listening_address);
[Throws=BuildError]
void set_listening_addresses(sequence<SocketAddress> listening_addresses);
[Throws=BuildError]
LDKNode build();
};
Expand All @@ -43,7 +44,7 @@ interface LDKNode {
Event wait_next_event();
void event_handled();
PublicKey node_id();
SocketAddress? listening_address();
sequence<SocketAddress>? listening_addresses();
[Throws=NodeError]
Address new_onchain_address();
[Throws=NodeError]
Expand Down Expand Up @@ -133,6 +134,7 @@ enum BuildError {
"InvalidSeedFile",
"InvalidSystemTime",
"InvalidChannelMonitor",
"InvalidListeningAddresses",
"ReadFailed",
"WriteFailed",
"StoragePathAccessFailed",
Expand Down
12 changes: 6 additions & 6 deletions bindings/python/src/ldk_node/test_ldk_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,13 @@ def send_to_address(address, amount_sats):
return res


def setup_node(tmp_dir, esplora_endpoint, listening_address):
def setup_node(tmp_dir, esplora_endpoint, listening_addresses):
config = Config()
builder = Builder.from_config(config)
builder.set_storage_dir_path(tmp_dir)
builder.set_esplora_server(esplora_endpoint)
builder.set_network(DEFAULT_TEST_NETWORK)
builder.set_listening_address(listening_address)
builder.set_listening_addresses(listening_addresses)
return builder.build()

def get_esplora_endpoint():
Expand All @@ -109,8 +109,8 @@ def test_channel_full_cycle(self):
tmp_dir_1 = tempfile.TemporaryDirectory("_ldk_node_1")
print("TMP DIR 1:", tmp_dir_1.name)

listening_address_1 = "127.0.0.1:2323"
node_1 = setup_node(tmp_dir_1.name, esplora_endpoint, listening_address_1)
listening_addresses_1 = ["127.0.0.1:2323"]
node_1 = setup_node(tmp_dir_1.name, esplora_endpoint, listening_addresses_1)
node_1.start()
node_id_1 = node_1.node_id()
print("Node ID 1:", node_id_1)
Expand All @@ -119,8 +119,8 @@ def test_channel_full_cycle(self):
tmp_dir_2 = tempfile.TemporaryDirectory("_ldk_node_2")
print("TMP DIR 2:", tmp_dir_2.name)

listening_address_2 = "127.0.0.1:2324"
node_2 = setup_node(tmp_dir_2.name, esplora_endpoint, listening_address_2)
listening_addresses_2 = ["127.0.0.1:2324"]
node_2 = setup_node(tmp_dir_2.name, esplora_endpoint, listening_addresses_2)
node_2.start()
node_id_2 = node_2.node_id()
print("Node ID 2:", node_id_2)
Expand Down
21 changes: 16 additions & 5 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ pub enum BuildError {
InvalidSystemTime,
/// The a read channel monitor is invalid.
InvalidChannelMonitor,
/// The given listening addresses are invalid, e.g. too many were passed.
InvalidListeningAddresses,
/// We failed to read data from the [`KVStore`].
ReadFailed,
/// We failed to write data to the [`KVStore`].
Expand All @@ -115,6 +117,7 @@ impl fmt::Display for BuildError {
Self::InvalidChannelMonitor => {
write!(f, "Failed to watch a deserialized ChannelMonitor")
}
Self::InvalidListeningAddresses => write!(f, "Given listening addresses are invalid."),
Self::ReadFailed => write!(f, "Failed to read from store."),
Self::WriteFailed => write!(f, "Failed to write to store."),
Self::StoragePathAccessFailed => write!(f, "Failed to access the given storage path."),
Expand Down Expand Up @@ -231,9 +234,15 @@ impl NodeBuilder {
}

/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
pub fn set_listening_address(&mut self, listening_address: SocketAddress) -> &mut Self {
self.config.listening_address = Some(listening_address);
self
pub fn set_listening_addresses(
&mut self, listening_addresses: Vec<SocketAddress>,
) -> Result<&mut Self, BuildError> {
if listening_addresses.len() > 100 {
return Err(BuildError::InvalidListeningAddresses);
}

self.config.listening_addresses = Some(listening_addresses);
Ok(self)
}

/// Sets the level at which [`Node`] will log messages.
Expand Down Expand Up @@ -386,8 +395,10 @@ impl ArcedNodeBuilder {
}

/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
pub fn set_listening_address(&self, listening_address: SocketAddress) {
self.inner.write().unwrap().set_listening_address(listening_address);
pub fn set_listening_addresses(
&self, listening_addresses: Vec<SocketAddress>,
) -> Result<(), BuildError> {
self.inner.write().unwrap().set_listening_addresses(listening_addresses).map(|_| ())
}

/// Sets the level at which [`Node`] will log messages.
Expand Down
54 changes: 27 additions & 27 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ const WALLET_KEYS_SEED_LEN: usize = 64;
/// | `storage_dir_path` | /tmp/ldk_node/ |
/// | `log_dir_path` | None |
/// | `network` | Bitcoin |
/// | `listening_address` | None |
/// | `listening_addresses` | None |
/// | `default_cltv_expiry_delta` | 144 |
/// | `onchain_wallet_sync_interval_secs` | 80 |
/// | `wallet_sync_interval_secs` | 30 |
Expand All @@ -225,8 +225,8 @@ pub struct Config {
pub log_dir_path: Option<String>,
/// The used Bitcoin network.
pub network: Network,
/// The IP address and TCP port the node will listen on.
pub listening_address: Option<SocketAddress>,
/// The addresses on which the node will listen for incoming connections.
pub listening_addresses: Option<Vec<SocketAddress>>,
/// The default CLTV expiry delta to be used for payments.
pub default_cltv_expiry_delta: u32,
/// The time in-between background sync attempts of the onchain wallet, in seconds.
Expand Down Expand Up @@ -264,7 +264,7 @@ impl Default for Config {
storage_dir_path: DEFAULT_STORAGE_DIR_PATH.to_string(),
log_dir_path: None,
network: DEFAULT_NETWORK,
listening_address: None,
listening_addresses: None,
default_cltv_expiry_delta: DEFAULT_CLTV_EXPIRY_DELTA,
onchain_wallet_sync_interval_secs: DEFAULT_BDK_WALLET_SYNC_INTERVAL_SECS,
wallet_sync_interval_secs: DEFAULT_LDK_WALLET_SYNC_INTERVAL_SECS,
Expand Down Expand Up @@ -490,38 +490,33 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
});
}

if let Some(listening_address) = &self.config.listening_address {
if let Some(listening_addresses) = &self.config.listening_addresses {
// Setup networking
let peer_manager_connection_handler = Arc::clone(&self.peer_manager);
let mut stop_listen = self.stop_receiver.clone();
let listening_address = listening_address.clone();
let listening_logger = Arc::clone(&self.logger);

let bind_addr = listening_address
.to_socket_addrs()
.map_err(|_| {
log_error!(
self.logger,
"Unable to resolve listing address: {:?}",
listening_address
);
Error::InvalidSocketAddress
})?
.next()
.ok_or_else(|| {
let mut bind_addrs = Vec::with_capacity(listening_addresses.len());

for listening_addr in listening_addresses {
let resolved_address = listening_addr.to_socket_addrs().map_err(|e| {
log_error!(
self.logger,
"Unable to resolve listing address: {:?}",
listening_address
"Unable to resolve listening address: {:?}. Error details: {}",
listening_addr,
e,
);
Error::InvalidSocketAddress
})?;

bind_addrs.extend(resolved_address);
}

runtime.spawn(async move {
let listener =
tokio::net::TcpListener::bind(bind_addr).await
tokio::net::TcpListener::bind(&*bind_addrs).await
.unwrap_or_else(|e| {
log_error!(listening_logger, "Failed to bind to listen address/port - is something else already listening on it?: {}", e);
log_error!(listening_logger, "Failed to bind to listen addresses/ports - is something else already listening on it?: {}", e);
panic!(
"Failed to bind to listen address/port - is something else already listening on it?",
);
Expand Down Expand Up @@ -631,8 +626,13 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
continue;
}

let addresses =
bcast_config.listening_address.iter().cloned().collect();
let addresses = bcast_config.listening_addresses.clone().unwrap_or(Vec::new());

if addresses.is_empty() {
// Skip if we are not listening on any addresses.
continue;
}

bcast_pm.broadcast_node_announcement([0; 3], [0; 32], addresses);

let unix_time_secs = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
Expand Down Expand Up @@ -781,9 +781,9 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
self.channel_manager.get_our_node_id()
}

/// Returns our own listening address.
pub fn listening_address(&self) -> Option<SocketAddress> {
self.config.listening_address.clone()
/// Returns our own listening addresses.
pub fn listening_addresses(&self) -> Option<Vec<SocketAddress>> {
self.config.listening_addresses.clone()
}

/// Retrieve a new on-chain/funding address.
Expand Down
4 changes: 2 additions & 2 deletions src/test/functional_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ fn do_channel_full_cycle<K: KVStore + Sync + Send>(
node_a
.connect_open_channel(
node_b.node_id(),
node_b.listening_address().unwrap().into(),
node_b.listening_addresses().unwrap().first().unwrap().clone(),
funding_amount_sat,
Some(push_msat),
None,
Expand Down Expand Up @@ -332,7 +332,7 @@ fn channel_open_fails_when_funds_insufficient() {
Err(Error::InsufficientFunds),
node_a.connect_open_channel(
node_b.node_id(),
node_b.listening_address().unwrap().into(),
node_b.listening_addresses().unwrap().first().unwrap().clone(),
120000,
None,
None,
Expand Down
21 changes: 17 additions & 4 deletions src/test/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::builder::NodeBuilder;
use crate::io::test_utils::TestSyncStore;
use crate::{Config, Node};
use lightning::ln::msgs::SocketAddress;
use lightning::util::logger::{Level, Logger, Record};

use bitcoin::{Address, Amount, Network, OutPoint, Txid};
Expand Down Expand Up @@ -134,6 +135,19 @@ pub fn random_port() -> u16 {
rng.gen_range(5000..65535)
}

pub fn random_listening_addresses() -> Vec<SocketAddress> {
let num_addresses = 2;
let mut listening_addresses = Vec::with_capacity(num_addresses);

for _ in 0..num_addresses {
let rand_port = random_port();
let address: SocketAddress = format!("127.0.0.1:{}", rand_port).parse().unwrap();
listening_addresses.push(address);
}

listening_addresses
}

pub fn random_config() -> Config {
let mut config = Config::default();

Expand All @@ -144,10 +158,9 @@ pub fn random_config() -> Config {
println!("Setting random LDK storage dir: {}", rand_dir.display());
config.storage_dir_path = rand_dir.to_str().unwrap().to_owned();

let rand_port = random_port();
println!("Setting random LDK listening port: {}", rand_port);
let listening_address_str = format!("127.0.0.1:{}", rand_port);
config.listening_address = Some(listening_address_str.parse().unwrap());
let rand_listening_addresses = random_listening_addresses();
println!("Setting random LDK listening addresses: {:?}", rand_listening_addresses);
config.listening_addresses = Some(rand_listening_addresses);

config.log_level = Level::Trace;

Expand Down

0 comments on commit 18f567c

Please sign in to comment.