Skip to content

Define NodeAlias struct and Display impl #1544

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 16, 2022
Merged
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
74 changes: 71 additions & 3 deletions lightning/src/routing/gossip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,7 @@ pub struct NodeAnnouncementInfo {
/// Moniker assigned to the node.
/// May be invalid or malicious (eg control chars),
/// should not be exposed to the user.
pub alias: [u8; 32],
pub alias: NodeAlias,
/// Internet-level addresses via which one can connect to the node
pub addresses: Vec<NetAddress>,
/// An initial announcement of the node
Expand All @@ -923,6 +923,51 @@ impl_writeable_tlv_based!(NodeAnnouncementInfo, {
(10, addresses, vec_type),
});

/// A user-defined name for a node, which may be used when displaying the node in a graph.
///
/// Since node aliases are provided by third parties, they are a potential avenue for injection
/// attacks. Care must be taken when processing.
#[derive(Clone, Debug, PartialEq)]
pub struct NodeAlias(pub [u8; 32]);

impl fmt::Display for NodeAlias {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let control_symbol = core::char::REPLACEMENT_CHARACTER;
let first_null = self.0.iter().position(|b| *b == 0).unwrap_or(self.0.len());
let bytes = self.0.split_at(first_null).0;
match core::str::from_utf8(bytes) {
Ok(alias) => {
for c in alias.chars() {
let mut bytes = [0u8; 4];
let c = if !c.is_control() { c } else { control_symbol };
f.write_str(c.encode_utf8(&mut bytes))?;
}
},
Err(_) => {
for c in bytes.iter().map(|b| *b as char) {
// Display printable ASCII characters
let mut bytes = [0u8; 4];
let c = if c >= '\x20' && c <= '\x7e' { c } else { control_symbol };
f.write_str(c.encode_utf8(&mut bytes))?;
}
},
};
Ok(())
}
}

impl Writeable for NodeAlias {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.0.write(w)
}
}

impl Readable for NodeAlias {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
Ok(NodeAlias(Readable::read(r)?))
}
}

#[derive(Clone, Debug, PartialEq)]
/// Details about a node in the network, known from the network announcement.
pub struct NodeInfo {
Expand Down Expand Up @@ -1126,7 +1171,7 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
features: msg.features.clone(),
last_update: msg.timestamp,
rgb: msg.rgb,
alias: msg.alias,
alias: NodeAlias(msg.alias),
addresses: msg.addresses.clone(),
announcement_message: if should_relay { full_msg.cloned() } else { None },
});
Expand Down Expand Up @@ -1627,7 +1672,7 @@ mod tests {
use chain;
use ln::PaymentHash;
use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
use routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate, MAX_EXCESS_BYTES_FOR_RELAY};
use routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate, NodeAlias, MAX_EXCESS_BYTES_FOR_RELAY};
use ln::msgs::{Init, OptionalField, RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement,
UnsignedChannelAnnouncement, ChannelAnnouncement, UnsignedChannelUpdate, ChannelUpdate,
ReplyChannelRange, QueryChannelRange, QueryShortChannelIds, MAX_VALUE_MSAT};
Expand Down Expand Up @@ -2731,6 +2776,29 @@ mod tests {
});
assert!(result.is_err());
}

#[test]
fn displays_node_alias() {
let format_str_alias = |alias: &str| {
let mut bytes = [0u8; 32];
bytes[..alias.as_bytes().len()].copy_from_slice(alias.as_bytes());
format!("{}", NodeAlias(bytes))
};

assert_eq!(format_str_alias("I\u{1F496}LDK! \u{26A1}"), "I\u{1F496}LDK! \u{26A1}");
assert_eq!(format_str_alias("I\u{1F496}LDK!\0\u{26A1}"), "I\u{1F496}LDK!");
assert_eq!(format_str_alias("I\u{1F496}LDK!\t\u{26A1}"), "I\u{1F496}LDK!\u{FFFD}\u{26A1}");

let format_bytes_alias = |alias: &[u8]| {
let mut bytes = [0u8; 32];
bytes[..alias.len()].copy_from_slice(alias);
format!("{}", NodeAlias(bytes))
};

assert_eq!(format_bytes_alias(b"\xFFI <heart> LDK!"), "\u{FFFD}I <heart> LDK!");
assert_eq!(format_bytes_alias(b"\xFFI <heart>\0LDK!"), "\u{FFFD}I <heart>");
assert_eq!(format_bytes_alias(b"\xFFI <heart>\tLDK!"), "\u{FFFD}I <heart>\u{FFFD}LDK!");
}
}

#[cfg(all(test, feature = "_bench_unstable"))]
Expand Down