Skip to content

Commit c9ebeb6

Browse files
authored
Merge pull request #330 from enigbe/feat-set-node-alias
Set node alias
2 parents 1a6a2ca + 4fd1cb8 commit c9ebeb6

File tree

13 files changed

+775
-363
lines changed

13 files changed

+775
-363
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ A ready-to-go Lightning node library built using [LDK][ldk] and [BDK][bdk].
1111
LDK Node is a self-custodial Lightning node in library form. Its central goal is to provide a small, simple, and straightforward interface that enables users to easily set up and run a Lightning node with an integrated on-chain wallet. While minimalism is at its core, LDK Node aims to be sufficiently modular and configurable to be useful for a variety of use cases.
1212

1313
## Getting Started
14-
The primary abstraction of the library is the [`Node`][api_docs_node], which can be retrieved by setting up and configuring a [`Builder`][api_docs_builder] to your liking and calling one of the `build` methods. `Node` can then be controlled via commands such as `start`, `stop`, `connect_open_channel`, `send`, etc.
14+
The primary abstraction of the library is the [`Node`][api_docs_node], which can be retrieved by setting up and configuring a [`Builder`][api_docs_builder] to your liking and calling one of the `build` methods. `Node` can then be controlled via commands such as `start`, `stop`, `open_channel`, `open_announced_channel`, `send`, etc.
1515

