1use std::{
2 fmt::Debug,
3 ops::{Add, Shr, Sub},
4};
5
6use crypto_bigint::{ArrayEncoding, Encoding, U256};
7use derive_more::From;
8use libp2p_identity::DecodingError;
9use malloc_size_of_derive::MallocSizeOf;
10use multiaddr::Multiaddr;
11use openmina_core::bug_condition;
12use serde::{Deserialize, Serialize};
13use sha2::{Digest, Sha256};
14
15use crate::{
16 ConnectionType, P2pNetworkKademliaMultiaddrError, P2pNetworkKademliaPeerIdError, PeerId,
17};
18
19use super::CID;
20
21mod u256_serde {
22 use std::array::TryFromSliceError;
23
24 use crypto_bigint::{Encoding, U256};
25 use serde::{Deserialize, Serialize};
26
27 pub fn serialize<S>(value: &U256, serializer: S) -> Result<S::Ok, S::Error>
28 where
29 S: serde::Serializer,
30 {
31 if serializer.is_human_readable() {
32 let hex = hex::encode(value.to_be_bytes());
33 serializer.serialize_str(&hex)
34 } else {
35 value.serialize(serializer)
36 }
37 }
38
39 fn decode_error<E: serde::de::Error>(e: hex::FromHexError) -> E {
40 E::custom(format!("error converting from hex string: {e}"))
41 }
42
43 fn bytes_error<E: serde::de::Error>(e: TryFromSliceError) -> E {
44 E::custom(format!("error converting from slice to array: {e}"))
45 }
46
47 pub fn deserialize<'de, D>(deserializer: D) -> Result<U256, D::Error>
48 where
49 D: serde::Deserializer<'de>,
50 {
51 if deserializer.is_human_readable() {
52 let s = String::deserialize(deserializer)?;
53 let bytes = hex::decode(s)
54 .map_err(decode_error)?
55 .as_slice()
56 .try_into()
57 .map_err(bytes_error)?;
58 let u256 = U256::from_be_bytes(bytes);
59 Ok(u256)
60 } else {
61 U256::deserialize(deserializer)
62 }
63 }
64}
65
66#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, MallocSizeOf)]
68pub struct P2pNetworkKadKey(
69 #[serde(with = "u256_serde")]
70 #[ignore_malloc_size_of = "doesn't allocate"]
71 U256,
72);
73
74impl P2pNetworkKadKey {
75 pub fn distance(self, rhs: &Self) -> P2pNetworkKadDist {
76 P2pNetworkKadDist(self.0 ^ rhs.0)
77 }
78}
79
80#[derive(Clone, Debug, Serialize, PartialEq, Deserialize, thiserror::Error, MallocSizeOf)]
81pub enum P2pNetworkKadKeyError {
82 #[error("decoding error")]
83 DecodingError,
84}
85
86impl TryFrom<&PeerId> for P2pNetworkKadKey {
87 type Error = P2pNetworkKadKeyError;
88
89 fn try_from(value: &PeerId) -> Result<Self, Self::Error> {
90 P2pNetworkKadKey::try_from(*value)
91 }
92}
93
94impl TryFrom<PeerId> for P2pNetworkKadKey {
95 type Error = P2pNetworkKadKeyError;
96
97 fn try_from(value: PeerId) -> Result<Self, Self::Error> {
98 Ok(P2pNetworkKadKey::from(
99 CID::try_from(value).map_err(|_| P2pNetworkKadKeyError::DecodingError)?,
100 ))
101 }
102}
103
104impl From<CID> for P2pNetworkKadKey {
105 fn from(value: CID) -> Self {
106 P2pNetworkKadKey(U256::from_be_byte_array(Sha256::digest(value.0)))
107 }
108}
109
110impl Debug for P2pNetworkKadKey {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 if f.alternate() {
113 let bytes = self.0.to_be_bytes();
114 f.write_str(&hex::encode(bytes))
115 } else {
116 f.debug_tuple("P2pNetworkKadKey").field(&self.0).finish()
117 }
118 }
119}
120
121impl Add<&P2pNetworkKadDist> for &P2pNetworkKadKey {
122 type Output = P2pNetworkKadKey;
123
124 #[allow(clippy::suspicious_arithmetic_impl, clippy::arithmetic_side_effects)]
125 fn add(self, rhs: &P2pNetworkKadDist) -> Self::Output {
126 P2pNetworkKadKey(self.0 ^ rhs.0)
127 }
128}
129
130impl Sub for P2pNetworkKadKey {
131 type Output = P2pNetworkKadDist;
132
133 #[allow(clippy::suspicious_arithmetic_impl, clippy::arithmetic_side_effects)]
134 fn sub(self, rhs: Self) -> Self::Output {
135 self.distance(&rhs)
136 }
137}
138
139impl Sub<&P2pNetworkKadKey> for &P2pNetworkKadKey {
140 type Output = P2pNetworkKadDist;
141
142 #[allow(clippy::suspicious_arithmetic_impl, clippy::arithmetic_side_effects)]
143 fn sub(self, rhs: &P2pNetworkKadKey) -> Self::Output {
144 self.distance(rhs)
145 }
146}
147
148impl Sub<P2pNetworkKadKey> for &P2pNetworkKadKey {
149 type Output = P2pNetworkKadDist;
150
151 #[allow(clippy::suspicious_arithmetic_impl, clippy::arithmetic_side_effects)]
152 fn sub(self, rhs: P2pNetworkKadKey) -> Self::Output {
153 self.distance(&rhs)
154 }
155}
156
157impl Sub<&P2pNetworkKadKey> for P2pNetworkKadKey {
158 type Output = P2pNetworkKadDist;
159
160 #[allow(clippy::suspicious_arithmetic_impl, clippy::arithmetic_side_effects)]
161 fn sub(self, rhs: &P2pNetworkKadKey) -> Self::Output {
162 self.distance(rhs)
163 }
164}
165
166impl Shr<usize> for &P2pNetworkKadKey {
167 type Output = P2pNetworkKadKey;
168
169 fn shr(self, rhs: usize) -> Self::Output {
170 P2pNetworkKadKey(self.0 >> rhs)
171 }
172}
173
174#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
176pub struct P2pNetworkKadDist(#[serde(with = "u256_serde")] U256);
177
178impl Debug for P2pNetworkKadDist {
179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 if f.alternate() {
181 let bytes = self.0.to_be_bytes();
182 f.write_str(&hex::encode(bytes))
183 } else {
184 f.debug_tuple("P2pNetworkKadDist").field(&self.0).finish()
185 }
186 }
187}
188
189impl P2pNetworkKadDist {
190 fn to_index(&self) -> usize {
194 256 - self.0.bits_vartime()
195 }
196}
197
198impl From<usize> for P2pNetworkKadDist {
201 fn from(value: usize) -> Self {
202 P2pNetworkKadDist(U256::MAX >> value)
203 }
204}
205
206#[derive(Clone, Serialize, Deserialize)]
209pub struct P2pNetworkKadRoutingTable<const K: usize = 20> {
210 pub this_key: P2pNetworkKadKey,
212 pub buckets: Vec<P2pNetworkKadBucket<K>>,
217}
218
219impl<const K: usize> Default for P2pNetworkKadRoutingTable<K> {
220 fn default() -> Self {
221 Self {
222 this_key: P2pNetworkKadKey(U256::ZERO),
223 buckets: Default::default(),
224 }
225 }
226}
227
228impl<const K: usize> Debug for P2pNetworkKadRoutingTable<K> {
229 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230 if f.alternate() {
231 if f.sign_plus() {
232 writeln!(f, "this key: {:#?}", self.this_key)?;
233 }
234 for (i, bucket) in self.buckets.iter().enumerate() {
235 writeln!(f, "{i}: {}", bucket.0.len())?;
236 if f.sign_plus() {
237 for entry in &bucket.0 {
238 writeln!(f, " {:#.*?}", i, &entry.key)?;
239 }
240 }
241 }
242 Ok(())
243 } else {
244 f.debug_struct("P2pNetworkKadRoutingTable")
245 .field("this_key", &self.this_key)
246 .field("buckets", &self.buckets)
247 .finish()
248 }
249 }
250}
251
252#[derive(Debug, thiserror::Error)]
253#[error("bucket capacity exceeded")]
254pub struct P2pNetworkKadRoutingTableInsertError;
255
256impl<const K: usize> P2pNetworkKadRoutingTable<K> {
257 pub fn new(this_entry: P2pNetworkKadEntry) -> Self {
258 let this_key = this_entry.key;
259 let global_bucket = P2pNetworkKadBucket(vec![this_entry]);
260 let buckets = vec![global_bucket];
261 P2pNetworkKadRoutingTable { this_key, buckets }
262 }
263
264 pub fn insert(
276 &mut self,
277 entry: P2pNetworkKadEntry,
278 ) -> Result<bool, P2pNetworkKadRoutingTableInsertError> {
279 if entry.addrs.is_empty() {
280 return Err(P2pNetworkKadRoutingTableInsertError);
281 }
282 let dist = self.this_key - entry.key;
284
285 let index = dist.to_index();
287
288 let mut max_index = self.buckets.len() - 1;
289 loop {
290 if index < max_index {
291 if self.buckets[index].can_insert(&entry) {
293 break Ok(self.buckets[index].insert(entry));
294 } else {
295 break Err(P2pNetworkKadRoutingTableInsertError);
296 }
297 } else if self.buckets[max_index].can_insert(&entry) {
298 break Ok(self.buckets[max_index].insert(entry));
299 } else {
300 max_index += 1;
301 let split_dist = max_index.into();
302 let Some((bucket1, bucket2)) = self
303 .buckets
304 .pop()
305 .map(|b| b.split(|e| (self.this_key - e.key) >= split_dist))
306 else {
307 bug_condition!("should be unreachable");
308 return Err(P2pNetworkKadRoutingTableInsertError);
309 };
310 self.buckets.extend([bucket1, bucket2]);
311 }
312 }
313 }
314
315 pub fn look_up(&self, key: &P2pNetworkKadKey) -> Option<&P2pNetworkKadEntry> {
317 let dist = self.this_key - key;
319
320 let index = dist.to_index().min(self.buckets.len() - 1);
322
323 self.buckets[index].iter().find(|e| &e.key == key)
324 }
325
326 pub fn find_node<'a>(
332 &'a self,
333 key: &'a P2pNetworkKadKey,
334 ) -> impl Iterator<Item = &'a P2pNetworkKadEntry> {
335 self.closest_peers(key).take(20)
336 }
337
338 pub fn closest_peers<'a>(&'a self, key: &'a P2pNetworkKadKey) -> ClosestPeers<'a, K> {
341 ClosestPeers::new(self, key)
342 }
343
344 #[cfg(test)]
345 fn assert_k_buckets(&self) {
346 let mut prev_dist = None;
347 for (i, bucket) in self.buckets.iter().enumerate().rev() {
348 assert!(bucket.0.len() <= K, "{self:+#?}");
349 let dist = P2pNetworkKadDist::from(i);
350 for entry in &bucket.0 {
351 assert!(
352 self.this_key - entry.key <= dist,
353 "for {:#?} at {i} distance {:#?} is too big, expecting at most {dist:#?}\nrouting table:\n{self:+#?}",
354 entry.key,
355 self.this_key - entry.key,
356 );
357 if let Some(prev_dist) = &prev_dist {
358 assert!(
359 &(self.this_key - entry.key) > prev_dist,
360 "distance too small: {:#?}\nrouting table:\n{:+#?}\ndist: {:#?}\nprev_dist: {:#?}",
361 entry.key,
362 self,
363 self.this_key - entry.key,
364 prev_dist,
365 );
366 }
367 }
368 prev_dist = Some(dist);
369 }
370 }
371}
372
373impl Extend<P2pNetworkKadEntry> for P2pNetworkKadRoutingTable {
374 fn extend<T: IntoIterator<Item = P2pNetworkKadEntry>>(&mut self, iter: T) {
375 for entry in iter {
376 let _ = self.insert(entry);
378 }
379 }
380}
381
382#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, MallocSizeOf)]
383pub struct P2pNetworkKadEntry {
384 pub key: P2pNetworkKadKey,
385 pub peer_id: PeerId,
386 #[with_malloc_size_of_func = "measurement::multiaddr_vec"]
387 addrs: Vec<Multiaddr>,
388 #[ignore_malloc_size_of = "doesn't allocate"]
389 pub connection: ConnectionType,
390}
391
392impl P2pNetworkKadEntry {
393 pub const MAX_ADDRS: usize = 16;
394
395 pub fn new(peer_id: PeerId, addrs: Vec<Multiaddr>) -> Result<Self, P2pNetworkKadKeyError> {
396 if addrs.len() > Self::MAX_ADDRS {
397 openmina_core::log::info!(
398 openmina_core::log::system_time();
399 kind = "P2pNetworkKadEntry new",
400 summary = format!("truncating {addrs:?} to {} elements", Self::MAX_ADDRS),
401 );
402 }
403
404 Ok(P2pNetworkKadEntry {
405 key: peer_id.try_into()?,
406 peer_id,
407 addrs: addrs.into_iter().take(Self::MAX_ADDRS).collect(),
408 connection: ConnectionType::NotConnected,
409 })
410 }
411
412 pub fn dist(&self, other: &P2pNetworkKadEntry) -> P2pNetworkKadDist {
413 self.key - other.key
414 }
415
416 pub fn addresses(&self) -> &Vec<Multiaddr> {
417 &self.addrs
418 }
419}
420
421#[derive(Clone, Debug, Serialize, PartialEq, Deserialize, thiserror::Error, MallocSizeOf)]
422pub enum P2pNetworkKadEntryTryFromError {
423 #[error(transparent)]
424 PeerId(#[from] P2pNetworkKademliaPeerIdError),
425 #[error(transparent)]
426 Key(#[from] P2pNetworkKadKeyError),
427 #[error(transparent)]
428 Multiaddr(#[from] P2pNetworkKademliaMultiaddrError),
429}
430
431impl TryFrom<super::mod_Message::Peer<'_>> for P2pNetworkKadEntry {
432 type Error = P2pNetworkKadEntryTryFromError;
433
434 fn try_from(value: super::mod_Message::Peer) -> Result<Self, Self::Error> {
435 let peer_id = super::peer_id_try_from_bytes(value.id)?;
436 let key = peer_id.try_into()?;
437 let addrs = value
438 .addrs
439 .into_iter()
440 .map(super::multiaddr_try_from_bytes)
441 .collect::<Result<_, _>>()?;
442 let connection = value.connection.into();
443 Ok(P2pNetworkKadEntry {
444 peer_id,
445 key,
446 addrs,
447 connection,
448 })
449 }
450}
451
452impl<'a> TryFrom<&'a P2pNetworkKadEntry> for super::mod_Message::Peer<'a> {
453 type Error = DecodingError;
454
455 fn try_from(value: &'a P2pNetworkKadEntry) -> Result<Self, Self::Error> {
456 Ok(super::mod_Message::Peer {
457 id: (&value.peer_id).try_into()?,
458 addrs: value
459 .addrs
460 .iter()
461 .map(|addr| addr.as_ref().into())
462 .collect(),
463 connection: value.connection.into(),
464 })
465 }
466}
467
468pub struct ClosestPeers<'a, const K: usize> {
469 table: &'a P2pNetworkKadRoutingTable<K>,
470 index_iter: std::vec::IntoIter<usize>,
471 bucket_index: usize,
472 bucket_iterator: std::vec::IntoIter<&'a P2pNetworkKadEntry>,
473 key: &'a P2pNetworkKadKey,
474}
475
476impl<'a, const K: usize> ClosestPeers<'a, K> {
477 fn new(table: &'a P2pNetworkKadRoutingTable<K>, key: &'a P2pNetworkKadKey) -> Self {
478 let dist = table.this_key - key;
479 let mut index_iter = Self::bucket_index_iterator(dist, table.buckets.len());
480 let bucket_index = index_iter
481 .next()
482 .expect("implementation should ensure there is at least one bucket");
483 let bucket_iterator =
484 Self::get_bucket_iter(&table.buckets[bucket_index], key, &table.this_key);
485 ClosestPeers {
487 table,
488 index_iter,
489 bucket_index,
490 bucket_iterator,
491 key,
492 }
493 }
494
495 fn bucket_index_iterator(
496 dist: P2pNetworkKadDist,
497 buckets_len: usize,
498 ) -> std::vec::IntoIter<usize> {
499 let (mut ones, zeroes) =
500 (0..buckets_len).partition::<Vec<_>, _>(|index| dist.0.bit_vartime(255 - *index));
501 ones.extend(zeroes.into_iter().rev());
502 let it: std::vec::IntoIter<usize> = ones.into_iter();
503 it
504 }
505
506 fn get_bucket_iter(
507 bucket: &'a P2pNetworkKadBucket<K>,
508 key: &P2pNetworkKadKey,
509 this_key: &P2pNetworkKadKey,
510 ) -> std::vec::IntoIter<&'a P2pNetworkKadEntry> {
511 let mut vec = Vec::from_iter(
512 bucket
513 .into_iter()
514 .filter(|e| &e.key != key && &e.key != this_key),
515 );
516 vec.sort_by_cached_key(|entry| key - entry.key);
517 vec.into_iter()
518 }
519}
520
521impl<'a, const K: usize> Iterator for ClosestPeers<'a, K> {
522 type Item = &'a P2pNetworkKadEntry;
523
524 fn next(&mut self) -> Option<Self::Item> {
525 Some(loop {
526 if let Some(item) = self.bucket_iterator.next() {
527 break item;
528 }
529 self.bucket_index = self.index_iter.next()?;
530 self.bucket_iterator = Self::get_bucket_iter(
531 &self.table.buckets[self.bucket_index],
532 self.key,
533 &self.table.this_key,
534 );
535 })
536 }
537}
538
539#[derive(Clone, Debug, Default, Serialize, Deserialize, From, MallocSizeOf)]
540pub struct P2pNetworkKadBucket<const K: usize>(Vec<P2pNetworkKadEntry>);
541
542impl<const K: usize> P2pNetworkKadBucket<K> {
543 pub fn len(&self) -> usize {
544 self.0.len()
545 }
546
547 pub fn is_empty(&self) -> bool {
548 self.0.is_empty()
549 }
550
551 pub fn iter(&self) -> std::slice::Iter<'_, P2pNetworkKadEntry> {
552 self.0.iter()
553 }
554
555 fn can_insert(&self, entry: &P2pNetworkKadEntry) -> bool {
559 self.len() < K || self.0.iter().any(|e| e.key == entry.key)
560 }
561
562 fn insert(&mut self, entry: P2pNetworkKadEntry) -> bool {
565 if let Some(pos) = self.0.iter().position(|e| e.key == entry.key) {
566 let e = &mut self.0[pos];
567
568 if e.peer_id != entry.peer_id {
569 bug_condition!(
570 "Kad entry peer_id mismatch {:?} != {:?}",
571 e.peer_id,
572 entry.peer_id
573 );
574 }
575
576 let addrs = entry
577 .addrs
578 .into_iter()
579 .filter(|addr| !e.addrs.contains(addr))
580 .collect::<Vec<_>>();
581
582 for addr in addrs {
583 if e.addrs.len() >= P2pNetworkKadEntry::MAX_ADDRS {
584 openmina_core::warn!(
585 openmina_core::log::system_time();
586 kind = "P2pNetworkKadBucket insert",
587 peer_id = e.peer_id.to_string(),
588 summary = format!("Skipping updates to Kad entry multiaddress list"),
589 );
590 break;
591 }
592
593 if !e.addrs.contains(&addr) {
594 e.addrs.push(addr);
595 }
596 }
597 false
598 } else {
599 if self.len() >= K {
600 bug_condition!("Kad bucket len {:?} >= K ({:?})", self.len(), K);
601 }
602 self.0.push(entry);
603 true
604 }
605 }
606
607 fn split<F: Fn(&P2pNetworkKadEntry) -> bool>(self, f: F) -> (Self, Self) {
610 self.into_iter().partition(f)
611 }
612}
613
614impl<const K: usize> FromIterator<P2pNetworkKadEntry> for P2pNetworkKadBucket<K> {
615 fn from_iter<T: IntoIterator<Item = P2pNetworkKadEntry>>(iter: T) -> Self {
616 P2pNetworkKadBucket(Vec::from_iter(iter))
617 }
618}
619
620impl<const K: usize> IntoIterator for P2pNetworkKadBucket<K> {
621 type Item = P2pNetworkKadEntry;
622
623 type IntoIter = <Vec<P2pNetworkKadEntry> as IntoIterator>::IntoIter;
624
625 fn into_iter(self) -> Self::IntoIter {
626 self.0.into_iter()
627 }
628}
629
630impl<const K: usize> Extend<P2pNetworkKadEntry> for P2pNetworkKadBucket<K> {
631 fn extend<T: IntoIterator<Item = P2pNetworkKadEntry>>(&mut self, iter: T) {
632 self.0.extend(iter)
633 }
634}
635
636impl<'a, const K: usize> IntoIterator for &'a P2pNetworkKadBucket<K> {
637 type Item = &'a P2pNetworkKadEntry;
638
639 type IntoIter = std::slice::Iter<'a, P2pNetworkKadEntry>;
640
641 fn into_iter(self) -> Self::IntoIter {
642 self.0.as_slice().iter()
643 }
644}
645
646#[cfg(test)]
647mod tests {
648 use std::collections::BTreeSet;
649
650 use crypto_bigint::{Random, U256};
651 use multiaddr::multiaddr;
652
653 use crate::{identity::SecretKey, PeerId, CID};
654
655 use super::{P2pNetworkKadEntry, P2pNetworkKadKey, P2pNetworkKadRoutingTable};
656
657 const THIS_KEY: P2pNetworkKadKey = P2pNetworkKadKey(U256::ZERO);
658
659 fn this_key() -> P2pNetworkKadKey {
660 THIS_KEY
661 }
662
663 fn key_pow_2(pow: usize) -> P2pNetworkKadKey {
664 P2pNetworkKadKey(U256::ONE.shl_vartime(pow))
665 }
666
667 fn key_rand() -> P2pNetworkKadKey {
668 P2pNetworkKadKey(U256::random(&mut rand::thread_rng()))
669 }
670
671 fn peer_id_rand() -> PeerId {
672 crate::identity::SecretKey::rand().public_key().peer_id()
673 }
674
675 fn entry(key: P2pNetworkKadKey) -> P2pNetworkKadEntry {
676 let peer_id = peer_id_rand();
677 P2pNetworkKadEntry {
678 key,
679 peer_id,
680 addrs: vec![multiaddr!(Ip4([0; 4]), Tcp(1000_u16))],
681 connection: super::ConnectionType::Connected,
682 }
683 }
684
685 fn entry_with_peer_id(peer_id: PeerId) -> P2pNetworkKadEntry {
686 let key = peer_id.try_into().expect("Error converting PeerId");
687 P2pNetworkKadEntry {
688 key,
689 peer_id,
690 addrs: vec![multiaddr!(Ip4([0; 4]), Tcp(1000_u16))],
691 connection: super::ConnectionType::Connected,
692 }
693 }
694
695 #[test]
696 fn test_key_generation() {
697 let random_peer_id = SecretKey::rand().public_key().peer_id();
698 let libp2p_peer_id =
699 libp2p_identity::PeerId::try_from(random_peer_id).expect("Conversion failed");
700 let cid = CID::from(libp2p_peer_id);
701
702 let key0 = P2pNetworkKadKey::try_from(&random_peer_id).expect("Conversion failed");
703 let key1 = P2pNetworkKadKey::try_from(random_peer_id).expect("Conversion failed");
704 let key2 = P2pNetworkKadKey::from(cid);
705
706 assert_eq!(key0, key1);
707 assert_eq!(key1, key2);
708 }
709
710 #[test]
711 fn test_256_keys() {
712 let mut rt: P2pNetworkKadRoutingTable = P2pNetworkKadRoutingTable::new(entry(this_key()));
713
714 for i in 0..255 {
715 println!("=== adding {i}...");
716 let key = key_pow_2(255 - i);
717 let entry = entry(key);
718 let _ = rt.insert(entry);
719 rt.assert_k_buckets();
720 }
721 println!("routing table: {rt:+#?}");
722 }
723
724 #[test]
725 fn test_256_keys_rev() {
726 let mut rt: P2pNetworkKadRoutingTable = P2pNetworkKadRoutingTable::new(entry(this_key()));
727
728 for i in 0..255 {
729 println!("=== adding {i}...");
730 let key = key_pow_2(i);
731 let entry = entry(key);
732 let _ = rt.insert(entry);
733 rt.assert_k_buckets();
734 }
735 println!("routing table: {rt:+#?}");
736 }
737
738 #[test]
739 fn test_rand_keys() {
740 let mut rt: P2pNetworkKadRoutingTable = P2pNetworkKadRoutingTable::new(entry(this_key()));
741 for _ in 0..(256 * 256) {
742 let key = key_rand();
743 let entry = entry(key);
744 let _ = rt.insert(entry);
745 rt.assert_k_buckets();
746 }
747 println!("routing table: {rt:+#?}");
748 }
749
750 #[test]
751 fn test_rand_peers_rand_this() {
752 let mut rt: P2pNetworkKadRoutingTable =
753 P2pNetworkKadRoutingTable::new(entry_with_peer_id(peer_id_rand()));
754 for _ in 0..(256 * 256) {
755 let peer_id = peer_id_rand();
756 let entry = entry_with_peer_id(peer_id);
757 let _ = rt.insert(entry);
758 rt.assert_k_buckets();
759 }
760 println!("routing table: {rt:+#?}");
761 }
762
763 #[test]
764 fn test_rand_peers() {
765 let mut rt: P2pNetworkKadRoutingTable = P2pNetworkKadRoutingTable::new(entry(this_key()));
766 for _ in 0..(256 * 256) {
767 let peer_id = peer_id_rand();
768 let entry = entry_with_peer_id(peer_id);
769 let _ = rt.insert(entry);
770 rt.assert_k_buckets();
771 }
772 println!("routing table: {rt:+#?}");
773 }
774
775 #[test]
776 fn test_find_node_zero() {
777 let this_entry = entry_with_peer_id(peer_id_rand());
778 let mut rt: P2pNetworkKadRoutingTable = P2pNetworkKadRoutingTable::new(this_entry.clone());
779 for _ in 0..(256 * 32) {
780 let peer_id = peer_id_rand();
781 let entry = entry_with_peer_id(peer_id);
782 let _ = rt.insert(entry);
783 rt.assert_k_buckets();
784 }
785
786 let entry = entry(this_key());
787 let iter = rt.find_node(&entry.key);
788
789 let closest = BTreeSet::from_iter(iter);
798 println!("{}", closest.len());
799
800 let max_closest_dist = closest
801 .iter()
802 .max_by_key(|e| entry.dist(e))
803 .expect("Failed to find entry");
804 let min_non_closest_dist = rt
805 .buckets
806 .iter()
807 .flat_map(|e| e.iter())
808 .filter(|e| !closest.contains(*e))
809 .min_by_key(|e| entry.dist(e))
810 .expect("Failed to find entry");
811
812 let max = entry.dist(max_closest_dist);
813 let min = entry.dist(min_non_closest_dist);
814 println!(
815 "farthest {:#?} is closer than the closest {:#?}",
816 max_closest_dist.key, min_non_closest_dist.key
817 );
818 assert!(min > max);
819 }
820
821 #[test]
822 fn test_find_node_rand() {
823 let mut rt: P2pNetworkKadRoutingTable = P2pNetworkKadRoutingTable::new(entry(this_key()));
824 for _ in 0..(256 * 32) {
825 let peer_id = peer_id_rand();
826 let entry = entry_with_peer_id(peer_id);
827 let _ = rt.insert(entry);
828 rt.assert_k_buckets();
829 }
830
831 for _ in 0..(1024 * 16) {
832 let peer_id = peer_id_rand();
833 let entry = entry_with_peer_id(peer_id);
834
835 let find_node = rt.find_node(&entry.key);
836 let closest = BTreeSet::from_iter(find_node);
837
838 let max_closest_dist = closest
839 .iter()
840 .max_by_key(|e| entry.dist(e))
841 .expect("Error finding entry");
842 let min_non_closest_dist = rt
843 .buckets
844 .iter()
845 .flat_map(|e| e.iter())
846 .filter(|e| e.key != entry.key && e.key != rt.this_key)
847 .filter(|e| !closest.contains(*e))
848 .min_by_key(|e| entry.dist(e))
849 .expect("Error finding entry");
850
851 let max = entry.dist(max_closest_dist);
852 let min = entry.dist(min_non_closest_dist);
853 if max > min {
854 println!(
855 "farthest {:#?} should be closer than the closest {:#?}",
856 max_closest_dist.key, min_non_closest_dist.key
857 );
858 panic!("min is {min:#?}\nmax is {max:#?}");
859 }
860 }
861 }
862
863 #[test]
865 fn test_closest_peers_rand() {
866 let mut rt: P2pNetworkKadRoutingTable = P2pNetworkKadRoutingTable::new(entry(this_key()));
867 for _ in 0..(256 * 32) {
868 let peer_id = peer_id_rand();
869 let entry = entry_with_peer_id(peer_id);
870 let _ = rt.insert(entry);
871 rt.assert_k_buckets();
872 }
873
874 for _ in 0..(16 * 1024) {
875 let peer_id = peer_id_rand();
876 let entry = entry_with_peer_id(peer_id);
877
878 let mut prev = None;
879 for e in rt.find_node(&entry.key) {
880 let dist = entry.dist(e);
881 if let Some((prev, prev_dist)) = prev {
882 if dist <= prev_dist {
883 panic!(
884 "incorrect order\n{:#?}\nshould go before\n{:#?}",
885 e.key, prev
886 );
887 }
888 }
889 prev = Some((e.clone(), dist));
890 }
891 }
892 }
893}
894mod measurement {
895 use std::mem;
896
897 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
898
899 use super::{Multiaddr, P2pNetworkKadBucket, P2pNetworkKadRoutingTable};
900
901 pub fn multiaddr_vec(v: &Vec<Multiaddr>, _ops: &mut MallocSizeOfOps) -> usize {
902 v.capacity() * mem::size_of::<Multiaddr>() + v.iter().map(Multiaddr::len).sum::<usize>()
903 }
904
905 impl<const K: usize> MallocSizeOf for P2pNetworkKadRoutingTable<K> {
906 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
907 self.this_key.size_of(ops)
908 + self.buckets.capacity() * mem::size_of::<P2pNetworkKadBucket<K>>()
909 + self.buckets.iter().map(|b| b.size_of(ops)).sum::<usize>()
910 }
911 }
912}