libp2p_kad/kbucket/
key.rs1use derivative::Derivative;
22use libp2p_core::{PeerId, multihash::Multihash};
23use sha2::digest::generic_array::{typenum::U32, GenericArray};
24use sha2::{Digest, Sha256};
25use std::borrow::Borrow;
26use std::hash::{Hash, Hasher};
27use uint::*;
28use crate::record;
29
30construct_uint! {
31 pub(super) struct U256(4);
33}
34
35#[derive(Clone, Derivative)]
43#[derivative(Debug)]
44pub struct Key<T> {
45 preimage: T,
46 #[derivative(Debug = "ignore")]
47 bytes: KeyBytes,
48}
49
50impl<T> Key<T> {
51 pub fn new(preimage: T) -> Key<T>
57 where
58 T: Borrow<[u8]>
59 {
60 let bytes = KeyBytes::new(preimage.borrow());
61 Key { preimage, bytes }
62 }
63
64 pub fn preimage(&self) -> &T {
66 &self.preimage
67 }
68
69 pub fn into_preimage(self) -> T {
71 self.preimage
72 }
73
74 pub fn distance<U>(&self, other: &U) -> Distance
76 where
77 U: AsRef<KeyBytes>
78 {
79 self.bytes.distance(other)
80 }
81
82 pub fn for_distance(&self, d: Distance) -> KeyBytes {
88 self.bytes.for_distance(d)
89 }
90}
91
92impl<T> Into<KeyBytes> for Key<T> {
93 fn into(self) -> KeyBytes {
94 self.bytes
95 }
96}
97
98impl From<Multihash> for Key<Multihash> {
99 fn from(m: Multihash) -> Self {
100 let bytes = KeyBytes(Sha256::digest(&m.to_bytes()));
101 Key {
102 preimage: m,
103 bytes
104 }
105 }
106}
107
108impl From<PeerId> for Key<PeerId> {
109 fn from(p: PeerId) -> Self {
110 let bytes = KeyBytes(Sha256::digest(&p.to_bytes()));
111 Key {
112 preimage: p,
113 bytes
114 }
115 }
116}
117
118impl From<Vec<u8>> for Key<Vec<u8>> {
119 fn from(b: Vec<u8>) -> Self {
120 Key::new(b)
121 }
122}
123
124impl From<record::Key> for Key<record::Key> {
125 fn from(k: record::Key) -> Self {
126 Key::new(k)
127 }
128}
129
130impl<T> AsRef<KeyBytes> for Key<T> {
131 fn as_ref(&self) -> &KeyBytes {
132 &self.bytes
133 }
134}
135
136impl<T, U> PartialEq<Key<U>> for Key<T> {
137 fn eq(&self, other: &Key<U>) -> bool {
138 self.bytes == other.bytes
139 }
140}
141
142impl<T> Eq for Key<T> {}
143
144impl<T> Hash for Key<T> {
145 fn hash<H: Hasher>(&self, state: &mut H) {
146 self.bytes.0.hash(state);
147 }
148}
149
150#[derive(PartialEq, Eq, Clone, Debug)]
152pub struct KeyBytes(GenericArray<u8, U32>);
153
154impl KeyBytes {
155 pub fn new<T>(value: T) -> Self
158 where
159 T: Borrow<[u8]>
160 {
161 KeyBytes(Sha256::digest(value.borrow()))
162 }
163
164 pub fn distance<U>(&self, other: &U) -> Distance
166 where
167 U: AsRef<KeyBytes>
168 {
169 let a = U256::from(self.0.as_slice());
170 let b = U256::from(other.as_ref().0.as_slice());
171 Distance(a ^ b)
172 }
173
174 pub fn for_distance(&self, d: Distance) -> KeyBytes {
180 let key_int = U256::from(self.0.as_slice()) ^ d.0;
181 KeyBytes(GenericArray::from(<[u8; 32]>::from(key_int)))
182 }
183}
184
185impl AsRef<KeyBytes> for KeyBytes {
186 fn as_ref(&self) -> &KeyBytes {
187 self
188 }
189}
190
191impl AsRef<[u8]> for KeyBytes {
192 fn as_ref(&self) -> &[u8] {
193 self.0.as_ref()
194 }
195}
196
197#[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Debug)]
199pub struct Distance(pub(super) U256);
200
201impl Distance {
202 pub fn ilog2(&self) -> Option<u32> {
206 (256 - self.0.leading_zeros()).checked_sub(1)
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213 use libp2p_core::multihash::Code;
214 use quickcheck::*;
215 use rand::Rng;
216
217 impl Arbitrary for Key<PeerId> {
218 fn arbitrary<G: Gen>(_: &mut G) -> Key<PeerId> {
219 Key::from(PeerId::random())
220 }
221 }
222
223 impl Arbitrary for Key<Multihash> {
224 fn arbitrary<G: Gen>(_: &mut G) -> Key<Multihash> {
225 let hash = rand::thread_rng().gen::<[u8; 32]>();
226 Key::from(Multihash::wrap(Code::Sha2_256.into(), &hash).unwrap())
227 }
228 }
229
230 #[test]
231 fn identity() {
232 fn prop(a: Key<PeerId>) -> bool {
233 a.distance(&a) == Distance::default()
234 }
235 quickcheck(prop as fn(_) -> _)
236 }
237
238 #[test]
239 fn symmetry() {
240 fn prop(a: Key<PeerId>, b: Key<PeerId>) -> bool {
241 a.distance(&b) == b.distance(&a)
242 }
243 quickcheck(prop as fn(_, _) -> _)
244 }
245
246 #[test]
247 fn triangle_inequality() {
248 fn prop(a: Key<PeerId>, b: Key<PeerId>, c: Key<PeerId>) -> TestResult {
249 let ab = a.distance(&b);
250 let bc = b.distance(&c);
251 let (ab_plus_bc, overflow) = ab.0.overflowing_add(bc.0);
252 if overflow {
253 TestResult::discard()
254 } else {
255 TestResult::from_bool(a.distance(&c) <= Distance(ab_plus_bc))
256 }
257 }
258 quickcheck(prop as fn(_, _, _) -> _)
259 }
260
261 #[test]
262 fn unidirectionality() {
263 fn prop(a: Key<PeerId>, b: Key<PeerId>) -> bool {
264 let d = a.distance(&b);
265 (0..100).all(|_| {
266 let c = Key::from(PeerId::random());
267 a.distance(&c) != d || b == c
268 })
269 }
270 quickcheck(prop as fn(_, _) -> _)
271 }
272}