Skip to content
Merged
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
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand Up@@ -133,6 +134,7 @@ enum BuildError {
"InvalidSeedFile",
"InvalidSystemTime",
"InvalidChannelMonitor",
"InvalidListeningAddresses",
"ReadFailed",
"WriteFailed",
"StoragePathAccessFailed",
Expand Down
14 changes: 7 additions & 7 deletions bindings/python/src/ldk_node/test_ldk_node.py
Original file line numberDiff line numberDiff 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"]
Comment thread
alexanderwiederin marked this conversation as resolved.
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 DownExpand Up@@ -155,7 +155,7 @@ def test_channel_full_cycle(self):
print("TOTAL 2:", total_balance_2)
self.assertEqual(total_balance_2, 100000)

node_1.connect_open_channel(node_id_2, listening_address_2, 50000, None, None, True)
node_1.connect_open_channel(node_id_2, listening_addresses_2[0], 50000, None, None, True)

channel_pending_event_1 = node_1.wait_next_event()
assert isinstance(channel_pending_event_1, Event.CHANNEL_PENDING)
Expand Down
23 changes: 17 additions & 6 deletions src/builder.rs
Original file line numberDiff line numberDiff 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@@ -113,8 +115,9 @@ impl fmt::Display for BuildError {
write!(f, "System time is invalid. Clocks might have gone back in time.")
}
Self::InvalidChannelMonitor => {
write!(f, "Failed to watch a deserialzed ChannelMonitor")
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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Allow to listen on and advertise multiple `SocketAddr` by alexanderwiederin · Pull Request #187 · lightningdevkit/ldk-node · GitHub
Skip to content
Merged
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
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand Up@@ -133,6 +134,7 @@ enum BuildError {
"InvalidSeedFile",
"InvalidSystemTime",
"InvalidChannelMonitor",
"InvalidListeningAddresses",
"ReadFailed",
"WriteFailed",
"StoragePathAccessFailed",
Expand Down
14 changes: 7 additions & 7 deletions bindings/python/src/ldk_node/test_ldk_node.py
Original file line numberDiff line numberDiff 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"]
Comment thread
alexanderwiederin marked this conversation as resolved.
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 DownExpand Up@@ -155,7 +155,7 @@ def test_channel_full_cycle(self):
print("TOTAL 2:", total_balance_2)
self.assertEqual(total_balance_2, 100000)

node_1.connect_open_channel(node_id_2, listening_address_2, 50000, None, None, True)
node_1.connect_open_channel(node_id_2, listening_addresses_2[0], 50000, None, None, True)

channel_pending_event_1 = node_1.wait_next_event()
assert isinstance(channel_pending_event_1, Event.CHANNEL_PENDING)
Expand Down
23 changes: 17 additions & 6 deletions src/builder.rs
Original file line numberDiff line numberDiff 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@@ -113,8 +115,9 @@ impl fmt::Display for BuildError {
write!(f, "System time is invalid. Clocks might have gone back in time.")
}
Self::InvalidChannelMonitor => {
write!(f, "Failed to watch a deserialzed ChannelMonitor")
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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Allow to listen on and advertise multiple `SocketAddr` by alexanderwiederin · Pull Request #187 · lightningdevkit/ldk-node · GitHub
Skip to content
Merged
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
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand Up@@ -133,6 +134,7 @@ enum BuildError {
"InvalidSeedFile",
"InvalidSystemTime",
"InvalidChannelMonitor",
"InvalidListeningAddresses",
"ReadFailed",
"WriteFailed",
"StoragePathAccessFailed",
Expand Down
14 changes: 7 additions & 7 deletions bindings/python/src/ldk_node/test_ldk_node.py
Original file line numberDiff line numberDiff 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"]
Comment thread
alexanderwiederin marked this conversation as resolved.
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 DownExpand Up@@ -155,7 +155,7 @@ def test_channel_full_cycle(self):
print("TOTAL 2:", total_balance_2)
self.assertEqual(total_balance_2, 100000)

node_1.connect_open_channel(node_id_2, listening_address_2, 50000, None, None, True)
node_1.connect_open_channel(node_id_2, listening_addresses_2[0], 50000, None, None, True)

channel_pending_event_1 = node_1.wait_next_event()
assert isinstance(channel_pending_event_1, Event.CHANNEL_PENDING)
Expand Down
23 changes: 17 additions & 6 deletions src/builder.rs
Original file line numberDiff line numberDiff 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@@ -113,8 +115,9 @@ impl fmt::Display for BuildError {
write!(f, "System time is invalid. Clocks might have gone back in time.")
}
Self::InvalidChannelMonitor => {
write!(f, "Failed to watch a deserialzed ChannelMonitor")
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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Allow to listen on and advertise multiple `SocketAddr` by alexanderwiederin · Pull Request #187 · lightningdevkit/ldk-node · GitHub
Skip to content
Merged
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
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand Up@@ -133,6 +134,7 @@ enum BuildError {
"InvalidSeedFile",
"InvalidSystemTime",
"InvalidChannelMonitor",
"InvalidListeningAddresses",
"ReadFailed",
"WriteFailed",
"StoragePathAccessFailed",
Expand Down
14 changes: 7 additions & 7 deletions bindings/python/src/ldk_node/test_ldk_node.py
Original file line numberDiff line numberDiff 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"]
Comment thread
alexanderwiederin marked this conversation as resolved.
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 DownExpand Up@@ -155,7 +155,7 @@ def test_channel_full_cycle(self):
print("TOTAL 2:", total_balance_2)
self.assertEqual(total_balance_2, 100000)

node_1.connect_open_channel(node_id_2, listening_address_2, 50000, None, None, True)
node_1.connect_open_channel(node_id_2, listening_addresses_2[0], 50000, None, None, True)

channel_pending_event_1 = node_1.wait_next_event()
assert isinstance(channel_pending_event_1, Event.CHANNEL_PENDING)
Expand Down
23 changes: 17 additions & 6 deletions src/builder.rs
Original file line numberDiff line numberDiff 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@@ -113,8 +115,9 @@ impl fmt::Display for BuildError {
write!(f, "System time is invalid. Clocks might have gone back in time.")
}
Self::InvalidChannelMonitor => {
write!(f, "Failed to watch a deserialzed ChannelMonitor")
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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Allow to listen on and advertise multiple `SocketAddr` by alexanderwiederin · Pull Request #187 · lightningdevkit/ldk-node · GitHub
Skip to content
Merged
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
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand Up@@ -133,6 +134,7 @@ enum BuildError {
"InvalidSeedFile",
"InvalidSystemTime",
"InvalidChannelMonitor",
"InvalidListeningAddresses",
"ReadFailed",
"WriteFailed",
"StoragePathAccessFailed",
Expand Down
14 changes: 7 additions & 7 deletions bindings/python/src/ldk_node/test_ldk_node.py
Original file line numberDiff line numberDiff 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"]
Comment thread
alexanderwiederin marked this conversation as resolved.
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 DownExpand Up@@ -155,7 +155,7 @@ def test_channel_full_cycle(self):
print("TOTAL 2:", total_balance_2)
self.assertEqual(total_balance_2, 100000)

node_1.connect_open_channel(node_id_2, listening_address_2, 50000, None, None, True)
node_1.connect_open_channel(node_id_2, listening_addresses_2[0], 50000, None, None, True)

channel_pending_event_1 = node_1.wait_next_event()
assert isinstance(channel_pending_event_1, Event.CHANNEL_PENDING)
Expand Down
23 changes: 17 additions & 6 deletions src/builder.rs
Original file line numberDiff line numberDiff 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@@ -113,8 +115,9 @@ impl fmt::Display for BuildError {
write!(f, "System time is invalid. Clocks might have gone back in time.")
}
Self::InvalidChannelMonitor => {
write!(f, "Failed to watch a deserialzed ChannelMonitor")
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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Allow to listen on and advertise multiple `SocketAddr` by alexanderwiederin · Pull Request #187 · lightningdevkit/ldk-node · GitHub
Skip to content
Merged
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
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand Up@@ -133,6 +134,7 @@ enum BuildError {
"InvalidSeedFile",
"InvalidSystemTime",
"InvalidChannelMonitor",
"InvalidListeningAddresses",
"ReadFailed",
"WriteFailed",
"StoragePathAccessFailed",
Expand Down
14 changes: 7 additions & 7 deletions bindings/python/src/ldk_node/test_ldk_node.py
Original file line numberDiff line numberDiff 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"]
Comment thread
alexanderwiederin marked this conversation as resolved.
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 DownExpand Up@@ -155,7 +155,7 @@ def test_channel_full_cycle(self):
print("TOTAL 2:", total_balance_2)
self.assertEqual(total_balance_2, 100000)

node_1.connect_open_channel(node_id_2, listening_address_2, 50000, None, None, True)
node_1.connect_open_channel(node_id_2, listening_addresses_2[0], 50000, None, None, True)

channel_pending_event_1 = node_1.wait_next_event()
assert isinstance(channel_pending_event_1, Event.CHANNEL_PENDING)
Expand Down
23 changes: 17 additions & 6 deletions src/builder.rs
Original file line numberDiff line numberDiff 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@@ -113,8 +115,9 @@ impl fmt::Display for BuildError {
write!(f, "System time is invalid. Clocks might have gone back in time.")
}
Self::InvalidChannelMonitor => {
write!(f, "Failed to watch a deserialzed ChannelMonitor")
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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Allow to listen on and advertise multiple `SocketAddr` by alexanderwiederin · Pull Request #187 · lightningdevkit/ldk-node · GitHub
Skip to content
Merged
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
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand Up@@ -133,6 +134,7 @@ enum BuildError {
"InvalidSeedFile",
"InvalidSystemTime",
"InvalidChannelMonitor",
"InvalidListeningAddresses",
"ReadFailed",
"WriteFailed",
"StoragePathAccessFailed",
Expand Down
14 changes: 7 additions & 7 deletions bindings/python/src/ldk_node/test_ldk_node.py
Original file line numberDiff line numberDiff 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"]
Comment thread
alexanderwiederin marked this conversation as resolved.
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 DownExpand Up@@ -155,7 +155,7 @@ def test_channel_full_cycle(self):
print("TOTAL 2:", total_balance_2)
self.assertEqual(total_balance_2, 100000)

node_1.connect_open_channel(node_id_2, listening_address_2, 50000, None, None, True)
node_1.connect_open_channel(node_id_2, listening_addresses_2[0], 50000, None, None, True)

channel_pending_event_1 = node_1.wait_next_event()
assert isinstance(channel_pending_event_1, Event.CHANNEL_PENDING)
Expand Down
23 changes: 17 additions & 6 deletions src/builder.rs
Original file line numberDiff line numberDiff 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@@ -113,8 +115,9 @@ impl fmt::Display for BuildError {
write!(f, "System time is invalid. Clocks might have gone back in time.")
}
Self::InvalidChannelMonitor => {
write!(f, "Failed to watch a deserialzed ChannelMonitor")
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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Allow to listen on and advertise multiple `SocketAddr` by alexanderwiederin · Pull Request #187 · lightningdevkit/ldk-node · GitHub
Skip to content
Merged
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
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand Up@@ -133,6 +134,7 @@ enum BuildError {
"InvalidSeedFile",
"InvalidSystemTime",
"InvalidChannelMonitor",
"InvalidListeningAddresses",
"ReadFailed",
"WriteFailed",
"StoragePathAccessFailed",
Expand Down
14 changes: 7 additions & 7 deletions bindings/python/src/ldk_node/test_ldk_node.py
Original file line numberDiff line numberDiff 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"]
Comment thread
alexanderwiederin marked this conversation as resolved.
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 DownExpand Up@@ -155,7 +155,7 @@ def test_channel_full_cycle(self):
print("TOTAL 2:", total_balance_2)
self.assertEqual(total_balance_2, 100000)

node_1.connect_open_channel(node_id_2, listening_address_2, 50000, None, None, True)
node_1.connect_open_channel(node_id_2, listening_addresses_2[0], 50000, None, None, True)

channel_pending_event_1 = node_1.wait_next_event()
assert isinstance(channel_pending_event_1, Event.CHANNEL_PENDING)
Expand Down
23 changes: 17 additions & 6 deletions src/builder.rs
Original file line numberDiff line numberDiff 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@@ -113,8 +115,9 @@ impl fmt::Display for BuildError {
write!(f, "System time is invalid. Clocks might have gone back in time.")
}
Self::InvalidChannelMonitor => {
write!(f, "Failed to watch a deserialzed ChannelMonitor")
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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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