ln_types/
node_id.rs

1//! Node identifier (encoded pubkey)
2//!
3//! This module provides the [`NodeId`] type and the related error types.
4
5use core::convert::{TryFrom, TryInto};
6use core::str::FromStr;
7use core::fmt;
8
9#[cfg(feature = "alloc")]
10use alloc::{boxed::Box, string::String, vec::Vec};
11
12/// Byte representation of a node identifier.
13///
14/// This type is used when referring to nodes without doing cryptographic operations.
15/// It can be used in search algorithms, LN explorers, manager UIs etc.
16/// By avoiding cryptography it is significantly more performant but may make debugging harder.
17/// It is therefore recommended to perform checking at system boundaries where performance is not
18/// very important - e.g. user inputs.
19///
20/// Despite this not being a guaranteed point on the curve it still performs cheap basic sanity
21/// check: whether the key begins with 0x02 or 0x03.
22///
23/// ## Example
24///
25/// ```
26/// let marvin_str = "029ef8ee0ba895e2807ac1df1987a7888116c468e70f42e7b089e06811b0e45482";
27/// let marvin = marvin_str.parse::<ln_types::NodeId>().unwrap();
28/// assert_eq!(marvin.to_string(), marvin_str);
29/// ```
30#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
31pub struct NodeId([u8; 33]);
32
33impl NodeId {
34    /// Creates `NodeId` from raw byte representation.
35    #[inline]
36    pub fn from_raw_bytes(bytes: [u8; 33]) -> Result<Self, InvalidNodeId> {
37        if bytes[0] == 0x02 || bytes[0] == 0x03 {
38            Ok(NodeId(bytes))
39        } else {
40            Err(InvalidNodeId { bad_byte: bytes[0], })
41        }
42    }
43
44    /// Puts the byte representation into `Vec<u8>`.
45    ///
46    /// This is meant for convenience around APIs that require `Vec<u8>`. Since it allocates it's
47    /// best to avoid it if possible.
48    #[cfg(feature = "alloc")]
49    pub fn to_vec(self) -> Vec<u8> {
50        self.0.to_vec()
51    }
52
53    /// Convenience conversion to byte array.
54    ///
55    /// This can be used instead of `From` to avoid inference issues.
56    pub fn to_array(self) -> [u8; 33] {
57        self.0
58    }
59
60    /// Internal monomorphic parsing method.
61    ///
62    /// This should improve codegen without requiring allocations.
63    pub(crate) fn parse_raw(s: &str) -> Result<Self, ParseErrorInner> {
64        fn decode_digit(digit: u8, pos: usize, s: &str) -> Result<u8, ParseErrorInner> {
65            match digit {
66                b'0'..=b'9' => Ok(digit - b'0'),
67                b'a'..=b'f' => Ok(digit - b'a' + 10),
68                b'A'..=b'F' => Ok(digit - b'A' + 10),
69                _ => Err(ParseErrorInner::Char { pos, c: s.chars().nth(pos).unwrap(), }),
70            }
71        }
72
73        let mut result = [0; 33];
74
75        if s.len() != 66 {
76            return Err(ParseErrorInner::Length)
77        }
78
79        for ((i, pair), dst) in s.as_bytes().chunks_exact(2).enumerate().zip(&mut result) {
80            *dst = decode_digit(pair[0], i * 2, s)? * 16 + decode_digit(pair[1], i * 2 + 1, s)?;
81        }
82
83        Self::from_raw_bytes(result).map_err(Into::into)
84    }
85
86    /// Generic wrapper for parsing that is used to implement parsing from multiple types.
87    #[cfg(feature = "alloc")]
88    #[inline]
89    fn internal_parse<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, ParseError> {
90        Self::parse_raw(s.as_ref()).map_err(|error| ParseError {
91            input: s.into(),
92            reason: error,
93        })
94    }
95
96    #[cfg(not(feature = "alloc"))]
97    #[inline]
98    fn internal_parse<S: AsRef<str>>(s: S) -> Result<Self, ParseError> {
99        Self::parse_raw(s.as_ref()).map_err(|error| ParseError {
100            reason: error,
101        })
102    }
103    /// Writes the fill character required number of times.
104    #[cfg(not(feature = "hex-conservative"))]
105    fn prefill(&self, f: &mut fmt::Formatter) -> fmt::Result {
106        use fmt::Write;
107
108        if let Some(width) = f.width() {
109            for _ in 0..width.saturating_sub(66) {
110                f.write_char(f.fill())?;
111            }
112        }
113        Ok(())
114    }
115}
116
117/// Shows `NodeId` as hex
118impl fmt::Display for NodeId {
119    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120        fmt::LowerHex::fmt(self, f)
121    }
122}
123
124/// Same as Display
125impl fmt::Debug for NodeId {
126    #[inline]
127    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
128        fmt::Display::fmt(self, f)
129    }
130}
131
132/// Same as Display
133impl fmt::LowerHex for NodeId {
134    #[inline]
135    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136        #[cfg(feature = "hex-conservative")]
137        {
138            hex_conservative::fmt_hex_exact!(f, 33, &self.0, hex_conservative::Case::Lower)
139        }
140
141        #[cfg(not(feature = "hex-conservative"))]
142        {
143            self.prefill(f)?;
144            for byte in &self.0 {
145                write!(f, "{:02x}", byte)?;
146            }
147            Ok(())
148        }
149    }
150}
151
152/// As `Display` but with upper-case letters
153impl fmt::UpperHex for NodeId {
154    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
155        #[cfg(feature = "hex-conservative")]
156        {
157            hex_conservative::fmt_hex_exact!(f, 33, &self.0, hex_conservative::Case::Upper)
158        }
159
160        #[cfg(not(feature = "hex-conservative"))]
161        {
162            self.prefill(f)?;
163            for byte in &self.0 {
164                write!(f, "{:02X}", byte)?;
165            }
166            Ok(())
167        }
168    }
169}
170
171/// Expects hex representation
172impl FromStr for NodeId {
173    type Err = ParseError;
174
175    #[inline]
176    fn from_str(s: &str) -> Result<Self, Self::Err> {
177        Self::internal_parse(s)
178    }
179}
180
181/// Expects hex representation
182impl<'a> TryFrom<&'a str> for NodeId {
183    type Error = ParseError;
184
185    #[inline]
186    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
187        Self::internal_parse(s)
188    }
189}
190
191/// Expects hex representation
192#[cfg(feature = "alloc")]
193impl TryFrom<String> for NodeId {
194    type Error = ParseError;
195
196    #[inline]
197    fn try_from(s: String) -> Result<Self, Self::Error> {
198        Self::internal_parse(s)
199    }
200}
201
202/// Expects hex representation
203#[cfg(feature = "alloc")]
204impl TryFrom<Box<str>> for NodeId {
205    type Error = ParseError;
206
207    #[inline]
208    fn try_from(s: Box<str>) -> Result<Self, Self::Error> {
209        Self::internal_parse(s)
210    }
211}
212
213impl<'a> TryFrom<&'a [u8]> for NodeId {
214    type Error = DecodeError;
215
216    #[inline]
217    fn try_from(slice: &'a [u8]) -> Result<Self, Self::Error> {
218        let bytes = slice.try_into()
219            .map_err(|_| DecodeError { error: DecodeErrorInner::InvalidLen(slice.len()) })?;
220
221        NodeId::from_raw_bytes(bytes).map_err(Into::into)
222    }
223}
224
225#[cfg(feature = "alloc")]
226impl TryFrom<Vec<u8>> for NodeId {
227    type Error = DecodeError;
228
229    #[inline]
230    fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
231        (*vec).try_into()
232    }
233}
234
235#[cfg(feature = "alloc")]
236impl TryFrom<Box<[u8]>> for NodeId {
237    type Error = DecodeError;
238
239    #[inline]
240    fn try_from(slice: Box<[u8]>) -> Result<Self, Self::Error> {
241        (*slice).try_into()
242    }
243}
244
245impl From<NodeId> for [u8; 33] {
246    fn from(value: NodeId) -> Self {
247        value.0
248    }
249}
250
251impl AsRef<[u8; 33]> for NodeId {
252    fn as_ref(&self) -> &[u8; 33] {
253        &self.0
254    }
255}
256
257impl AsRef<[u8]> for NodeId {
258    fn as_ref(&self) -> &[u8] {
259        &self.0
260    }
261}
262
263impl core::borrow::Borrow<[u8; 33]> for NodeId {
264    fn borrow(&self) -> &[u8; 33] {
265        &self.0
266    }
267}
268
269impl core::borrow::Borrow<[u8]> for NodeId {
270    fn borrow(&self) -> &[u8] {
271        &self.0
272    }
273}
274
275/// Error returned when decoding raw bytes fails
276///
277/// **Important: consumer code MUST NOT match on this using `DecodeError { .. }` syntax.
278#[derive(Debug, Clone)]
279pub struct DecodeError {
280    error: DecodeErrorInner,
281}
282
283#[derive(Debug, Clone)]
284enum DecodeErrorInner {
285    InvalidLen(usize),
286    InvalidNodeId(InvalidNodeId),
287}
288
289impl From<InvalidNodeId> for DecodeError {
290    fn from(value: InvalidNodeId) -> Self {
291        DecodeError {
292            error: DecodeErrorInner::InvalidNodeId(value),
293        }
294    }
295}
296
297
298impl fmt::Display for DecodeError {
299    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
300        match &self.error {
301            DecodeErrorInner::InvalidLen(len) => write!(f, "invalid length {} bytes, the lenght must be 33 bytes", len),
302            DecodeErrorInner::InvalidNodeId(error) => write_err!(f, "invalid node ID"; error),
303        }
304    }
305}
306
307#[cfg(feature = "std")]
308impl std::error::Error for DecodeError {
309    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
310        match &self.error {
311            DecodeErrorInner::InvalidLen(_) => None,
312            DecodeErrorInner::InvalidNodeId(error) => Some(error),
313        }
314    }
315}
316
317/// Error returned when parsing text representation fails.
318///
319/// **Important: consumer code MUST NOT match on this using `ParseError { .. }` syntax.
320#[derive(Debug, Clone)]
321pub struct ParseError {
322    /// The string that was attempted to be parsed
323    #[cfg(feature = "alloc")]
324    input: String,
325    /// Information about what exactly went wrong
326    reason: ParseErrorInner,
327}
328
329impl fmt::Display for ParseError {
330    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
331        write_err!(f, "failed to parse{} Lightning Network node ID", opt_fmt!("alloc", format_args!(" '{}' as", &self.input)); &self.reason)
332    }
333}
334
335#[cfg(feature = "std")]
336impl std::error::Error for ParseError {
337    #[inline]
338    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
339        Some(&self.reason)
340    }
341}
342
343/// Details about the error.
344///
345/// This is private to avoid committing to a representation.
346#[derive(Debug, Clone)]
347pub(crate) enum ParseErrorInner {
348    /// Length != 66 chars
349    Length,
350    Char { pos: usize, c: char, },
351    NodeId(InvalidNodeId),
352}
353
354impl fmt::Display for ParseErrorInner {
355    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
356        match self {
357            ParseErrorInner::Length => f.write_str("invalid length (must be 66 chars)"),
358            ParseErrorInner::Char { c, pos, } => write!(f, "invalid character '{}' at position {} (must be hex digit)", c, pos),
359            ParseErrorInner::NodeId(error) => write_err!(f, "invalid node ID"; error),
360        }
361    }
362}
363
364#[cfg(feature = "std")]
365impl std::error::Error for ParseErrorInner {
366    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
367        match self {
368            ParseErrorInner::Length | ParseErrorInner::Char { .. } => None,
369            ParseErrorInner::NodeId(error) => Some(error),
370        }
371    }
372}
373
374impl From<InvalidNodeId> for ParseErrorInner {
375    fn from(value: InvalidNodeId) -> Self {
376        ParseErrorInner::NodeId(value)
377    }
378}
379
380/// Error returned when attempting to convert bytes to `NodeId`
381///
382/// Conversions to `NodeId` perform a cheap basic sanity check and return this error if it doesn't
383/// pass.
384///
385/// **Important: consumer code MUST NOT match on this using `InvalidNodeId { .. }` syntax.
386#[derive(Debug, Clone)]
387pub struct InvalidNodeId {
388    bad_byte: u8,
389}
390
391impl fmt::Display for InvalidNodeId {
392    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
393        // We currently only detect zeroth byte
394        write!(f, "invalid zeroth byte 0x{:02x}", self.bad_byte)?;
395        Ok(())
396    }
397}
398
399#[cfg(feature = "std")]
400impl std::error::Error for InvalidNodeId {}
401
402/// Implementation of `parse_arg::ParseArg` trait
403#[cfg(feature = "parse_arg")]
404mod parse_arg_impl {
405    use core::fmt;
406    use super::NodeId;
407
408    impl parse_arg::ParseArgFromStr for NodeId {
409        fn describe_type<W: fmt::Write>(mut writer: W) -> fmt::Result {
410            writer.write_str("a hex-encoded LN node ID (66 hex digits/33 bytes)")
411        }
412    }
413}
414
415/// Implementations of `serde` traits
416#[cfg(feature = "serde")]
417mod serde_impl {
418    use core::fmt;
419    use super::NodeId;
420    use serde::{Serialize, Deserialize, Serializer, Deserializer, de::{Visitor, Error}};
421    use core::convert::TryFrom;
422
423    /// Visitor for human-readable formats
424    struct HRVisitor;
425
426    impl<'de> Visitor<'de> for HRVisitor {
427        type Value = NodeId;
428
429        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
430            formatter.write_str("a 66 digits long hex string")
431        }
432
433        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where E: Error {
434            use super::ParseErrorInner;
435
436            NodeId::parse_raw(v).map_err(|error| {
437                match error {
438                    ParseErrorInner::Length => E::invalid_length(v.len(), &"66 hex digits beginning with 02 or 03"),
439                    ParseErrorInner::Char { c, pos: _, } => E::invalid_value(serde::de::Unexpected::Char(c), &"a hex digit"),
440                    ParseErrorInner::NodeId(error) => E::invalid_value(serde::de::Unexpected::Bytes(&[error.bad_byte]), &"02 or 03"),
441                }
442            })
443        }
444    }
445
446    /// Visitor for non-human-readable (binary) formats
447    struct BytesVisitor;
448
449    impl<'de> Visitor<'de> for BytesVisitor {
450        type Value = NodeId;
451
452        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
453            formatter.write_str("33 bytes")
454        }
455
456        fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> where E: Error {
457            use super::DecodeErrorInner;
458
459            NodeId::try_from(v).map_err(|error| {
460                match error.error {
461                    DecodeErrorInner::InvalidLen(len) => E::invalid_length(len, &"33 bytes"),
462                    DecodeErrorInner::InvalidNodeId(error) => E::invalid_value(serde::de::Unexpected::Bytes(&[error.bad_byte]), &"02 or 03"),
463                }
464            })
465        }
466    }
467
468    /// `NodeId` is serialized as hex to human-readable formats and as bytes to non-human-readable.
469    impl Serialize for NodeId {
470        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
471            if serializer.is_human_readable() {
472                serializer.collect_str(self)
473            } else {
474                serializer.serialize_bytes(&self.0)        
475            }
476        }
477    }
478
479    /// `NodeId` is deserialized as hex from human-readable formats and as bytes from non-human-readable.
480    impl<'de> Deserialize<'de> for NodeId {
481        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
482            if deserializer.is_human_readable() {
483                deserializer.deserialize_str(HRVisitor)
484            } else {
485                deserializer.deserialize_bytes(BytesVisitor)
486            }
487        }
488    }
489}
490
491/// Implementations of `postgres-types` traits
492#[cfg(feature = "postgres-types")]
493mod postgres_impl {
494    use alloc::boxed::Box;
495    use super::NodeId;
496    use postgres_types::{ToSql, FromSql, IsNull, Type};
497    use bytes::BytesMut;
498    use std::error::Error;
499    use core::convert::TryInto;
500
501    /// Supports `BYTEA`, `TEXT`, and `VARCHAR`.
502    ///
503    /// Stored as bytes if `BYTEA` is used, as hex string otherwise.
504    impl ToSql for NodeId {
505        fn to_sql(&self, ty: &Type, out: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Send + Sync + 'static>> {
506            use core::fmt::Write;
507
508            match *ty {
509                Type::BYTEA => (&self.0 as &[_]).to_sql(ty, out),
510                _ => write!(out, "{}", self).map(|_| IsNull::No).map_err(|error| Box::new(error) as _)
511            }
512        }
513
514        fn accepts(ty: &Type) -> bool {
515            match *ty {
516                Type::BYTEA => true,
517                Type::TEXT => true,
518                Type::VARCHAR => true,
519                _ => false,
520            }
521        }
522
523        postgres_types::to_sql_checked!();
524    }
525
526    /// Supports `BYTEA`, `TEXT`, and `VARCHAR`.
527    ///
528    /// Decoded as bytes if `BYTEA` is used, as hex string otherwise.
529    impl<'a> FromSql<'a> for NodeId {
530        fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Send + Sync + 'static>> {
531            match *ty {
532                Type::BYTEA => <&[u8]>::from_sql(ty, raw)?.try_into().map_err(|error| Box::new(error) as _),
533                _ => <&str>::from_sql(ty, raw)?.parse().map_err(|error| Box::new(error) as _),
534            }
535        }
536
537        fn accepts(ty: &Type) -> bool {
538            match *ty {
539                Type::BYTEA => true,
540                Type::TEXT => true,
541                Type::VARCHAR => true,
542                _ => false,
543            }
544        }
545    }
546}
547
548/// Implementations of `slog` traits
549#[cfg(feature = "slog")]
550mod slog_impl {
551    use super::NodeId;
552    use slog::{Key, Value, Record, Serializer};
553
554    /// Currently uses `Display` but may use `emit_bytes` if/when it's implemented.
555    impl Value for NodeId {
556        fn serialize(&self, _rec: &Record, key: Key, serializer: &mut dyn Serializer) -> slog::Result {
557            serializer.emit_arguments(key, &format_args!("{}", self))
558        }
559    }
560
561    impl_error_value!(super::ParseError, super::DecodeError);
562}
563
564#[cfg(test)]
565mod tests {
566    use super::NodeId;
567
568    #[test]
569    fn empty() {
570        assert!("".parse::<NodeId>().is_err());
571    }
572
573    #[test]
574    fn one_less() {
575        assert!("02234567890123456789012345678901234567890123456789012345678901234".parse::<NodeId>().is_err());
576    }
577
578    #[test]
579    fn one_more() {
580        assert!("0223456789012345678901234567890123456789012345678901234567890123456".parse::<NodeId>().is_err());
581    }
582
583    #[test]
584    fn invalid_node_id() {
585        assert!("012345678901234567890123456789012345678901234567890123456789abcdef".parse::<NodeId>().is_err());
586    }
587
588    #[test]
589    fn correct_02() {
590        let parsed = "022345678901234567890123456789012345678901234567890123456789abcdef".parse::<NodeId>().unwrap();
591        let expected = b"\x02\x23\x45\x67\x89\x01\x23\x45\x67\x89\x01\x23\x45\x67\x89\x01\x23\x45\x67\x89\x01\x23\x45\x67\x89\x01\x23\x45\x67\x89\xab\xcd\xef";
592        assert_eq!(parsed.0, *expected);
593    }
594
595    #[test]
596    fn correct_03() {
597        let parsed = "032345678901234567890123456789012345678901234567890123456789abcdef".parse::<NodeId>().unwrap();
598        let expected = b"\x03\x23\x45\x67\x89\x01\x23\x45\x67\x89\x01\x23\x45\x67\x89\x01\x23\x45\x67\x89\x01\x23\x45\x67\x89\x01\x23\x45\x67\x89\xab\xcd\xef";
599        assert_eq!(parsed.0, *expected);
600    }
601
602    #[test]
603    fn invalid_digit() {
604        assert!("g12345678901234567890123456789012345678901234567890123456789012345".parse::<NodeId>().is_err());
605    }
606
607    chk_err_impl! {
608        parse_node_id_error_empty, "", NodeId, [
609            "failed to parse '' as Lightning Network node ID",
610            "invalid length (must be 66 chars)",
611        ], [
612            "failed to parse Lightning Network node ID",
613            "invalid length (must be 66 chars)",
614        ];
615    }
616}