libp2p_kad/kbucket/
key.rs1#![allow(clippy::manual_div_ceil)]
21
22use std::{
23 borrow::Borrow,
24 hash::{Hash, Hasher},
25};
26
27use libp2p_core::multihash::Multihash;
28use libp2p_identity::PeerId;
29use sha2::{
30 digest::generic_array::{typenum::U32, GenericArray},
31 Digest, Sha256,
32};
33use uint::*;
34
35use crate::record;
36
37construct_uint! {
38 pub struct U256(4);
40}
41
42#[derive(Clone, Copy, Debug)]
50pub struct Key<T> {
51 preimage: T,
52 bytes: KeyBytes,
53}
54
55impl<T> Key<T> {
56 pub fn new(preimage: T) -> Key<T>
62 where
63 T: Borrow<[u8]>,
64 {
65 let bytes = KeyBytes::new(preimage.borrow());
66 Key { preimage, bytes }
67 }
68
69 pub fn preimage(&self) -> &T {
71 &self.preimage
72 }
73
74 pub fn into_preimage(self) -> T {
76 self.preimage
77 }
78
79 pub fn distance<U>(&self, other: &U) -> Distance
81 where
82 U: AsRef<KeyBytes>,
83 {
84 self.bytes.distance(other)
85 }
86
87 pub fn hashed_bytes(&self) -> &[u8] {
89 &self.bytes.0
90 }
91
92 pub fn for_distance(&self, d: Distance) -> KeyBytes {
98 self.bytes.for_distance(d)
99 }
100}
101
102impl<T> From<Key<T>> for KeyBytes {
103 fn from(key: Key<T>) -> KeyBytes {
104 key.bytes
105 }
106}
107
108impl<const S: usize> From<Multihash<S>> for Key<Multihash<S>> {
109 fn from(m: Multihash<S>) -> Self {
110 let bytes = KeyBytes(Sha256::digest(m.to_bytes()));
111 Key { preimage: m, bytes }
112 }
113}
114
115impl From<PeerId> for Key<PeerId> {
116 fn from(p: PeerId) -> Self {
117 let bytes = KeyBytes(Sha256::digest(p.to_bytes()));
118 Key { preimage: p, bytes }
119 }
120}
121
122impl From<Vec<u8>> for Key<Vec<u8>> {
123 fn from(b: Vec<u8>) -> Self {
124 Key::new(b)
125 }
126}
127
128impl From<record::Key> for Key<record::Key> {
129 fn from(k: record::Key) -> Self {
130 Key::new(k)
131 }
132}
133
134impl<T> AsRef<KeyBytes> for Key<T> {
135 fn as_ref(&self) -> &KeyBytes {
136 &self.bytes
137 }
138}
139
140impl<T, U> PartialEq<Key<U>> for Key<T> {
141 fn eq(&self, other: &Key<U>) -> bool {
142 self.bytes == other.bytes
143 }
144}
145
146impl<T> Eq for Key<T> {}
147
148impl<T> Hash for Key<T> {
149 fn hash<H: Hasher>(&self, state: &mut H) {
150 self.bytes.0.hash(state);
151 }
152}
153
154#[derive(PartialEq, Eq, Clone, Copy, Debug)]
156pub struct KeyBytes(GenericArray<u8, U32>);
157
158impl KeyBytes {
159 pub fn new<T>(value: T) -> Self
162 where
163 T: Borrow<[u8]>,
164 {
165 KeyBytes(Sha256::digest(value.borrow()))
166 }
167
168 pub fn distance<U>(&self, other: &U) -> Distance
170 where
171 U: AsRef<KeyBytes>,
172 {
173 let a = U256::from_big_endian(self.0.as_slice());
174 let b = U256::from_big_endian(other.as_ref().0.as_slice());
175 Distance(a ^ b)
176 }
177
178 pub fn for_distance(&self, d: Distance) -> KeyBytes {
184 let key_int = U256::from_big_endian(self.0.as_slice()) ^ d.0;
185 KeyBytes(GenericArray::from(key_int.to_big_endian()))
186 }
187}
188
189impl AsRef<KeyBytes> for KeyBytes {
190 fn as_ref(&self) -> &KeyBytes {
191 self
192 }
193}
194
195#[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Debug)]
197pub struct Distance(pub U256);
198
199impl Distance {
200 pub fn ilog2(&self) -> Option<u32> {
204 (256 - self.0.leading_zeros()).checked_sub(1)
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use quickcheck::*;
211
212 use super::*;
213 use crate::SHA_256_MH;
214
215 impl Arbitrary for Key<PeerId> {
216 fn arbitrary(_: &mut Gen) -> Key<PeerId> {
217 Key::from(PeerId::random())
218 }
219 }
220
221 impl Arbitrary for Key<Multihash<64>> {
222 fn arbitrary(g: &mut Gen) -> Key<Multihash<64>> {
223 let hash: [u8; 32] = core::array::from_fn(|_| u8::arbitrary(g));
224 Key::from(Multihash::wrap(SHA_256_MH, &hash).unwrap())
225 }
226 }
227
228 #[test]
229 fn identity() {
230 fn prop(a: Key<PeerId>) -> bool {
231 a.distance(&a) == Distance::default()
232 }
233 quickcheck(prop as fn(_) -> _)
234 }
235
236 #[test]
237 fn symmetry() {
238 fn prop(a: Key<PeerId>, b: Key<PeerId>) -> bool {
239 a.distance(&b) == b.distance(&a)
240 }
241 quickcheck(prop as fn(_, _) -> _)
242 }
243
244 #[test]
245 fn triangle_inequality() {
246 fn prop(a: Key<PeerId>, b: Key<PeerId>, c: Key<PeerId>) -> TestResult {
247 let ab = a.distance(&b);
248 let bc = b.distance(&c);
249 let (ab_plus_bc, overflow) = ab.0.overflowing_add(bc.0);
250 if overflow {
251 TestResult::discard()
252 } else {
253 TestResult::from_bool(a.distance(&c) <= Distance(ab_plus_bc))
254 }
255 }
256 quickcheck(prop as fn(_, _, _) -> _)
257 }
258
259 #[test]
260 fn unidirectionality() {
261 fn prop(a: Key<PeerId>, b: Key<PeerId>) -> bool {
262 let d = a.distance(&b);
263 (0..100).all(|_| {
264 let c = Key::from(PeerId::random());
265 a.distance(&c) != d || b == c
266 })
267 }
268 quickcheck(prop as fn(_, _) -> _)
269 }
270}