1use ed25519_dalek::VerifyingKey;
8use sha2::{Digest, Sha256};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub struct NodeId(pub [u8; 20]);
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct ShareId(pub [u8; 32]);
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub struct ContentId(pub [u8; 32]);
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
20pub struct ManifestId(pub [u8; 32]);
21
22impl NodeId {
23 pub fn from_pubkey(pubkey: &VerifyingKey) -> Self {
24 Self::from_pubkey_bytes(pubkey.as_bytes())
25 }
26
27 pub fn from_pubkey_bytes(pubkey: &[u8; 32]) -> Self {
28 let digest = Sha256::digest(pubkey);
29 let mut id = [0u8; 20];
30 id.copy_from_slice(&digest[..20]);
31 Self(id)
32 }
33
34 pub fn xor_distance(&self, other: &Self) -> [u8; 20] {
35 let mut out = [0u8; 20];
36 for (idx, byte) in out.iter_mut().enumerate() {
37 *byte = self.0[idx] ^ other.0[idx];
38 }
39 out
40 }
41
42 #[deprecated(note = "use NodeId::xor_distance_cmp(a, b, target) instead")]
44 pub fn distance_cmp(&self, target: &Self, other: &Self) -> std::cmp::Ordering {
45 Self::xor_distance_cmp(self, other, target)
46 }
47
48 pub fn xor_distance_cmp(a: &Self, b: &Self, target: &Self) -> std::cmp::Ordering {
53 let da = a.xor_distance(target);
54 let db = b.xor_distance(target);
55 da.cmp(&db)
56 }
57}
58
59impl ShareId {
60 pub fn from_pubkey(pubkey: &VerifyingKey) -> Self {
61 Self::from_pubkey_bytes(pubkey.as_bytes())
62 }
63
64 pub fn from_pubkey_bytes(pubkey: &[u8; 32]) -> Self {
65 let digest = Sha256::digest(pubkey);
66 let mut id = [0u8; 32];
67 id.copy_from_slice(&digest[..]);
68 Self(id)
69 }
70
71 pub fn xor_distance(&self, other: &Self) -> [u8; 20] {
72 let mut out = [0u8; 20];
73 for (idx, byte) in out.iter_mut().enumerate() {
74 *byte = self.0[idx] ^ other.0[idx];
75 }
76 out
77 }
78
79 #[deprecated(note = "use ShareId::xor_distance_cmp(a, b, target) instead")]
81 pub fn distance_cmp(&self, target: &Self, other: &Self) -> std::cmp::Ordering {
82 Self::xor_distance_cmp(self, other, target)
83 }
84
85 pub fn xor_distance_cmp(a: &Self, b: &Self, target: &Self) -> std::cmp::Ordering {
87 let da = a.xor_distance(target);
88 let db = b.xor_distance(target);
89 da.cmp(&db)
90 }
91}
92
93impl ContentId {
94 pub fn from_bytes(bytes: &[u8]) -> Self {
95 Self(*blake3::hash(bytes).as_bytes())
96 }
97}
98
99impl ManifestId {
100 pub fn from_manifest_bytes(bytes: &[u8]) -> Self {
101 Self(*blake3::hash(bytes).as_bytes())
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use ed25519_dalek::SigningKey;
109 use rand::rngs::OsRng;
110
111 #[test]
112 fn node_id_len_matches_spec() {
113 let mut rng = OsRng;
114 let key = SigningKey::generate(&mut rng);
115 let node_id = NodeId::from_pubkey(&key.verifying_key());
116 assert_eq!(node_id.0.len(), 20);
117 }
118
119 #[test]
120 fn content_id_is_stable() {
121 let a = ContentId::from_bytes(b"scp2p");
122 let b = ContentId::from_bytes(b"scp2p");
123 assert_eq!(a, b);
124 }
125
126 #[test]
127 fn node_distance_compare_orders_closest() {
128 let target = NodeId([0u8; 20]);
129 let a = NodeId([1u8; 20]);
130 let b = NodeId([2u8; 20]);
131 assert!(NodeId::xor_distance_cmp(&a, &b, &target).is_lt());
132 }
133}