//! 16-byte node identifier derived from truncated SHA-256(pubkey). use secp256k1::XOnlyPublicKey; use sha2::{Digest, Sha256}; use std::fmt; use super::{IdentityError, hex_encode}; /// 16-byte node identifier derived from truncated SHA-256(pubkey). /// /// The node_addr is the first 16 bytes of SHA-256(pubkey), providing 128 bits /// of collision resistance. Hashing the public key prevents grinding attacks /// that exploit secp256k1's algebraic structure. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct NodeAddr([u8; 16]); impl NodeAddr { /// Create a NodeAddr from a 16-byte array. pub fn from_bytes(bytes: [u8; 16]) -> Self { Self(bytes) } /// Create a NodeAddr from a slice. pub fn from_slice(slice: &[u8]) -> Result { if slice.len() != 16 { return Err(IdentityError::InvalidNodeAddrLength(slice.len())); } let mut bytes = [0u8; 16]; bytes.copy_from_slice(slice); Ok(Self(bytes)) } /// Derive a NodeAddr from an x-only public key (npub). /// /// Computes SHA-256(pubkey) and takes the first 16 bytes. pub fn from_pubkey(pubkey: &XOnlyPublicKey) -> Self { let mut hasher = Sha256::new(); hasher.update(pubkey.serialize()); let hash = hasher.finalize(); let mut bytes = [0u8; 16]; bytes.copy_from_slice(&hash[..16]); Self(bytes) } /// Return the raw bytes. pub fn as_bytes(&self) -> &[u8; 16] { &self.0 } /// Return the bytes as a slice. pub fn as_slice(&self) -> &[u8] { &self.0 } /// Return a short hex representation: first 4 bytes + "...". pub fn short_hex(&self) -> String { format!("{}...", hex_encode(&self.0[..4])) } } impl fmt::Debug for NodeAddr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "NodeAddr({})", hex_encode(&self.0[..8])) } } impl fmt::Display for NodeAddr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", hex_encode(&self.0)) } } impl AsRef<[u8]> for NodeAddr { fn as_ref(&self) -> &[u8] { &self.0 } }