relay_set.rs (1431B)
1 #![forbid(unsafe_code)] 2 3 use std::collections::BTreeSet; 4 5 const FNV_1A_64_OFFSET_BASIS: u64 = 0xcbf29ce484222325; 6 const FNV_1A_64_PRIME: u64 = 0x100000001b3; 7 8 pub const CANONICAL_RELAY_SET_FINGERPRINT_VERSION: &str = "radroots-local-events-relay-set-v1"; 9 10 /// Returns the canonical shared local-event relay-set fingerprint. 11 /// 12 /// Relay URLs are trimmed, blank entries are discarded, duplicates are removed, 13 /// and the remaining set is sorted before hashing. 14 pub fn canonical_relay_set_fingerprint<I, S>(relay_urls: I) -> Option<String> 15 where 16 I: IntoIterator<Item = S>, 17 S: AsRef<str>, 18 { 19 let relays = relay_urls 20 .into_iter() 21 .filter_map(|relay_url| { 22 let relay_url = relay_url.as_ref().trim(); 23 (!relay_url.is_empty()).then(|| relay_url.to_owned()) 24 }) 25 .collect::<BTreeSet<_>>(); 26 27 if relays.is_empty() { 28 return None; 29 } 30 31 let mut hash = FNV_1A_64_OFFSET_BASIS; 32 for relay in relays { 33 update_hash(&mut hash, relay.len().to_string().as_bytes()); 34 update_hash(&mut hash, &[0]); 35 update_hash(&mut hash, relay.as_bytes()); 36 update_hash(&mut hash, &[0]); 37 } 38 39 Some(format!( 40 "{CANONICAL_RELAY_SET_FINGERPRINT_VERSION}:{hash:016x}" 41 )) 42 } 43 44 fn update_hash(hash: &mut u64, bytes: &[u8]) { 45 for byte in bytes { 46 *hash ^= u64::from(*byte); 47 *hash = hash.wrapping_mul(FNV_1A_64_PRIME); 48 } 49 }