//! Truncated mesh addresses for bandwidth-efficient routing. //! //! A [`MeshAddress`] is derived from an Ed25519 public key by taking the first //! 16 bytes of its SHA-256 hash. This provides globally unique addressing //! (birthday collision at ~2^64) while saving 16 bytes per packet compared to //! full 32-byte public keys. use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use std::fmt; /// 16-byte truncated mesh address. #[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct MeshAddress([u8; 16]); impl MeshAddress { /// Derive from a 32-byte Ed25519 public key. pub fn from_public_key(key: &[u8; 32]) -> Self { let hash = Sha256::digest(key); let mut addr = [0u8; 16]; addr.copy_from_slice(&hash[..16]); Self(addr) } /// Create from raw 16-byte array. pub fn from_bytes(bytes: [u8; 16]) -> Self { Self(bytes) } /// Get the raw 16-byte address. pub fn as_bytes(&self) -> &[u8; 16] { &self.0 } /// Check if a 32-byte public key matches this address. pub fn matches_key(&self, key: &[u8; 32]) -> bool { Self::from_public_key(key) == *self } /// The broadcast address (all zeros). pub const BROADCAST: Self = Self([0u8; 16]); /// Check if this is the broadcast address. pub fn is_broadcast(&self) -> bool { self.0 == [0u8; 16] } } impl fmt::Debug for MeshAddress { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "MeshAddress({})", hex::encode(self.0)) } } impl fmt::Display for MeshAddress { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", hex::encode(&self.0[..8])) } } impl From<[u8; 16]> for MeshAddress { fn from(bytes: [u8; 16]) -> Self { Self(bytes) } } impl AsRef<[u8; 16]> for MeshAddress { fn as_ref(&self) -> &[u8; 16] { &self.0 } } #[cfg(test)] mod tests { use super::*; #[test] fn from_key_deterministic() { let key = [42u8; 32]; let addr1 = MeshAddress::from_public_key(&key); let addr2 = MeshAddress::from_public_key(&key); assert_eq!(addr1, addr2, "same key must produce same address"); } #[test] fn different_keys_different_addresses() { let key_a = [1u8; 32]; let key_b = [2u8; 32]; let addr_a = MeshAddress::from_public_key(&key_a); let addr_b = MeshAddress::from_public_key(&key_b); assert_ne!(addr_a, addr_b, "different keys must produce different addresses"); } #[test] fn matches_key_works() { let key = [99u8; 32]; let addr = MeshAddress::from_public_key(&key); assert!(addr.matches_key(&key), "correct key must match"); let wrong_key = [100u8; 32]; assert!(!addr.matches_key(&wrong_key), "wrong key must not match"); } #[test] fn broadcast_address() { assert_eq!(*MeshAddress::BROADCAST.as_bytes(), [0u8; 16]); assert!(MeshAddress::BROADCAST.is_broadcast()); let non_broadcast = MeshAddress::from_bytes([1u8; 16]); assert!(!non_broadcast.is_broadcast()); } #[test] fn display_formatting() { let key = [0xAB; 32]; let addr = MeshAddress::from_public_key(&key); let display = format!("{addr}"); // Display shows first 8 bytes as hex = 16 hex chars. assert_eq!(display.len(), 16, "display should show 8 bytes = 16 hex chars"); let debug = format!("{addr:?}"); // Debug shows all 16 bytes as hex = 32 hex chars, plus wrapper. assert!(debug.starts_with("MeshAddress(")); assert!(debug.ends_with(')')); } #[test] fn serde_roundtrip() { let key = [77u8; 32]; let addr = MeshAddress::from_public_key(&key); let json = serde_json::to_string(&addr).expect("serialize"); let restored: MeshAddress = serde_json::from_str(&json).expect("deserialize"); assert_eq!(addr, restored); } }