1616
```rust
1717
use ldk_node::Builder;
@@ -37,7 +37,7 @@ fn main() {
3737

3838
let node_id = PublicKey::from_str("NODE_ID").unwrap();
3939
let node_addr = SocketAddress::from_str("IP_ADDR:PORT").unwrap();
40-
node.connect_open_channel(node_id, node_addr, 10000, None, None, false).unwrap();
40+
node.open_channel(node_id, node_addr, 10000, None, None).unwrap();
4141

4242
let event = node.wait_next_event();
4343
println!("EVENT: {:?}", event);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ class LibraryTest {
175175
assertEquals(100000uL, totalBalance1)
176176
assertEquals(100000uL, totalBalance2)
177177

178-
node1.connectOpenChannel(nodeId2, listenAddress2, 50000u, null, null, true)
178+
node1.openChannel(nodeId2, listenAddress2, 50000u, null, null)
179179

180180
val channelPendingEvent1 = node1.waitNextEvent()
181181
println("Got event: $channelPendingEvent1")

bindings/ldk_node.udl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ dictionary Config {
88
string? log_dir_path;
99
Network network;
1010
sequence<SocketAddress>? listening_addresses;
11+
NodeAlias? node_alias;
1112
u64 onchain_wallet_sync_interval_secs;
1213
u64 wallet_sync_interval_secs;
1314
u64 fee_rate_cache_update_interval_secs;
@@ -40,6 +41,8 @@ interface Builder {
4041
[Throws=BuildError]
4142
void set_listening_addresses(sequence<SocketAddress> listening_addresses);
4243
[Throws=BuildError]
44+
void set_node_alias(string node_alias);
45+
[Throws=BuildError]
4346
Node build();
4447
[Throws=BuildError]
4548
Node build_with_fs_store();
@@ -59,6 +62,7 @@ interface Node {
5962
void event_handled();
6063
PublicKey node_id();
6164
sequence<SocketAddress>? listening_addresses();
65+
NodeAlias? node_alias();
6266
Bolt11Payment bolt11_payment();
6367
Bolt12Payment bolt12_payment();
6468
SpontaneousPayment spontaneous_payment();
@@ -69,7 +73,9 @@ interface Node {
6973
[Throws=NodeError]
7074
void disconnect(PublicKey node_id);
7175
[Throws=NodeError]
72-
UserChannelId connect_open_channel(PublicKey node_id, SocketAddress address, u64 channel_amount_sats, u64? push_to_counterparty_msat, ChannelConfig? channel_config, boolean announce_channel);
76+
UserChannelId open_channel(PublicKey node_id, SocketAddress address, u64 channel_amount_sats, u64? push_to_counterparty_msat, ChannelConfig? channel_config);
77+
[Throws=NodeError]
78+
UserChannelId open_announced_channel(PublicKey node_id, SocketAddress address, u64 channel_amount_sats, u64? push_to_counterparty_msat, ChannelConfig? channel_config);
7379
[Throws=NodeError]
7480
void close_channel([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id);
7581
[Throws=NodeError]
@@ -202,6 +208,7 @@ enum NodeError {
202208
"InvalidNetwork",
203209
"InvalidUri",
204210
"InvalidQuantity",
211+
"InvalidNodeAlias",
205212
"DuplicatePayment",
206213
"UnsupportedCurrency",
207214
"InsufficientFunds",
@@ -232,6 +239,7 @@ enum BuildError {
232239
"InvalidSystemTime",
233240
"InvalidChannelMonitor",
234241
"InvalidListeningAddresses",
242+
"InvalidNodeAlias",
235243
"ReadFailed",
236244
"WriteFailed",
237245
"StoragePathAccessFailed",
@@ -529,3 +537,6 @@ typedef string Mnemonic;
529537

530538
[Custom]
531539
typedef string UntrustedString;
540+
541+
[Custom]
542+
typedef string NodeAlias;

bindings/python/src/ldk_node/test_ldk_node.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def test_channel_full_cycle(self):
155155
print("TOTAL 2:", total_balance_2)
156156
self.assertEqual(total_balance_2, 100000)
157157

158-
node_1.connect_open_channel(node_id_2, listening_addresses_2[0], 50000, None, None, True)
158+
node_1.open_channel(node_id_2, listening_addresses_2[0], 50000, None, None)
159159

160160
channel_pending_event_1 = node_1.wait_next_event()
161161
assert isinstance(channel_pending_event_1, Event.CHANNEL_PENDING)

src/builder.rs

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use lightning::chain::{chainmonitor, BestBlock, Watch};
3232
use lightning::ln::channelmanager::{self, ChainParameters, ChannelManagerReadArgs};
3333
use lightning::ln::msgs::{RoutingMessageHandler, SocketAddress};
3434
use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler};
35+
use lightning::routing::gossip::NodeAlias;
3536
use lightning::routing::router::DefaultRouter;
3637
use lightning::routing::scoring::{
3738
ProbabilisticScorer, ProbabilisticScoringDecayParameters, ProbabilisticScoringFeeParameters,
@@ -109,7 +110,7 @@ impl Default for LiquiditySourceConfig {
109110
/// An error encountered during building a [`Node`].
110111
///
111112
/// [`Node`]: crate::Node
112-
#[derive(Debug, Clone)]
113+
#[derive(Debug, Clone, PartialEq)]
113114
pub enum BuildError {
114115
/// The given seed bytes are invalid, e.g., have invalid length.
115116
InvalidSeedBytes,
@@ -121,6 +122,8 @@ pub enum BuildError {
121122
InvalidChannelMonitor,
122123
/// The given listening addresses are invalid, e.g. too many were passed.
123124
InvalidListeningAddresses,
125+
/// The provided alias is invalid.
126+
InvalidNodeAlias,
124127
/// We failed to read data from the [`KVStore`].
125128
///
126129
/// [`KVStore`]: lightning::util::persist::KVStore
@@ -159,6 +162,7 @@ impl fmt::Display for BuildError {
159162
Self::KVStoreSetupFailed => write!(f, "Failed to setup KVStore."),
160163
Self::WalletSetupFailed => write!(f, "Failed to setup onchain wallet."),
161164
Self::LoggerSetupFailed => write!(f, "Failed to setup the logger."),
165+
Self::InvalidNodeAlias => write!(f, "Given node alias is invalid."),
162166
}
163167
}
164168
}
@@ -303,6 +307,16 @@ impl NodeBuilder {
303307
Ok(self)
304308
}
305309

310+
/// Sets the alias the [`Node`] will use in its announcement.
311+
///
312+
/// The provided alias must be a valid UTF-8 string.
313+
pub fn set_node_alias(&mut self, node_alias: String) -> Result<&mut Self, BuildError> {
314+
let node_alias = sanitize_alias(&node_alias)?;
315+
316+
self.config.node_alias = Some(node_alias);
317+
Ok(self)
318+
}
319+
306320
/// Sets the level at which [`Node`] will log messages.
307321
pub fn set_log_level(&mut self, level: LogLevel) -> &mut Self {
308322
self.config.log_level = level;
@@ -501,6 +515,11 @@ impl ArcedNodeBuilder {
501515
self.inner.write().unwrap().set_listening_addresses(listening_addresses).map(|_| ())
502516
}
503517

518+
/// Sets the node alias.
519+
pub fn set_node_alias(&self, node_alias: String) -> Result<(), BuildError> {
520+
self.inner.write().unwrap().set_node_alias(node_alias).map(|_| ())
521+
}
522+
504523
/// Sets the level at which [`Node`] will log messages.
505524
pub fn set_log_level(&self, level: LogLevel) {
506525
self.inner.write().unwrap().set_log_level(level);
@@ -1050,3 +1069,58 @@ fn seed_bytes_from_config(
10501069
},
10511070
}
10521071
}
1072+
1073+
/// Sanitize the user-provided node alias to ensure that it is a valid protocol-specified UTF-8 string.
1074+
pub(crate) fn sanitize_alias(alias_str: &str) -> Result<NodeAlias, BuildError> {
1075+
let alias = alias_str.trim();
1076+
1077+
// Alias must be 32-bytes long or less.
1078+
if alias.as_bytes().len() > 32 {
1079+
return Err(BuildError::InvalidNodeAlias);
1080+
}
1081+
1082+
let mut bytes = [0u8; 32];
1083+
bytes[..alias.as_bytes().len()].copy_from_slice(alias.as_bytes());
1084+
Ok(NodeAlias(bytes))
1085+
}
1086+
1087+
#[cfg(test)]
1088+
mod tests {
1089+
use super::{sanitize_alias, BuildError, NodeAlias};
1090+
1091+
#[test]
1092+
fn sanitize_empty_node_alias() {
1093+
// Empty node alias
1094+
let alias = "";
1095+
let mut buf = [0u8; 32];
1096+
buf[..alias.as_bytes().len()].copy_from_slice(alias.as_bytes());
1097+
1098+
let expected_node_alias = NodeAlias([0; 32]);
1099+
let node_alias = sanitize_alias(alias).unwrap();
1100+
assert_eq!(node_alias, expected_node_alias);
1101+
}
1102+
1103+
#[test]
1104+
fn sanitize_alias_with_sandwiched_null() {
1105+
// Alias with emojis
1106+
let alias = "I\u{1F496}LDK-Node!";
1107+
let mut buf = [0u8; 32];
1108+
buf[..alias.as_bytes().len()].copy_from_slice(alias.as_bytes());
1109+
let expected_alias = NodeAlias(buf);
1110+
1111+
let user_provided_alias = "I\u{1F496}LDK-Node!\0\u{26A1}";
1112+
let node_alias = sanitize_alias(user_provided_alias).unwrap();
1113+
1114+
let node_alias_display = format!("{}", node_alias);
1115+
1116+
assert_eq!(alias, &node_alias_display);
1117+
assert_ne!(expected_alias, node_alias);
1118+
}
1119+
1120+
#[test]
1121+
fn sanitize_alias_gt_32_bytes() {
1122+
let alias = "This is a string longer than thirty-two bytes!"; // 46 bytes
1123+
let node = sanitize_alias(alias);
1124+
assert_eq!(node.err().unwrap(), BuildError::InvalidNodeAlias);
1125+
}
1126+
}

src/config.rs

Lines changed: 121 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use crate::payment::SendingParameters;
99

1010
use lightning::ln::msgs::SocketAddress;
11+
use lightning::routing::gossip::NodeAlias;
1112
use lightning::util::config::UserConfig;
1213
use lightning::util::logger::Level as LogLevel;
1314

@@ -86,6 +87,7 @@ pub(crate) const WALLET_KEYS_SEED_LEN: usize = 64;
8687
/// | `log_dir_path` | None |
8788
/// | `network` | Bitcoin |
8889
/// | `listening_addresses` | None |
90+
/// | `node_alias` | None |
8991
/// | `default_cltv_expiry_delta` | 144 |
9092
/// | `onchain_wallet_sync_interval_secs` | 80 |
9193
/// | `wallet_sync_interval_secs` | 30 |
@@ -110,7 +112,15 @@ pub struct Config {
110112
/// The used Bitcoin network.
111113
pub network: Network,
112114
/// The addresses on which the node will listen for incoming connections.
115+
///
116+
/// **Note**: Node announcements will only be broadcast if the `node_alias` and the
117+
/// `listening_addresses` are set.
113118
pub listening_addresses: Option<Vec<SocketAddress>>,
119+
/// The node alias to be used in announcements.
120+
///
121+
/// **Note**: Node announcements will only be broadcast if the `node_alias` and the
122+
/// `listening_addresses` are set.
123+
pub node_alias: Option<NodeAlias>,
114124
/// The time in-between background sync attempts of the onchain wallet, in seconds.
115125
///
116126
/// **Note:** A minimum of 10 seconds is always enforced.
@@ -180,6 +190,7 @@ impl Default for Config {
180190
log_level: DEFAULT_LOG_LEVEL,
181191
anchor_channels_config: Some(AnchorChannelsConfig::default()),
182192
sending_parameters: None,
193+
node_alias: None,
183194
}
184195
}
185196
}
@@ -265,17 +276,124 @@ pub fn default_config() -> Config {
265276
Config::default()
266277
}
267278

279+
/// Specifies reasons why a channel cannot be announced.
280+
#[derive(Debug, PartialEq)]
281+
pub(crate) enum ChannelAnnouncementBlocker {
282+
/// The node alias is not set.
283+
MissingNodeAlias,
284+
/// The listening addresses are not set.
285+
MissingListeningAddresses,
286+
// This listening addresses is set but the vector is empty.
287+
EmptyListeningAddresses,
288+
}
289+
290+
/// Enumeration defining the announcement status of a channel.
291+
#[derive(Debug, PartialEq)]
292+
pub(crate) enum ChannelAnnouncementStatus {
293+
/// The channel is announceable.
294+
Announceable,
295+
/// The channel is not announceable.
296+
Unannounceable(ChannelAnnouncementBlocker),
297+
}
298+
299+
/// Checks if a node is can announce a channel based on the configured values of both the node's
300+
/// alias and its listening addresses.
301+
///
302+
/// If either of them is unset, the node cannot announce the channel. This ability to announce/
303+
/// unannounce a channel is codified with `ChannelAnnouncementStatus`
304+
pub(crate) fn can_announce_channel(config: &Config) -> ChannelAnnouncementStatus {
305+
if config.node_alias.is_none() {
306+
return ChannelAnnouncementStatus::Unannounceable(
307+
ChannelAnnouncementBlocker::MissingNodeAlias,
308+
);
309+
}
310+
311+
match &config.listening_addresses {
312+
None => ChannelAnnouncementStatus::Unannounceable(
313+
ChannelAnnouncementBlocker::MissingListeningAddresses,
314+
),
315+
Some(addresses) if addresses.is_empty() => ChannelAnnouncementStatus::Unannounceable(
316+
ChannelAnnouncementBlocker::EmptyListeningAddresses,
317+
),
318+
Some(_) => ChannelAnnouncementStatus::Announceable,
319+
}
320+
}
321+
268322
pub(crate) fn default_user_config(config: &Config) -> UserConfig {
269323
// Initialize the default config values.
270324
//
271-
// Note that methods such as Node::connect_open_channel might override some of the values set
272-
// here, e.g. the ChannelHandshakeConfig, meaning these default values will mostly be relevant
273-
// for inbound channels.
325+
// Note that methods such as Node::open_channel and Node::open_announced_channel might override
326+
// some of the values set here, e.g. the ChannelHandshakeConfig, meaning these default values
327+
// will mostly be relevant for inbound channels.
274328
let mut user_config = UserConfig::default();
275329
user_config.channel_handshake_limits.force_announced_channel_preference = false;
276330
user_config.manually_accept_inbound_channels = true;
277331
user_config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx =
278332
config.anchor_channels_config.is_some();
279333

334+
match can_announce_channel(config) {
335+
ChannelAnnouncementStatus::Announceable => (),
336+
ChannelAnnouncementStatus::Unannounceable(_) => {
337+
user_config.accept_forwards_to_priv_channels = false;
338+
user_config.channel_handshake_config.announced_channel = false;
339+
user_config.channel_handshake_limits.force_announced_channel_preference = true;
340+
},
341+
}
342+
280343
user_config
281344
}
345+
346+
#[cfg(test)]
347+
mod tests {
348+
use std::str::FromStr;
349+
350+
use crate::config::ChannelAnnouncementStatus;
351+
352+
use super::can_announce_channel;
353+
use super::Config;
354+
use super::NodeAlias;
355+
use super::SocketAddress;
356+
357+
#[test]
358+
fn node_can_announce_channel() {
359+
// Default configuration with node alias and listening addresses unset
360+
let mut node_config = Config::default();
361+
assert_eq!(
362+
can_announce_channel(&node_config),
363+
ChannelAnnouncementStatus::Unannounceable(
364+
crate::config::ChannelAnnouncementBlocker::MissingNodeAlias
365+
)
366+
);
367+
368+
// Set node alias with listening addresses unset
369+
let alias_frm_str = |alias: &str| {
370+
let mut bytes = [0u8; 32];
371+
bytes[..alias.as_bytes().len()].copy_from_slice(alias.as_bytes());
372+
NodeAlias(bytes)
373+
};
374+
node_config.node_alias = Some(alias_frm_str("LDK_Node"));
375+
assert_eq!(
376+
can_announce_channel(&node_config),
377+
ChannelAnnouncementStatus::Unannounceable(
378+
crate::config::ChannelAnnouncementBlocker::MissingListeningAddresses
379+
)
380+
);
381+
382+
// Set node alias with an empty list of listening addresses
383+
node_config.listening_addresses = Some(vec![]);
384+
assert_eq!(
385+
can_announce_channel(&node_config),
386+
ChannelAnnouncementStatus::Unannounceable(
387+
crate::config::ChannelAnnouncementBlocker::EmptyListeningAddresses
388+
)
389+
);
390+
391+
// Set node alias with a non-empty list of listening addresses
392+
let socket_address =
393+
SocketAddress::from_str("localhost:8000").expect("Socket address conversion failed.");
394+
if let Some(ref mut addresses) = node_config.listening_addresses {
395+
addresses.push(socket_address);
396+
}
397+
assert_eq!(can_announce_channel(&node_config), ChannelAnnouncementStatus::Announceable);
398+
}
399+
}

0 commit comments

Comments
 (0)