1use crate::util;
51use alloc::{
52 borrow::ToOwned as _,
53 collections::{BTreeMap, BTreeSet, btree_map},
54 vec::Vec,
55};
56use core::{hash::Hash, iter, ops};
57use rand::seq::IteratorRandom as _;
58use rand_chacha::{
59 ChaCha20Rng,
60 rand_core::{RngCore as _, SeedableRng as _},
61};
62
63pub use crate::libp2p::PeerId;
64
65#[derive(Debug)]
66pub struct BasicPeeringStrategy<TChainId, TInstant> {
67 peer_ids: slab::Slab<PeerId>,
69
70 peer_ids_indices: hashbrown::HashMap<PeerId, usize, util::SipHasherBuild>,
72
73 addresses: BTreeMap<(usize, Vec<u8>), u32>,
76
77 chains: slab::Slab<TChainId>,
84
85 chains_indices: hashbrown::HashMap<TChainId, usize, util::SipHasherBuild>,
89
90 peers_chains: BTreeMap<(usize, usize), PeerChainState<TInstant>>,
93
94 peers_chains_by_state: BTreeSet<(usize, PeerChainState<TInstant>, usize)>,
96
97 randomness: ChaCha20Rng,
99}
100
101#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
102enum PeerChainState<TInstant> {
103 Assignable,
104 Banned { expires: TInstant },
105 Slot,
106}
107
108pub struct Config {
110 pub randomness_seed: [u8; 32],
112
113 pub peers_capacity: usize,
115
116 pub chains_capacity: usize,
118}
119
120impl<TChainId, TInstant> BasicPeeringStrategy<TChainId, TInstant>
121where
122 TChainId: PartialOrd + Ord + Eq + Hash + Clone,
123 TInstant: PartialOrd + Ord + Eq + Clone,
124{
125 pub fn new(config: Config) -> Self {
130 let mut randomness = ChaCha20Rng::from_seed(config.randomness_seed);
131
132 BasicPeeringStrategy {
133 peer_ids: slab::Slab::with_capacity(config.peers_capacity),
134 peer_ids_indices: hashbrown::HashMap::with_capacity_and_hasher(
135 config.peers_capacity,
136 util::SipHasherBuild::new({
137 let mut seed = [0; 16];
138 randomness.fill_bytes(&mut seed);
139 seed
140 }),
141 ),
142 addresses: BTreeMap::new(),
143 chains: slab::Slab::with_capacity(config.chains_capacity),
144 chains_indices: hashbrown::HashMap::with_capacity_and_hasher(
145 config.chains_capacity,
146 util::SipHasherBuild::new({
147 let mut seed = [0; 16];
148 randomness.fill_bytes(&mut seed);
149 seed
150 }),
151 ),
152 peers_chains: BTreeMap::new(),
153 peers_chains_by_state: BTreeSet::new(),
154 randomness,
155 }
156 }
157
158 pub fn remove_chain_peers(&mut self, chain: &TChainId) {
163 let Some(chain_index) = self.chains_indices.remove(chain) else {
164 return;
166 };
167 self.chains.remove(chain_index);
168
169 let chain_peers = {
170 let mut in_chain_and_after_chain = self.peers_chains_by_state.split_off(&(
171 chain_index,
172 PeerChainState::Assignable,
173 usize::MIN,
174 ));
175 let mut after_chain = in_chain_and_after_chain.split_off(&(
176 chain_index + 1,
177 PeerChainState::Assignable,
178 usize::MIN,
179 ));
180 self.peers_chains_by_state.append(&mut after_chain);
181 in_chain_and_after_chain
182 };
183
184 for (_, _, peer_id_index) in chain_peers {
185 let _was_in = self.peers_chains.remove(&(peer_id_index, chain_index));
186 debug_assert!(_was_in.is_some());
187 self.try_clean_up_peer_id(peer_id_index);
188 }
189 }
190
191 pub fn insert_chain_peer(
202 &mut self,
203 chain: TChainId,
204 peer_id: PeerId,
205 max_peers_per_chain: usize,
206 ) -> InsertChainPeerResult {
207 let peer_id_index = self.get_or_insert_peer_index(&peer_id);
208 let chain_index = self.get_or_insert_chain_index(&chain);
209
210 if let btree_map::Entry::Vacant(entry) =
211 self.peers_chains.entry((peer_id_index, chain_index))
212 {
213 let peer_to_remove = if self
214 .peers_chains_by_state
215 .range(
216 (chain_index, PeerChainState::Assignable, usize::MIN)
217 ..=(chain_index, PeerChainState::Slot, usize::MAX),
218 )
219 .count()
220 >= max_peers_per_chain
221 {
222 self.peers_chains_by_state
223 .range(
224 (chain_index, PeerChainState::Assignable, usize::MIN)
225 ..(chain_index, PeerChainState::Slot, usize::MIN),
226 )
227 .choose(&mut self.randomness)
228 .map(|(_, _, peer_index)| *peer_index)
229 } else {
230 None
231 };
232
233 let _was_inserted = self.peers_chains_by_state.insert((
234 chain_index,
235 PeerChainState::Assignable,
236 peer_id_index,
237 ));
238 debug_assert!(_was_inserted);
239
240 entry.insert(PeerChainState::Assignable);
241
242 let peer_removed = if let Some(peer_to_remove) = peer_to_remove {
243 let peer_id_to_remove = self.peer_ids[peer_to_remove].clone();
244 let state = self
245 .peers_chains
246 .remove(&(peer_to_remove, chain_index))
247 .unwrap_or_else(|| unreachable!());
248 debug_assert!(!matches!(state, PeerChainState::Slot));
249 let _was_removed =
250 self.peers_chains_by_state
251 .remove(&(chain_index, state, peer_to_remove));
252 debug_assert!(_was_removed);
253 self.try_clean_up_peer_id(peer_to_remove);
254 Some(peer_id_to_remove)
255 } else {
256 None
257 };
258
259 InsertChainPeerResult::Inserted { peer_removed }
260 } else {
261 InsertChainPeerResult::Duplicate
262 }
263 }
264
265 pub fn unassign_slot_and_remove_chain_peer(
273 &mut self,
274 chain: &TChainId,
275 peer_id: &PeerId,
276 ) -> UnassignSlotAndRemoveChainPeer<TInstant> {
277 let Some(&peer_id_index) = self.peer_ids_indices.get(peer_id) else {
278 return UnassignSlotAndRemoveChainPeer::NotAssigned;
280 };
281
282 let Some(&chain_index) = self.chains_indices.get(chain) else {
283 return UnassignSlotAndRemoveChainPeer::NotAssigned;
285 };
286
287 if let Some(state) = self.peers_chains.remove(&(peer_id_index, chain_index)) {
288 let _was_removed =
289 self.peers_chains_by_state
290 .remove(&(chain_index, state.clone(), peer_id_index));
291 debug_assert!(_was_removed);
292
293 self.try_clean_up_peer_id(peer_id_index);
294 self.try_clean_up_chain(chain_index);
295
296 match state {
297 PeerChainState::Assignable => UnassignSlotAndRemoveChainPeer::Assigned {
298 ban_expiration: None,
299 },
300 PeerChainState::Banned { expires } => UnassignSlotAndRemoveChainPeer::Assigned {
301 ban_expiration: Some(expires),
302 },
303 PeerChainState::Slot => UnassignSlotAndRemoveChainPeer::HadSlot,
304 }
305 } else {
306 UnassignSlotAndRemoveChainPeer::NotAssigned
307 }
308 }
309
310 pub fn chain_peers_unordered(&self, chain: &TChainId) -> impl Iterator<Item = &PeerId> {
315 let Some(&chain_index) = self.chains_indices.get(chain) else {
316 return either::Right(iter::empty());
318 };
319
320 either::Left(
321 self.peers_chains_by_state
322 .range(
323 (chain_index, PeerChainState::Assignable, usize::MIN)
324 ..=(chain_index, PeerChainState::Slot, usize::MAX),
325 )
326 .map(|(_, _, p)| &self.peer_ids[*p]),
327 )
328 }
329
330 pub fn insert_address(
344 &mut self,
345 peer_id: &PeerId,
346 address: Vec<u8>,
347 max_addresses: usize,
348 ) -> InsertAddressResult {
349 let Some(&peer_id_index) = self.peer_ids_indices.get(peer_id) else {
350 return InsertAddressResult::UnknownPeer;
351 };
352
353 match self.insert_address_inner(peer_id_index, address, max_addresses, 0, false) {
354 InsertAddressConnectionsResult::AlreadyKnown => InsertAddressResult::AlreadyKnown,
355 InsertAddressConnectionsResult::Inserted { address_removed } => {
356 InsertAddressResult::Inserted { address_removed }
357 }
358 }
359 }
360
361 pub fn increase_address_connections(
375 &mut self,
376 peer_id: &PeerId,
377 address: Vec<u8>,
378 max_addresses: usize,
379 ) -> InsertAddressConnectionsResult {
380 let peer_id_index = self.get_or_insert_peer_index(peer_id);
381 self.insert_address_inner(peer_id_index, address, max_addresses, 1, true)
382 }
383
384 fn insert_address_inner(
385 &mut self,
386 peer_id_index: usize,
387 address: Vec<u8>,
388 max_addresses: usize,
389 initial_num_connections: u32,
390 increase_if_present: bool,
391 ) -> InsertAddressConnectionsResult {
392 match self.addresses.entry((peer_id_index, address.clone())) {
393 btree_map::Entry::Vacant(entry) => {
394 entry.insert(initial_num_connections);
395
396 let address_removed = {
397 let num_addresses = self
398 .addresses
399 .range((peer_id_index, Vec::new())..=(peer_id_index + 1, Vec::new()))
400 .count();
401
402 if num_addresses >= max_addresses {
403 self.addresses
405 .range((peer_id_index, Vec::new())..=(peer_id_index + 1, Vec::new()))
406 .filter(|((_, a), n)| **n == 0 && *a != address)
407 .choose(&mut self.randomness)
408 .map(|((_, a), _)| a.clone())
409 } else {
410 None
411 }
412 };
413
414 if let Some(address_removed) = address_removed.as_ref() {
415 self.addresses
416 .remove(&(peer_id_index, address_removed.clone()));
417 }
418
419 InsertAddressConnectionsResult::Inserted { address_removed }
420 }
421 btree_map::Entry::Occupied(entry) => {
422 let entry = entry.into_mut();
423 if increase_if_present {
424 *entry = entry
425 .checked_add(1)
426 .unwrap_or_else(|| panic!("overflow in number of connections"));
427 }
428
429 InsertAddressConnectionsResult::AlreadyKnown
430 }
431 }
432 }
433
434 pub fn peer_addresses(&self, peer_id: &PeerId) -> impl Iterator<Item = &[u8]> {
436 let Some(&peer_id_index) = self.peer_ids_indices.get(peer_id) else {
437 return either::Right(iter::empty());
439 };
440
441 either::Left(
442 self.addresses
443 .range((peer_id_index, Vec::new())..(peer_id_index + 1, Vec::new()))
444 .map(|((_, a), _)| &a[..]),
445 )
446 }
447
448 pub fn pick_assignable_peer(
461 &mut self,
462 chain: &TChainId,
463 now: &TInstant,
464 ) -> AssignablePeer<'_, TInstant> {
465 let Some(&chain_index) = self.chains_indices.get(chain) else {
466 return AssignablePeer::NoPeer;
467 };
468
469 if let Some((_, _, peer_id_index)) = self
470 .peers_chains_by_state
471 .range(
472 (chain_index, PeerChainState::Assignable, usize::MIN)
473 ..=(
474 chain_index,
475 PeerChainState::Banned {
476 expires: now.clone(),
477 },
478 usize::MAX,
479 ),
480 )
481 .choose(&mut self.randomness)
482 {
483 return AssignablePeer::Assignable(&self.peer_ids[*peer_id_index]);
484 }
485
486 if let Some((_, state, _)) = self
487 .peers_chains_by_state
488 .range((
489 ops::Bound::Excluded((
490 chain_index,
491 PeerChainState::Banned {
492 expires: now.clone(),
493 },
494 usize::MAX,
495 )),
496 ops::Bound::Excluded((chain_index, PeerChainState::Slot, usize::MIN)),
497 ))
498 .next()
499 {
500 let PeerChainState::Banned { expires } = state else {
501 unreachable!()
502 };
503 AssignablePeer::AllPeersBanned {
504 next_unban: expires,
505 }
506 } else {
507 AssignablePeer::NoPeer
508 }
509 }
510
511 pub fn assign_slot(&mut self, chain: &TChainId, peer_id: &PeerId) {
518 let peer_id_index = self.get_or_insert_peer_index(peer_id);
519 let chain_index = self.get_or_insert_chain_index(chain);
520
521 match self.peers_chains.entry((peer_id_index, chain_index)) {
522 btree_map::Entry::Occupied(e) => {
523 let _was_removed = self.peers_chains_by_state.remove(&(
524 chain_index,
525 e.get().clone(),
526 peer_id_index,
527 ));
528 debug_assert!(_was_removed);
529 *e.into_mut() = PeerChainState::Slot;
530 }
531 btree_map::Entry::Vacant(e) => {
532 e.insert(PeerChainState::Slot);
533 }
534 }
535
536 let _was_inserted =
537 self.peers_chains_by_state
538 .insert((chain_index, PeerChainState::Slot, peer_id_index));
539 debug_assert!(_was_inserted);
540 }
541
542 pub fn unassign_slot_and_ban(
551 &mut self,
552 chain: &TChainId,
553 peer_id: &PeerId,
554 when_unban: TInstant,
555 ) -> UnassignSlotAndBan<TInstant> {
556 let (Some(&peer_id_index), Some(&chain_index)) = (
557 self.peer_ids_indices.get(peer_id),
558 self.chains_indices.get(chain),
559 ) else {
560 return UnassignSlotAndBan::NotAssigned;
561 };
562
563 if let Some(state) = self.peers_chains.get_mut(&(peer_id_index, chain_index)) {
564 let return_value = match state {
565 PeerChainState::Banned { expires } if *expires >= when_unban => {
566 return UnassignSlotAndBan::AlreadyBanned {
568 when_unban: expires.clone(),
569 ban_extended: false,
570 };
571 }
572 PeerChainState::Banned { .. } => UnassignSlotAndBan::AlreadyBanned {
573 when_unban: when_unban.clone(),
574 ban_extended: true,
575 },
576 PeerChainState::Assignable => UnassignSlotAndBan::Banned { had_slot: false },
577 PeerChainState::Slot => UnassignSlotAndBan::Banned { had_slot: true },
578 };
579
580 let _was_in =
581 self.peers_chains_by_state
582 .remove(&(chain_index, state.clone(), peer_id_index));
583 debug_assert!(_was_in);
584
585 *state = PeerChainState::Banned {
586 expires: when_unban,
587 };
588
589 let _was_inserted =
590 self.peers_chains_by_state
591 .insert((chain_index, state.clone(), peer_id_index));
592 debug_assert!(_was_inserted);
593
594 return_value
595 } else {
596 UnassignSlotAndBan::NotAssigned
597 }
598 }
599
600 pub fn unassign_slots_and_ban(
614 &'_ mut self,
615 peer_id: &PeerId,
616 when_unban: TInstant,
617 ) -> UnassignSlotsAndBanIter<'_, TChainId, TInstant> {
618 let Some(&peer_id_index) = self.peer_ids_indices.get(peer_id) else {
619 return UnassignSlotsAndBanIter {
620 chains: &self.chains,
621 peers_chains_by_state: &mut self.peers_chains_by_state,
622 inner_iter: None,
623 peer_id_index: 0,
624 when_unban,
625 };
626 };
627
628 UnassignSlotsAndBanIter {
629 chains: &self.chains,
630 peers_chains_by_state: &mut self.peers_chains_by_state,
631 inner_iter: Some(
632 self.peers_chains
633 .range_mut((peer_id_index, usize::MIN)..=(peer_id_index, usize::MAX))
634 .fuse(),
635 ),
636 peer_id_index,
637 when_unban,
638 }
639 }
640
641 pub fn pick_address_and_add_connection(&mut self, peer_id: &PeerId) -> Option<&[u8]> {
644 let Some(&peer_id_index) = self.peer_ids_indices.get(peer_id) else {
645 return None;
647 };
648
649 if let Some(((_, address), num_connections)) = self
651 .addresses
652 .range_mut((peer_id_index, Vec::new())..(peer_id_index + 1, Vec::new()))
653 .filter(|(_, num_connections)| **num_connections == 0)
654 .choose(&mut self.randomness)
655 {
656 *num_connections = 1;
657 return Some(address);
658 }
659
660 None
661 }
662
663 pub fn decrease_address_connections(
668 &mut self,
669 peer_id: &PeerId,
670 address: &[u8],
671 ) -> Result<(), DecreaseAddressConnectionsError> {
672 self.decrease_address_connections_inner(peer_id, address, false)
673 }
674
675 pub fn decrease_address_connections_and_remove_if_zero(
681 &mut self,
682 peer_id: &PeerId,
683 address: &[u8],
684 ) -> Result<(), DecreaseAddressConnectionsError> {
685 self.decrease_address_connections_inner(peer_id, address, true)
686 }
687
688 fn decrease_address_connections_inner(
689 &mut self,
690 peer_id: &PeerId,
691 address: &[u8],
692 remove_if_reaches_zero: bool,
693 ) -> Result<(), DecreaseAddressConnectionsError> {
694 let Some(&peer_id_index) = self.peer_ids_indices.get(peer_id) else {
695 return Err(DecreaseAddressConnectionsError::UnknownAddress);
697 };
698
699 let Some(num_connections) = self.addresses.get_mut(&(peer_id_index, address.to_owned()))
700 else {
701 return Err(DecreaseAddressConnectionsError::UnknownAddress);
702 };
703
704 if *num_connections == 0 {
705 return Err(DecreaseAddressConnectionsError::NotConnected);
706 }
707
708 *num_connections -= 1;
709
710 if *num_connections != 0 {
711 return Ok(());
712 }
713
714 if remove_if_reaches_zero {
715 self.addresses.remove(&(peer_id_index, address.to_owned()));
716 }
717
718 self.try_clean_up_peer_id(peer_id_index);
719 Ok(())
720 }
721
722 fn get_or_insert_chain_index(&mut self, chain: &TChainId) -> usize {
725 debug_assert_eq!(self.chains.len(), self.chains_indices.len());
726
727 match self.chains_indices.raw_entry_mut().from_key(chain) {
728 hashbrown::hash_map::RawEntryMut::Occupied(occupied_entry) => *occupied_entry.get(),
729 hashbrown::hash_map::RawEntryMut::Vacant(vacant_entry) => {
730 let idx = self.chains.insert(chain.clone());
731 vacant_entry.insert(chain.clone(), idx);
732 idx
733 }
734 }
735 }
736
737 fn try_clean_up_chain(&mut self, chain_index: usize) {
740 if self
741 .peers_chains_by_state
742 .range(
743 (chain_index, PeerChainState::Assignable, usize::MIN)
744 ..=(chain_index, PeerChainState::Slot, usize::MAX),
745 )
746 .next()
747 .is_some()
748 {
749 return;
750 }
751
752 let chain_id = self.chains.remove(chain_index);
754 let _was_in = self.chains_indices.remove(&chain_id);
755 debug_assert_eq!(_was_in, Some(chain_index));
756 }
757
758 fn get_or_insert_peer_index(&mut self, peer_id: &PeerId) -> usize {
761 debug_assert_eq!(self.peer_ids.len(), self.peer_ids_indices.len());
762
763 match self.peer_ids_indices.raw_entry_mut().from_key(peer_id) {
764 hashbrown::hash_map::RawEntryMut::Occupied(occupied_entry) => *occupied_entry.get(),
765 hashbrown::hash_map::RawEntryMut::Vacant(vacant_entry) => {
766 let idx = self.peer_ids.insert(peer_id.clone());
767 vacant_entry.insert(peer_id.clone(), idx);
768 idx
769 }
770 }
771 }
772
773 fn try_clean_up_peer_id(&mut self, peer_id_index: usize) {
776 if self
777 .peers_chains
778 .range((peer_id_index, usize::MIN)..=(peer_id_index, usize::MAX))
779 .next()
780 .is_some()
781 {
782 return;
783 }
784
785 if self
786 .addresses
787 .range((peer_id_index, Vec::new())..(peer_id_index + 1, Vec::new()))
788 .any(|(_, num_connections)| *num_connections >= 1)
789 {
790 return;
791 }
792
793 let peer_id = self.peer_ids.remove(peer_id_index);
795 let _was_in = self.peer_ids_indices.remove(&peer_id);
796 debug_assert_eq!(_was_in, Some(peer_id_index));
797 for address in self
798 .addresses
799 .range((peer_id_index, Vec::new())..(peer_id_index + 1, Vec::new()))
800 .map(|((_, a), _)| a.clone())
801 .collect::<Vec<_>>()
802 {
803 let _was_removed = self.addresses.remove(&(peer_id_index, address));
804 debug_assert!(_was_removed.is_some());
805 }
806 }
807}
808
809#[derive(Debug, derive_more::Display, derive_more::Error)]
811pub enum DecreaseAddressConnectionsError {
812 UnknownAddress,
814 NotConnected,
816}
817
818pub enum AssignablePeer<'a, TInstant> {
820 Assignable(&'a PeerId),
822 AllPeersBanned {
824 next_unban: &'a TInstant,
826 },
827 NoPeer,
829}
830
831pub enum InsertChainPeerResult {
833 Inserted {
835 peer_removed: Option<PeerId>,
838 },
839 Duplicate,
841}
842
843pub enum InsertAddressResult {
845 Inserted {
847 address_removed: Option<Vec<u8>>,
850 },
851 AlreadyKnown,
853 UnknownPeer,
855}
856
857pub enum InsertAddressConnectionsResult {
859 Inserted {
861 address_removed: Option<Vec<u8>>,
864 },
865 AlreadyKnown,
867}
868
869pub enum UnassignSlotAndBan<TInstant> {
871 NotAssigned,
873 AlreadyBanned {
875 when_unban: TInstant,
877 ban_extended: bool,
880 },
881 Banned {
883 had_slot: bool,
885 },
886}
887
888impl<TInstant> UnassignSlotAndBan<TInstant> {
889 pub fn had_slot(&self) -> bool {
891 matches!(self, UnassignSlotAndBan::Banned { had_slot: true })
892 }
893}
894
895pub enum UnassignSlotAndRemoveChainPeer<TInstant> {
897 NotAssigned,
899 Assigned {
901 ban_expiration: Option<TInstant>,
903 },
904 HadSlot,
906}
907
908pub struct UnassignSlotsAndBanIter<'a, TChainId, TInstant>
910where
911 TInstant: PartialOrd + Ord + Eq + Clone,
912{
913 chains: &'a slab::Slab<TChainId>,
915 peers_chains_by_state: &'a mut BTreeSet<(usize, PeerChainState<TInstant>, usize)>,
917 inner_iter:
919 Option<iter::Fuse<btree_map::RangeMut<'a, (usize, usize), PeerChainState<TInstant>>>>,
920 peer_id_index: usize,
923 when_unban: TInstant,
925}
926
927pub enum UnassignSlotsAndBan<TInstant> {
929 AlreadyBanned {
931 when_unban: TInstant,
933 ban_extended: bool,
936 },
937 Banned {
939 had_slot: bool,
941 },
942}
943
944impl<'a, TChainId, TInstant> Iterator for UnassignSlotsAndBanIter<'a, TChainId, TInstant>
945where
946 TInstant: PartialOrd + Ord + Eq + Clone,
947{
948 type Item = (&'a TChainId, UnassignSlotsAndBan<TInstant>);
949
950 fn next(&mut self) -> Option<Self::Item> {
951 let inner_iter = self.inner_iter.as_mut()?;
952 let (&(_, chain_index), state) = inner_iter.next()?;
953
954 let return_value = match state {
955 PeerChainState::Banned { expires } if *expires >= self.when_unban => {
956 return Some((
958 &self.chains[chain_index],
959 UnassignSlotsAndBan::AlreadyBanned {
960 when_unban: expires.clone(),
961 ban_extended: false,
962 },
963 ));
964 }
965 PeerChainState::Banned { .. } => UnassignSlotsAndBan::AlreadyBanned {
966 when_unban: self.when_unban.clone(),
967 ban_extended: true,
968 },
969 PeerChainState::Assignable => UnassignSlotsAndBan::Banned { had_slot: false },
970 PeerChainState::Slot => UnassignSlotsAndBan::Banned { had_slot: true },
971 };
972
973 let _was_in =
974 self.peers_chains_by_state
975 .remove(&(chain_index, state.clone(), self.peer_id_index));
976 debug_assert!(_was_in);
977
978 *state = PeerChainState::Banned {
979 expires: self.when_unban.clone(),
980 };
981
982 let _was_inserted =
983 self.peers_chains_by_state
984 .insert((chain_index, state.clone(), self.peer_id_index));
985 debug_assert!(_was_inserted);
986
987 Some((&self.chains[chain_index], return_value))
988 }
989
990 fn size_hint(&self) -> (usize, Option<usize>) {
991 self.inner_iter
992 .as_ref()
993 .map_or((0, Some(0)), |inner| inner.size_hint())
994 }
995}
996
997impl<'a, TChainId, TInstant> iter::FusedIterator for UnassignSlotsAndBanIter<'a, TChainId, TInstant> where
998 TInstant: PartialOrd + Ord + Eq + Clone
999{
1000}
1001
1002impl<'a, TChainId, TInstant> Drop for UnassignSlotsAndBanIter<'a, TChainId, TInstant>
1003where
1004 TInstant: PartialOrd + Ord + Eq + Clone,
1005{
1006 fn drop(&mut self) {
1007 while let Some(_) = self.next() {}
1009 }
1010}
1011
1012#[cfg(test)]
1013mod tests {
1014 use super::{
1015 BasicPeeringStrategy, Config, InsertAddressConnectionsResult, InsertAddressResult,
1016 InsertChainPeerResult,
1017 };
1018 use crate::network::service::{PeerId, peer_id::PublicKey};
1019 use core::time::Duration;
1020
1021 #[test]
1022 fn peer_state_ordering() {
1023 use super::PeerChainState;
1025 assert!(PeerChainState::Assignable < PeerChainState::Banned { expires: 0 });
1026 assert!(PeerChainState::Banned { expires: 5 } < PeerChainState::Banned { expires: 7 });
1027 assert!(PeerChainState::Banned { expires: u32::MAX } < PeerChainState::Slot);
1028 }
1029
1030 #[test]
1031 fn addresses_removed_when_peer_has_no_chain_association() {
1032 let mut bps = BasicPeeringStrategy::<u32, Duration>::new(Config {
1033 randomness_seed: [0; 32],
1034 peers_capacity: 0,
1035 chains_capacity: 0,
1036 });
1037
1038 let peer_id = PeerId::from_public_key(&PublicKey::Ed25519([0; 32]));
1039
1040 assert!(matches!(
1041 bps.insert_chain_peer(0, peer_id.clone(), usize::MAX),
1042 InsertChainPeerResult::Inserted { peer_removed: None }
1043 ));
1044
1045 assert!(matches!(
1046 bps.insert_address(&peer_id, Vec::new(), usize::MAX),
1047 InsertAddressResult::Inserted {
1048 address_removed: None
1049 }
1050 ));
1051
1052 assert_eq!(bps.peer_addresses(&peer_id).count(), 1);
1053 bps.unassign_slot_and_remove_chain_peer(&0, &peer_id);
1054 assert_eq!(bps.peer_addresses(&peer_id).count(), 0);
1055 }
1056
1057 #[test]
1058 fn addresses_not_removed_if_connected_when_peer_has_no_chain_association() {
1059 let mut bps = BasicPeeringStrategy::<u32, Duration>::new(Config {
1060 randomness_seed: [0; 32],
1061 peers_capacity: 0,
1062 chains_capacity: 0,
1063 });
1064
1065 let peer_id = PeerId::from_public_key(&PublicKey::Ed25519([0; 32]));
1066
1067 assert!(matches!(
1068 bps.insert_chain_peer(0, peer_id.clone(), usize::MAX),
1069 InsertChainPeerResult::Inserted { peer_removed: None }
1070 ));
1071
1072 assert!(matches!(
1073 bps.increase_address_connections(&peer_id, Vec::new(), usize::MAX),
1074 InsertAddressConnectionsResult::Inserted {
1075 address_removed: None
1076 }
1077 ));
1078
1079 assert!(matches!(
1080 bps.insert_address(&peer_id, vec![1], usize::MAX),
1081 InsertAddressResult::Inserted {
1082 address_removed: None
1083 }
1084 ));
1085
1086 assert_eq!(bps.peer_addresses(&peer_id).count(), 2);
1087 bps.unassign_slot_and_remove_chain_peer(&0, &peer_id);
1088 assert_eq!(bps.peer_addresses(&peer_id).count(), 2);
1089
1090 bps.decrease_address_connections(&peer_id, &[]).unwrap();
1091 assert_eq!(bps.peer_addresses(&peer_id).count(), 0);
1092 }
1093
1094 #[test]
1095 fn address_not_inserted_when_peer_has_no_chain_association() {
1096 let mut bps = BasicPeeringStrategy::<u32, Duration>::new(Config {
1097 randomness_seed: [0; 32],
1098 peers_capacity: 0,
1099 chains_capacity: 0,
1100 });
1101
1102 let peer_id = PeerId::from_public_key(&PublicKey::Ed25519([0; 32]));
1103
1104 assert!(matches!(
1105 bps.insert_address(&peer_id, Vec::new(), usize::MAX),
1106 InsertAddressResult::UnknownPeer
1107 ));
1108
1109 assert_eq!(bps.peer_addresses(&peer_id).count(), 0);
1110 }
1111
1112 #[test]
1113 fn address_connections_inserted_when_peer_has_no_chain_association() {
1114 let mut bps = BasicPeeringStrategy::<u32, Duration>::new(Config {
1115 randomness_seed: [0; 32],
1116 peers_capacity: 0,
1117 chains_capacity: 0,
1118 });
1119
1120 let peer_id = PeerId::from_public_key(&PublicKey::Ed25519([0; 32]));
1121
1122 assert!(matches!(
1123 bps.increase_address_connections(&peer_id, Vec::new(), usize::MAX),
1124 InsertAddressConnectionsResult::Inserted { .. }
1125 ));
1126
1127 assert_eq!(bps.peer_addresses(&peer_id).count(), 1);
1128 }
1129
1130 }