use crate::crypto::Rng;
use alloc::string::String;
pub const PEER_ID_LEN: usize = 20;
pub fn generate(rng: &mut Rng) -> [u8; 20] {
let mut id = [0u8; 20];
let tag = crate::consts::VERSION_TAG.as_bytes();
let n = tag.len().min(4);
id[0] = b'-';
id[1..1 + n].copy_from_slice(&tag[..n]);
for slot in id.iter_mut().take(5).skip(1 + n) {
*slot = b'0';
}
id[5] = b'0';
id[6] = b'0';
id[7] = b'-';
const HEX: &[u8; 16] = b"0123456789abcdef";
for i in 0..12 {
id[8 + i] = HEX[(rng.next_u32() & 0xf) as usize];
}
id
}
pub fn identify(id: &[u8; 20]) -> Option<(String, String)> {
if id.len() != PEER_ID_LEN || id[0] != b'-' {
return None;
}
let run_end = id
.iter()
.skip(1)
.position(|&b| !b.is_ascii_alphanumeric())
.map(|p| p + 1)
.unwrap_or(20);
let run = &id[1..run_end];
if run.len() < 2 {
return None;
}
let name = String::from_utf8_lossy(&run[..2]).into_owned();
let ver_len = core::cmp::min(run.len() - 2, 4);
let ver = String::from_utf8_lossy(&run[2..2 + ver_len]).into_owned();
Some((name, ver))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn gen_and_identify() {
let mut rng = Rng::from_seed([42u8; 32]);
let id = generate(&mut rng);
assert_eq!(id.len(), 20);
let (name, ver) = identify(&id).unwrap();
assert_eq!(name, "TB");
assert_eq!(ver, "1000");
}
#[test]
fn identify_known() {
let mut id = [0u8; 20];
id.copy_from_slice(b"-qB4300-abcdefghijkl");
let (name, ver) = identify(&id).unwrap();
assert_eq!(name, "qB");
assert_eq!(ver, "4300");
}
}