Skip to content

Commit 12dfc1a

Browse files
committed
feat: sanitize and set node alias
What this commit does: Implements a method `set_node_alias` on NodeBuilder to allow callers customize/set the value of the node alias. This method sanitizes the user-provided alias by ensuring the following: + Node alias is UTF-8-encoded String + Node alias is non-empty + Node alias cannot exceed 32 bytes + Node alias is only valid up to the first null byte. Every character after the null byte is discraded Additionally, a test case is provided to cover sanitizing empty node alias, as well as an alias with emojis (copied and modified from rust-lightning) and a sandwiched null byte.
1 parent 1a6a2ca commit 12dfc1a

File tree

2 files changed

+88
-1
lines changed

2 files changed

+88
-1
lines changed

src/builder.rs

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ impl Default for LiquiditySourceConfig {
109109
/// An error encountered during building a [`Node`].
110110
///
111111
/// [`Node`]: crate::Node
112-
#[derive(Debug, Clone)]
112+
#[derive(Debug, Clone, PartialEq)]
113113
pub enum BuildError {
114114
/// The given seed bytes are invalid, e.g., have invalid length.
115115
InvalidSeedBytes,
@@ -139,6 +139,8 @@ pub enum BuildError {
139139
WalletSetupFailed,
140140
/// We failed to setup the logger.
141141
LoggerSetupFailed,
142+
/// The provided alias is invalid
143+
InvalidNodeAlias(String),
142144
}
143145

144146
impl fmt::Display for BuildError {
@@ -159,6 +161,9 @@ impl fmt::Display for BuildError {
159161
Self::KVStoreSetupFailed => write!(f, "Failed to setup KVStore."),
160162
Self::WalletSetupFailed => write!(f, "Failed to setup onchain wallet."),
161163
Self::LoggerSetupFailed => write!(f, "Failed to setup the logger."),
164+
Self::InvalidNodeAlias(ref reason) => {
165+
write!(f, "Given node alias is invalid: {}", reason)
166+
},
162167
}
163168
}
164169
}
@@ -309,6 +314,17 @@ impl NodeBuilder {
309314
self
310315
}
311316

317+
/// Sets the alias the [`Node`] will use in its announcement. The provided
318+
/// alias must be a valid UTF-8 string.
319+
pub fn set_node_alias<T: Into<String>>(
320+
&mut self, node_alias: T,
321+
) -> Result<&mut Self, BuildError> {
322+
let node_alias = sanitize_alias(node_alias).map_err(|e| e)?;
323+
324+
self.config.node_alias = Some(node_alias);
325+
Ok(self)
326+
}
327+
312328
/// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
313329
/// previously configured.
314330
pub fn build(&self) -> Result<Node, BuildError> {
@@ -1050,3 +1066,68 @@ fn seed_bytes_from_config(
10501066
},
10511067
}
10521068
}
1069+
1070+
/// Sanitize the user-provided node alias to ensure that it is a valid protocol-specified UTF-8 string.
1071+
fn sanitize_alias<T: Into<String>>(node_alias: T) -> Result<String, BuildError> {
1072+
// Alias is convertible into UTF-8 encoded string
1073+
let node_alias: String = node_alias.into();
1074+
let alias = node_alias.trim();
1075+
1076+
// Alias is non-empty
1077+
if alias.is_empty() {
1078+
return Err(BuildError::InvalidNodeAlias("Node alias cannot be empty.".to_string()));
1079+
}
1080+
1081+
// Alias valid up to first null byte
1082+
let first_null = alias.as_bytes().iter().position(|b| *b == 0).unwrap_or(alias.len());
1083+
let actual_alias = alias.split_at(first_null).0;
1084+
1085+
// Alias must be 32-bytes long or less
1086+
if actual_alias.as_bytes().len() > 32 {
1087+
return Err(BuildError::InvalidNodeAlias("Node alias cannot exceed 32 bytes.".to_string()));
1088+
}
1089+
1090+
Ok(actual_alias.to_string())
1091+
}
1092+
1093+
#[cfg(test)]
1094+
mod tests {
1095+
use crate::{BuildError, Node};
1096+
1097+
use super::NodeBuilder;
1098+
1099+
fn create_node_with_alias<T: Into<String>>(alias: T) -> Result<Node, BuildError> {
1100+
NodeBuilder::new().set_node_alias(&alias.into())?.build()
1101+
}
1102+
1103+
#[test]
1104+
fn empty_node_alias() {
1105+
// Empty node alias
1106+
let alias = "";
1107+
let node = create_node_with_alias(alias);
1108+
assert_eq!(
1109+
node.err().unwrap(),
1110+
BuildError::InvalidNodeAlias("Node alias cannot be empty.".to_string())
1111+
);
1112+
}
1113+
1114+
#[test]
1115+
fn node_alias_with_sandwiched_null() {
1116+
// Alias with emojis
1117+
let expected_alias = "I\u{1F496}LDK-Node!";
1118+
let user_provided_alias = "I\u{1F496}LDK-Node!\0\u{26A1}";
1119+
let node = create_node_with_alias(user_provided_alias).unwrap();
1120+
1121+
assert_eq!(expected_alias, node.config().node_alias.unwrap());
1122+
}
1123+
1124+
#[test]
1125+
fn node_alias_longer_than_32_bytes() {
1126+
let alias = "This is a string longer than thirty-two bytes!"; // 46 bytes
1127+
let node = create_node_with_alias(alias);
1128+
assert_eq!(
1129+
node.err().unwrap(),
1130+
BuildError::InvalidNodeAlias("Node alias cannot exceed 32 bytes.".to_string())
1131+
);
1132+
}
1133+
}

src/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ pub struct Config {
163163
/// **Note:** If unset, default parameters will be used, and you will be able to override the
164164
/// parameters on a per-payment basis in the corresponding method calls.
165165
pub sending_parameters: Option<SendingParameters>,
166+
/// The node alias to be used in announcements.
167+
///
168+
/// **Note**: This is required if, alongside a valid public socket address, node announcements
169+
/// are to be broadcast.
170+
pub node_alias: Option<String>,
166171
}
167172

168173
impl Default for Config {
@@ -180,6 +185,7 @@ impl Default for Config {
180185
log_level: DEFAULT_LOG_LEVEL,
181186
anchor_channels_config: Some(AnchorChannelsConfig::default()),
182187
sending_parameters: None,
188+
node_alias: None,
183189
}
184190
}
185191
}

0 commit comments

Comments
 (0)