Skip to content

Commit 18f567c

Browse files
Allow to listen and advertise on multiple SocketAddr
1 parent c7ab47c commit 18f567c

File tree

8 files changed

+77
-51
lines changed

8 files changed

+77
-51
lines changed

bindings/kotlin/ldk-node-android/lib/src/androidTest/kotlin/org/lightningdevkit/ldknode/AndroidLibTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ class AndroidLibTest {
2626
val listenAddress1 = "127.0.0.1:2323"
2727
val listenAddress2 = "127.0.0.1:2324"
2828

29-
val config1 = Config(tmpDir1, network, listenAddress1, 2048u)
30-
val config2 = Config(tmpDir2, network, listenAddress2, 2048u)
29+
val config1 = Config(tmpDir1, network, listOf(listenAddress1), 2048u)
30+
val config2 = Config(tmpDir2, network, listOf(listenAddress2), 2048u)
3131

3232
val builder1 = Builder.fromConfig(config1)
3333
val builder2 = Builder.fromConfig(config2)

bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,15 @@ class LibraryTest {
116116

117117
val config1 = Config()
118118
config1.storageDirPath = tmpDir1
119-
config1.listeningAddress = listenAddress1
119+
config1.listeningAddresses = listOf(listenAddress1)
120120
config1.network = Network.REGTEST
121121
config1.logLevel = LogLevel.TRACE
122122

123123
println("Config 1: $config1")
124124

125125
val config2 = Config()
126126
config2.storageDirPath = tmpDir2
127-
config2.listeningAddress = listenAddress2
127+
config2.listeningAddresses = listOf(listenAddress2)
128128
config2.network = Network.REGTEST
129129
config2.logLevel = LogLevel.TRACE
130130
println("Config 2: $config2")

bindings/ldk_node.udl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ dictionary Config {
66
string storage_dir_path = "/tmp/ldk_node/";
77
string? log_dir_path = null;
88
Network network = "Bitcoin";
9-
SocketAddress? listening_address = null;
9+
sequence<SocketAddress>? listening_addresses = null;
1010
u32 default_cltv_expiry_delta = 144;
1111
u64 onchain_wallet_sync_interval_secs = 80;
1212
u64 wallet_sync_interval_secs = 30;
@@ -29,7 +29,8 @@ interface Builder {
2929
void set_gossip_source_rgs(string rgs_server_url);
3030
void set_storage_dir_path(string storage_dir_path);
3131
void set_network(Network network);
32-
void set_listening_address(SocketAddress listening_address);
32+
[Throws=BuildError]
33+
void set_listening_addresses(sequence<SocketAddress> listening_addresses);
3334
[Throws=BuildError]
3435
LDKNode build();
3536
};
@@ -43,7 +44,7 @@ interface LDKNode {
4344
Event wait_next_event();
4445
void event_handled();
4546
PublicKey node_id();
46-
SocketAddress? listening_address();
47+
sequence<SocketAddress>? listening_addresses();
4748
[Throws=NodeError]
4849
Address new_onchain_address();
4950
[Throws=NodeError]
@@ -133,6 +134,7 @@ enum BuildError {
133134
"InvalidSeedFile",
134135
"InvalidSystemTime",
135136
"InvalidChannelMonitor",
137+
"InvalidListeningAddresses",
136138
"ReadFailed",
137139
"WriteFailed",
138140
"StoragePathAccessFailed",

bindings/python/src/ldk_node/test_ldk_node.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,13 @@ def send_to_address(address, amount_sats):
8080
return res
8181

8282

83-
def setup_node(tmp_dir, esplora_endpoint, listening_address):
83+
def setup_node(tmp_dir, esplora_endpoint, listening_addresses):
8484
config = Config()
8585
builder = Builder.from_config(config)
8686
builder.set_storage_dir_path(tmp_dir)
8787
builder.set_esplora_server(esplora_endpoint)
8888
builder.set_network(DEFAULT_TEST_NETWORK)
89-
builder.set_listening_address(listening_address)
89+
builder.set_listening_addresses(listening_addresses)
9090
return builder.build()
9191

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

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

122-
listening_address_2 = "127.0.0.1:2324"
123-
node_2 = setup_node(tmp_dir_2.name, esplora_endpoint, listening_address_2)
122+
listening_addresses_2 = ["127.0.0.1:2324"]
123+
node_2 = setup_node(tmp_dir_2.name, esplora_endpoint, listening_addresses_2)
124124
node_2.start()
125125
node_id_2 = node_2.node_id()
126126
print("Node ID 2:", node_id_2)

src/builder.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ pub enum BuildError {
9090
InvalidSystemTime,
9191
/// The a read channel monitor is invalid.
9292
InvalidChannelMonitor,
93+
/// The given listening addresses are invalid, e.g. too many were passed.
94+
InvalidListeningAddresses,
9395
/// We failed to read data from the [`KVStore`].
9496
ReadFailed,
9597
/// We failed to write data to the [`KVStore`].
@@ -115,6 +117,7 @@ impl fmt::Display for BuildError {
115117
Self::InvalidChannelMonitor => {
116118
write!(f, "Failed to watch a deserialized ChannelMonitor")
117119
}
120+
Self::InvalidListeningAddresses => write!(f, "Given listening addresses are invalid."),
118121
Self::ReadFailed => write!(f, "Failed to read from store."),
119122
Self::WriteFailed => write!(f, "Failed to write to store."),
120123
Self::StoragePathAccessFailed => write!(f, "Failed to access the given storage path."),
@@ -231,9 +234,15 @@ impl NodeBuilder {
231234
}
232235

233236
/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
234-
pub fn set_listening_address(&mut self, listening_address: SocketAddress) -> &mut Self {
235-
self.config.listening_address = Some(listening_address);
236-
self
237+
pub fn set_listening_addresses(
238+
&mut self, listening_addresses: Vec<SocketAddress>,
239+
) -> Result<&mut Self, BuildError> {
240+
if listening_addresses.len() > 100 {
241+
return Err(BuildError::InvalidListeningAddresses);
242+
}
243+
244+
self.config.listening_addresses = Some(listening_addresses);
245+
Ok(self)
237246
}
238247

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

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

393404
/// Sets the level at which [`Node`] will log messages.

src/lib.rs

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ const WALLET_KEYS_SEED_LEN: usize = 64;
207207
/// | `storage_dir_path` | /tmp/ldk_node/ |
208208
/// | `log_dir_path` | None |
209209
/// | `network` | Bitcoin |
210-
/// | `listening_address` | None |
210+
/// | `listening_addresses` | None |
211211
/// | `default_cltv_expiry_delta` | 144 |
212212
/// | `onchain_wallet_sync_interval_secs` | 80 |
213213
/// | `wallet_sync_interval_secs` | 30 |
@@ -225,8 +225,8 @@ pub struct Config {
225225
pub log_dir_path: Option<String>,
226226
/// The used Bitcoin network.
227227
pub network: Network,
228-
/// The IP address and TCP port the node will listen on.
229-
pub listening_address: Option<SocketAddress>,
228+
/// The addresses on which the node will listen for incoming connections.
229+
pub listening_addresses: Option<Vec<SocketAddress>>,
230230
/// The default CLTV expiry delta to be used for payments.
231231
pub default_cltv_expiry_delta: u32,
232232
/// The time in-between background sync attempts of the onchain wallet, in seconds.
@@ -264,7 +264,7 @@ impl Default for Config {
264264
storage_dir_path: DEFAULT_STORAGE_DIR_PATH.to_string(),
265265
log_dir_path: None,
266266
network: DEFAULT_NETWORK,
267-
listening_address: None,
267+
listening_addresses: None,
268268
default_cltv_expiry_delta: DEFAULT_CLTV_EXPIRY_DELTA,
269269
onchain_wallet_sync_interval_secs: DEFAULT_BDK_WALLET_SYNC_INTERVAL_SECS,
270270
wallet_sync_interval_secs: DEFAULT_LDK_WALLET_SYNC_INTERVAL_SECS,
@@ -490,38 +490,33 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
490490
});
491491
}
492492

493-
if let Some(listening_address) = &self.config.listening_address {
493+
if let Some(listening_addresses) = &self.config.listening_addresses {
494494
// Setup networking
495495
let peer_manager_connection_handler = Arc::clone(&self.peer_manager);
496496
let mut stop_listen = self.stop_receiver.clone();
497-
let listening_address = listening_address.clone();
498497
let listening_logger = Arc::clone(&self.logger);
499498

500-
let bind_addr = listening_address
501-
.to_socket_addrs()
502-
.map_err(|_| {
503-
log_error!(
504-
self.logger,
505-
"Unable to resolve listing address: {:?}",
506-
listening_address
507-
);
508-
Error::InvalidSocketAddress
509-
})?
510-
.next()
511-
.ok_or_else(|| {
499+
let mut bind_addrs = Vec::with_capacity(listening_addresses.len());
500+
501+
for listening_addr in listening_addresses {
502+
let resolved_address = listening_addr.to_socket_addrs().map_err(|e| {
512503
log_error!(
513504
self.logger,
514-
"Unable to resolve listing address: {:?}",
515-
listening_address
505+
"Unable to resolve listening address: {:?}. Error details: {}",
506+
listening_addr,
507+
e,
516508
);
517509
Error::InvalidSocketAddress
518510
})?;
519511

512+
bind_addrs.extend(resolved_address);
513+
}
514+
520515
runtime.spawn(async move {
521516
let listener =
522-
tokio::net::TcpListener::bind(bind_addr).await
517+
tokio::net::TcpListener::bind(&*bind_addrs).await
523518
.unwrap_or_else(|e| {
524-
log_error!(listening_logger, "Failed to bind to listen address/port - is something else already listening on it?: {}", e);
519+
log_error!(listening_logger, "Failed to bind to listen addresses/ports - is something else already listening on it?: {}", e);
525520
panic!(
526521
"Failed to bind to listen address/port - is something else already listening on it?",
527522
);
@@ -631,8 +626,13 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
631626
continue;
632627
}
633628

634-
let addresses =
635-
bcast_config.listening_address.iter().cloned().collect();
629+
let addresses = bcast_config.listening_addresses.clone().unwrap_or(Vec::new());
630+
631+
if addresses.is_empty() {
632+
// Skip if we are not listening on any addresses.
633+
continue;
634+
}
635+
636636
bcast_pm.broadcast_node_announcement([0; 3], [0; 32], addresses);
637637

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

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

789789
/// Retrieve a new on-chain/funding address.

src/test/functional_tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ fn do_channel_full_cycle<K: KVStore + Sync + Send>(
7777
node_a
7878
.connect_open_channel(
7979
node_b.node_id(),
80-
node_b.listening_address().unwrap().into(),
80+
node_b.listening_addresses().unwrap().first().unwrap().clone(),
8181
funding_amount_sat,
8282
Some(push_msat),
8383
None,
@@ -332,7 +332,7 @@ fn channel_open_fails_when_funds_insufficient() {
332332
Err(Error::InsufficientFunds),
333333
node_a.connect_open_channel(
334334
node_b.node_id(),
335-
node_b.listening_address().unwrap().into(),
335+
node_b.listening_addresses().unwrap().first().unwrap().clone(),
336336
120000,
337337
None,
338338
None,

src/test/utils.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::builder::NodeBuilder;
22
use crate::io::test_utils::TestSyncStore;
33
use crate::{Config, Node};
4+
use lightning::ln::msgs::SocketAddress;
45
use lightning::util::logger::{Level, Logger, Record};
56

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

138+
pub fn random_listening_addresses() -> Vec<SocketAddress> {
139+
let num_addresses = 2;
140+
let mut listening_addresses = Vec::with_capacity(num_addresses);
141+
142+
for _ in 0..num_addresses {
143+
let rand_port = random_port();
144+
let address: SocketAddress = format!("127.0.0.1:{}", rand_port).parse().unwrap();
145+
listening_addresses.push(address);
146+
}
147+
148+
listening_addresses
149+
}
150+
137151
pub fn random_config() -> Config {
138152
let mut config = Config::default();
139153

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

147-
let rand_port = random_port();
148-
println!("Setting random LDK listening port: {}", rand_port);
149-
let listening_address_str = format!("127.0.0.1:{}", rand_port);
150-
config.listening_address = Some(listening_address_str.parse().unwrap());
161+
let rand_listening_addresses = random_listening_addresses();
162+
println!("Setting random LDK listening addresses: {:?}", rand_listening_addresses);
163+
config.listening_addresses = Some(rand_listening_addresses);
151164

152165
config.log_level = Level::Trace;
153166

0 commit comments

Comments
 (0)