1#![deny(missing_docs)]
2use num_bigint::BigUint;
22
23use super::Did;
24
25pub const RING_BITS: usize = 160;
27
28pub const DEFAULT_SUCCESSOR_CAPACITY: usize = 3;
30
31#[derive(Clone, Debug, PartialEq, Eq)]
33pub struct TopologyState {
34 pub local: Did,
36 pub successors: Vec<Did>,
38 pub predecessor: Option<Did>,
40 pub fingers: Vec<Option<Did>>,
42 pub fix_finger_index: usize,
44}
45
46impl TopologyState {
47 pub fn new(
49 local: Did,
50 successors: Vec<Did>,
51 predecessor: Option<Did>,
52 fingers: Vec<Option<Did>>,
53 fix_finger_index: usize,
54 ) -> Self {
55 Self {
56 local,
57 successors,
58 predecessor,
59 fingers,
60 fix_finger_index,
61 }
62 }
63}
64
65#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67pub enum FindSuccessorStep {
68 Local(Did),
70 Remote {
72 next: Did,
74 did: Did,
76 },
77}
78
79#[derive(Clone, Debug, PartialEq, Eq)]
81pub enum TopologyEvent {
82 Join {
84 peer: Did,
86 },
87 Admit {
89 peer: Did,
91 fixed_fingers: Vec<ConditionalFingerUpdate>,
93 },
94 Remove {
96 peer: Did,
98 successor: SuccessorRemoval,
100 },
101 UpdateSuccessor {
103 successor: Did,
105 },
106 Notify {
108 predecessor: Did,
110 },
111 Stabilize {
114 successors: Vec<Did>,
116 predecessor: Option<Did>,
118 },
119 FixFinger,
121 ApplyFinger {
123 index: usize,
125 successor: Did,
127 },
128}
129
130#[derive(Clone, Copy, Debug, PartialEq, Eq)]
133pub struct ConditionalFingerUpdate {
134 pub index: usize,
136 pub expected: Option<Did>,
138}
139
140#[derive(Clone, Debug, PartialEq, Eq)]
142pub enum SuccessorRemoval {
143 Preserve,
145 ReplaceWith(Vec<Did>),
150}
151
152#[derive(Clone, Copy, Debug, PartialEq, Eq)]
154pub enum TopologyAction {
155 FindSuccessorForConnect {
157 next: Did,
159 did: Did,
161 },
162 FindSuccessorForFix {
164 next: Did,
166 did: Did,
168 index: usize,
170 },
171 QuerySuccessorList(Did),
173 Notify(Did),
175}
176
177#[derive(Clone, Debug, PartialEq, Eq)]
179pub struct TopologyStep {
180 pub state: TopologyState,
182 pub actions: Vec<TopologyAction>,
184}
185
186pub fn dist(a: Did, b: Did) -> BigUint {
188 BigUint::from(b - a)
189}
190
191fn push_unique(xs: &mut Vec<Did>, x: Did) {
192 if !xs.contains(&x) {
193 xs.push(x);
194 }
195}
196
197fn sorted_successors(mut candidates: Vec<Did>, local: Did, capacity: usize) -> Vec<Did> {
198 candidates.retain(|&did| did != local);
199 candidates.sort_by_key(|&did| dist(local, did));
200 candidates.dedup();
201 candidates.truncate(capacity);
202 candidates
203}
204
205pub fn successors(all: &[Did], n: Did, capacity: usize) -> Vec<Did> {
207 sorted_successors(all.to_vec(), n, capacity)
208}
209
210pub fn predecessor(all: &[Did], n: Did) -> Option<Did> {
212 all.iter()
213 .copied()
214 .filter(|&did| did != n)
215 .max_by_key(|&did| dist(n, did))
216}
217
218pub fn finger(all: &[Did], n: Did, bit: usize) -> Option<Did> {
223 let threshold = BigUint::from(1u8) << bit;
224 all.iter()
225 .copied()
226 .filter(|&did| did != n && dist(n, did) >= threshold)
227 .min_by_key(|&did| dist(n, did))
228}
229
230pub fn finger_table(all: &[Did], n: Did) -> Vec<Option<Did>> {
232 (0..RING_BITS).map(|bit| finger(all, n, bit)).collect()
233}
234
235pub fn update_successors(local: Did, current: &[Did], candidate: Did, capacity: usize) -> Vec<Did> {
237 let mut candidates = current.to_vec();
238 push_unique(&mut candidates, candidate);
239 sorted_successors(candidates, local, capacity)
240}
241
242fn finger_join(local: Did, current: &[Option<Did>], peer: Did) -> Vec<Option<Did>> {
243 let bias = dist(local, peer);
244 current
245 .iter()
246 .copied()
247 .enumerate()
248 .map(|(slot, old)| {
249 let pos = BigUint::from(Did::power_of_two(slot));
250 if bias < pos || peer == local {
251 old
252 } else {
253 match old {
254 Some(existing) if dist(local, existing) < bias => old,
255 _ => Some(peer),
256 }
257 }
258 })
259 .collect()
260}
261
262pub(crate) fn remove_finger_peer(current: &[Option<Did>], peer: Did) -> Vec<Option<Did>> {
265 let mut next = current.to_vec();
266 let mut index = 0;
267 while index < next.len() {
268 if next.get(index).copied().flatten() != Some(peer) {
269 index = index.saturating_add(1);
270 continue;
271 }
272
273 let run_start = index;
274 while next.get(index).copied().flatten() == Some(peer) {
275 index = index.saturating_add(1);
276 }
277 let replacement = next.get(index).copied().flatten();
278 for slot in next.iter_mut().take(index).skip(run_start) {
279 *slot = replacement;
280 }
281 }
282 next
283}
284
285fn finger_set(
286 local: Did,
287 current: &[Option<Did>],
288 index: usize,
289 successor: Did,
290) -> Vec<Option<Did>> {
291 let mut next = current.to_vec();
292 if successor != local {
293 if let Some(slot) = next.get_mut(index) {
294 *slot = Some(successor);
295 }
296 }
297 next
298}
299
300pub fn find_successor(state: &TopologyState, did: Did) -> FindSuccessorStep {
302 let head = state.successors.first().copied().unwrap_or(state.local);
303 if state.successors.is_empty() || dist(state.local, did) <= dist(state.local, head) {
304 FindSuccessorStep::Local(head)
305 } else {
306 let next = state
307 .fingers
308 .iter()
309 .rev()
310 .flatten()
311 .copied()
312 .find(|peer| dist(state.local, *peer) < dist(state.local, did))
313 .unwrap_or(state.local);
314 FindSuccessorStep::Remote { next, did }
315 }
316}
317
318pub fn rectify_predecessor(local: Did, current: Option<Did>, candidate: Did) -> Option<Did> {
320 match current {
321 Some(cur) if dist(local, cur) >= dist(local, candidate) => Some(cur),
322 _ => Some(candidate),
323 }
324}
325
326pub fn stabilize_successors(
328 local: Did,
329 current: &[Did],
330 topo_successors: &[Did],
331 topo_predecessor: Option<Did>,
332 capacity: usize,
333) -> Vec<Did> {
334 let mut known = vec![local];
335 for &did in current {
336 push_unique(&mut known, did);
337 }
338 if let Some(pred) = topo_predecessor {
339 push_unique(&mut known, pred);
340 }
341 for &did in topo_successors
342 .iter()
343 .take(topo_successors.len().saturating_sub(1))
344 {
345 push_unique(&mut known, did);
346 }
347 successors(&known, local, capacity)
348}
349
350pub fn stabilize_query(local: Did, current: &[Did], topo_predecessor: Option<Did>) -> Option<Did> {
352 let pred = topo_predecessor?;
353 if pred == local {
354 return None;
355 }
356 let old_head = current.iter().copied().min_by_key(|&did| dist(local, did));
357 match old_head {
358 Some(head) if dist(local, pred) >= dist(local, head) => None,
359 _ => Some(pred),
360 }
361}
362
363pub fn stabilize_notify(local: Did, next_successors: &[Did]) -> Option<Did> {
365 next_successors.first().copied().filter(|&did| did != local)
366}
367
368fn step_join(state: &TopologyState, peer: Did, capacity: usize) -> TopologyStep {
369 if peer == state.local {
370 return TopologyStep {
371 state: state.clone(),
372 actions: Vec::new(),
373 };
374 }
375 TopologyStep {
376 state: TopologyState {
377 successors: update_successors(state.local, &state.successors, peer, capacity),
378 fingers: finger_join(state.local, &state.fingers, peer),
379 ..state.clone()
380 },
381 actions: vec![TopologyAction::FindSuccessorForConnect {
382 next: peer,
383 did: state.local,
384 }],
385 }
386}
387
388fn step_admit(
389 state: &TopologyState,
390 peer: Did,
391 fixed_fingers: &[ConditionalFingerUpdate],
392 capacity: usize,
393) -> TopologyStep {
394 if peer == state.local {
395 return TopologyStep {
396 state: state.clone(),
397 actions: Vec::new(),
398 };
399 }
400
401 let successors = update_successors(state.local, &state.successors, peer, capacity);
402 let inserted = !state.successors.contains(&peer) && successors.contains(&peer);
403 let mut fingers = finger_join(state.local, &state.fingers, peer);
404 for update in fixed_fingers {
405 if state.fingers.get(update.index).copied().flatten() == update.expected {
406 fingers = finger_set(state.local, &fingers, update.index, peer);
407 }
408 }
409
410 let mut actions = Vec::new();
411 if inserted {
412 actions.push(TopologyAction::QuerySuccessorList(peer));
413 }
414 actions.push(TopologyAction::FindSuccessorForConnect {
415 next: peer,
416 did: state.local,
417 });
418 TopologyStep {
419 state: TopologyState {
420 successors,
421 fingers,
422 ..state.clone()
423 },
424 actions,
425 }
426}
427
428fn step_remove(
429 state: &TopologyState,
430 peer: Did,
431 successor: SuccessorRemoval,
432 capacity: usize,
433) -> TopologyStep {
434 let removed_head = state.successors.first().copied() == Some(peer);
435 let mut next_successors = state
436 .successors
437 .iter()
438 .copied()
439 .filter(|&did| did != peer)
440 .collect::<Vec<_>>();
441 let fingers = remove_finger_peer(&state.fingers, peer);
442 if removed_head {
443 match successor {
444 SuccessorRemoval::Preserve => {}
445 SuccessorRemoval::ReplaceWith(mut validated) => {
446 validated.retain(|candidate| *candidate != peer);
447 next_successors = sorted_successors(validated, state.local, capacity);
448 }
449 }
450 }
451 TopologyStep {
452 state: TopologyState {
453 successors: next_successors,
454 predecessor: state.predecessor.filter(|&did| did != peer),
455 fingers,
456 ..state.clone()
457 },
458 actions: Vec::new(),
459 }
460}
461
462fn step_update_successor(state: &TopologyState, successor: Did, capacity: usize) -> TopologyStep {
463 let next_successors = update_successors(state.local, &state.successors, successor, capacity);
464 let inserted = !state.successors.contains(&successor) && next_successors.contains(&successor);
465 TopologyStep {
466 state: TopologyState {
467 successors: next_successors,
468 ..state.clone()
469 },
470 actions: if inserted {
471 vec![TopologyAction::QuerySuccessorList(successor)]
472 } else {
473 Vec::new()
474 },
475 }
476}
477
478fn step_fix_finger(state: &TopologyState) -> TopologyStep {
479 if state.fingers.is_empty() {
480 return TopologyStep {
481 state: state.clone(),
482 actions: Vec::new(),
483 };
484 }
485 let index = (state.fix_finger_index + 1) % state.fingers.len();
486 let did = state.local + Did::power_of_two(index);
487 match find_successor(state, did) {
488 FindSuccessorStep::Local(successor) => TopologyStep {
489 state: TopologyState {
490 fingers: finger_set(state.local, &state.fingers, index, successor),
491 fix_finger_index: index,
492 ..state.clone()
493 },
494 actions: Vec::new(),
495 },
496 FindSuccessorStep::Remote { next, did } => TopologyStep {
497 state: TopologyState {
498 fix_finger_index: index,
499 ..state.clone()
500 },
501 actions: vec![TopologyAction::FindSuccessorForFix { next, did, index }],
502 },
503 }
504}
505
506pub fn step(state: &TopologyState, event: TopologyEvent, capacity: usize) -> TopologyStep {
511 match event {
512 TopologyEvent::Join { peer } => step_join(state, peer, capacity),
513 TopologyEvent::Admit {
514 peer,
515 fixed_fingers,
516 } => step_admit(state, peer, &fixed_fingers, capacity),
517 TopologyEvent::Remove { peer, successor } => step_remove(state, peer, successor, capacity),
518 TopologyEvent::UpdateSuccessor { successor } => {
519 step_update_successor(state, successor, capacity)
520 }
521 TopologyEvent::Notify { predecessor } => TopologyStep {
522 state: TopologyState {
523 predecessor: rectify_predecessor(state.local, state.predecessor, predecessor),
524 ..state.clone()
525 },
526 actions: Vec::new(),
527 },
528 TopologyEvent::Stabilize {
529 successors: topo_successors,
530 predecessor: topo_predecessor,
531 } => {
532 let next_successors = stabilize_successors(
533 state.local,
534 &state.successors,
535 &topo_successors,
536 topo_predecessor,
537 capacity,
538 );
539 let mut actions = Vec::new();
540 if let Some(query) = stabilize_query(state.local, &state.successors, topo_predecessor) {
541 actions.push(TopologyAction::QuerySuccessorList(query));
542 }
543 if let Some(notify) = stabilize_notify(state.local, &next_successors) {
544 actions.push(TopologyAction::Notify(notify));
545 }
546 TopologyStep {
547 state: TopologyState {
548 successors: next_successors,
549 ..state.clone()
550 },
551 actions,
552 }
553 }
554 TopologyEvent::FixFinger => step_fix_finger(state),
555 TopologyEvent::ApplyFinger { index, successor } => TopologyStep {
556 state: TopologyState {
557 fingers: finger_set(state.local, &state.fingers, index, successor),
558 ..state.clone()
559 },
560 actions: Vec::new(),
561 },
562 }
563}
564
565#[cfg(test)]
566mod tests {
567 use num_bigint::BigUint;
568
569 use super::*;
570
571 fn did(value: u32) -> Did {
572 Did::from(value)
573 }
574
575 fn state(
576 local: Did,
577 successors: Vec<Did>,
578 predecessor: Option<Did>,
579 fingers: Vec<Option<Did>>,
580 fix_finger_index: usize,
581 ) -> TopologyState {
582 TopologyState::new(local, successors, predecessor, fingers, fix_finger_index)
583 }
584
585 fn successor_distances(local: Did, successors: &[Did], capacity: usize) -> Vec<BigUint> {
586 let infinity = BigUint::from(1u8) << RING_BITS;
587 (0..capacity)
588 .map(|index| {
589 successors
590 .get(index)
591 .map(|successor| dist(local, *successor))
592 .unwrap_or_else(|| infinity.clone())
593 })
594 .collect()
595 }
596
597 fn refines_successor_distances(before: &TopologyState, after: &TopologyState) -> bool {
598 let before_distances =
599 successor_distances(before.local, &before.successors, DEFAULT_SUCCESSOR_CAPACITY);
600 let after_distances =
601 successor_distances(after.local, &after.successors, DEFAULT_SUCCESSOR_CAPACITY);
602 before_distances
603 .iter()
604 .zip(after_distances.iter())
605 .all(|(before, after)| after <= before)
606 }
607
608 #[test]
609 fn test_join_step_updates_successors_fingers_and_connect_action() {
610 let local = did(0);
611 let peer = did(8);
612 let next = step(
613 &state(local, vec![], None, vec![None; 5], 0),
614 TopologyEvent::Join { peer },
615 DEFAULT_SUCCESSOR_CAPACITY,
616 );
617
618 assert_eq!(next.state.successors, vec![peer]);
619 assert_eq!(next.state.fingers, vec![
620 Some(peer),
621 Some(peer),
622 Some(peer),
623 Some(peer),
624 None
625 ]);
626 assert_eq!(next.actions, vec![
627 TopologyAction::FindSuccessorForConnect {
628 next: peer,
629 did: local
630 }
631 ]);
632 }
633
634 #[test]
635 fn test_join_step_refines_successor_distance_vector() {
636 let local = did(0);
637 let current = state(local, vec![did(20), did(40)], None, vec![None; 5], 0);
638 let next = step(
639 ¤t,
640 TopologyEvent::Join { peer: did(10) },
641 DEFAULT_SUCCESSOR_CAPACITY,
642 );
643
644 assert!(refines_successor_distances(¤t, &next.state));
645 }
646
647 #[test]
648 fn test_stabilize_step_refines_successor_distance_vector() {
649 let local = did(0);
650 let current = state(local, vec![did(40)], None, vec![None; 5], 0);
651 let next = step(
652 ¤t,
653 TopologyEvent::Stabilize {
654 successors: vec![did(50), did(60)],
655 predecessor: Some(did(10)),
656 },
657 DEFAULT_SUCCESSOR_CAPACITY,
658 );
659
660 assert!(refines_successor_distances(¤t, &next.state));
661 }
662
663 #[test]
664 fn test_remove_step_removes_peer_from_every_topology_slot() {
665 let local = did(0);
666 let peer = did(8);
667 let next = step(
668 &state(
669 local,
670 vec![peer],
671 Some(peer),
672 vec![Some(peer), Some(peer)],
673 0,
674 ),
675 TopologyEvent::Remove {
676 peer,
677 successor: SuccessorRemoval::Preserve,
678 },
679 DEFAULT_SUCCESSOR_CAPACITY,
680 );
681
682 assert!(next.state.successors.is_empty());
683 assert_eq!(next.state.predecessor, None);
684 assert_eq!(next.state.fingers, vec![None, None]);
685 assert!(next.actions.is_empty());
686 }
687
688 #[test]
689 fn test_ordinary_remove_does_not_promote_an_unverified_finger() {
690 let local = did(0);
691 let removed = did(8);
692 let fallback = did(16);
693 let next = step(
694 &state(
695 local,
696 vec![removed],
697 None,
698 vec![Some(removed), None, Some(fallback)],
699 0,
700 ),
701 TopologyEvent::Remove {
702 peer: removed,
703 successor: SuccessorRemoval::Preserve,
704 },
705 DEFAULT_SUCCESSOR_CAPACITY,
706 );
707
708 assert!(next.state.successors.is_empty());
709 assert_eq!(next.state.fingers, vec![None, None, Some(fallback)]);
710 assert!(next.actions.is_empty());
711 }
712
713 #[test]
714 fn test_remove_step_preserves_valid_slots_between_noncontiguous_peer_runs() {
715 let local = did(0);
716 let removed = did(8);
717 let middle = did(16);
718 let tail = did(32);
719 let next = step(
720 &state(
721 local,
722 vec![removed],
723 None,
724 vec![Some(removed), Some(middle), Some(removed), Some(tail)],
725 0,
726 ),
727 TopologyEvent::Remove {
728 peer: removed,
729 successor: SuccessorRemoval::Preserve,
730 },
731 DEFAULT_SUCCESSOR_CAPACITY,
732 );
733
734 assert_eq!(next.state.fingers, vec![
735 Some(middle),
736 Some(middle),
737 Some(tail),
738 Some(tail)
739 ]);
740 }
741
742 #[test]
743 fn test_unavailable_head_without_live_fallback_clears_unverified_successor_tail() {
744 let local = did(0);
745 let removed = did(8);
746 let unverified = did(12);
747 let next = step(
748 &state(
749 local,
750 vec![removed, unverified],
751 None,
752 vec![Some(unverified)],
753 0,
754 ),
755 TopologyEvent::Remove {
756 peer: removed,
757 successor: SuccessorRemoval::ReplaceWith(Vec::new()),
758 },
759 DEFAULT_SUCCESSOR_CAPACITY,
760 );
761
762 assert!(next.state.successors.is_empty());
763 assert_eq!(next.state.fingers, vec![Some(unverified)]);
764 assert!(next.actions.is_empty());
765 }
766
767 #[test]
768 fn test_remove_step_replaces_unavailable_head_with_validated_successors_only() {
769 let local = did(0);
770 let removed = did(8);
771 let unverified = did(12);
772 let fallback = did(16);
773 let verified_tail = did(24);
774 let next = step(
775 &state(
776 local,
777 vec![removed, unverified, fallback, verified_tail],
778 None,
779 vec![Some(unverified), Some(fallback), Some(verified_tail)],
780 0,
781 ),
782 TopologyEvent::Remove {
783 peer: removed,
784 successor: SuccessorRemoval::ReplaceWith(vec![
785 removed,
786 verified_tail,
787 fallback,
788 fallback,
789 local,
790 ]),
791 },
792 DEFAULT_SUCCESSOR_CAPACITY,
793 );
794
795 assert_eq!(next.state.successors, vec![fallback, verified_tail]);
796 assert_eq!(next.state.fingers, vec![
797 Some(unverified),
798 Some(fallback),
799 Some(verified_tail)
800 ]);
801 assert!(next.actions.is_empty());
802 }
803
804 #[test]
805 fn test_admit_step_commits_join_and_pending_fingers_in_one_state() {
806 let local = did(0);
807 let peer = did(16);
808 let next = step(
809 &state(local, Vec::new(), None, vec![None; 5], 0),
810 TopologyEvent::Admit {
811 peer,
812 fixed_fingers: vec![ConditionalFingerUpdate {
813 index: 4,
814 expected: None,
815 }],
816 },
817 DEFAULT_SUCCESSOR_CAPACITY,
818 );
819
820 assert_eq!(next.state.successors, vec![peer]);
821 assert_eq!(next.state.fingers, vec![
822 Some(peer),
823 Some(peer),
824 Some(peer),
825 Some(peer),
826 Some(peer)
827 ]);
828 assert_eq!(next.actions, vec![
829 TopologyAction::QuerySuccessorList(peer),
830 TopologyAction::FindSuccessorForConnect {
831 next: peer,
832 did: local
833 }
834 ]);
835 }
836
837 #[test]
838 fn test_admit_step_does_not_overwrite_finger_changed_after_update_was_deferred() {
839 let local = did(0);
840 let fresher = did(8);
841 let peer = did(16);
842 let next = step(
843 &state(local, vec![fresher], None, vec![Some(fresher); 5], 0),
844 TopologyEvent::Admit {
845 peer,
846 fixed_fingers: vec![ConditionalFingerUpdate {
847 index: 4,
848 expected: None,
849 }],
850 },
851 DEFAULT_SUCCESSOR_CAPACITY,
852 );
853
854 assert_eq!(next.state.fingers[4], Some(fresher));
855 }
856
857 #[test]
858 fn test_fix_finger_step_updates_local_successor_slot() {
859 let local = did(0);
860 let successor = did(8);
861 let next = step(
862 &state(local, vec![successor], None, vec![None; 4], 2),
863 TopologyEvent::FixFinger,
864 DEFAULT_SUCCESSOR_CAPACITY,
865 );
866
867 assert_eq!(next.state.fix_finger_index, 3);
868 assert_eq!(next.state.fingers, vec![None, None, None, Some(successor)]);
869 assert!(next.actions.is_empty());
870 }
871
872 #[test]
873 fn test_fix_finger_step_emits_indexed_remote_action() {
874 let local = did(0);
875 let successor = did(4);
876 let next_hop = did(6);
877 let next = step(
878 &state(
879 local,
880 vec![successor],
881 None,
882 vec![None, None, Some(next_hop), None],
883 2,
884 ),
885 TopologyEvent::FixFinger,
886 DEFAULT_SUCCESSOR_CAPACITY,
887 );
888
889 assert_eq!(next.state.fix_finger_index, 3);
890 assert_eq!(next.actions, vec![TopologyAction::FindSuccessorForFix {
891 next: next_hop,
892 did: Did::power_of_two(3),
893 index: 3
894 }]);
895 }
896
897 #[test]
898 fn test_fix_finger_step_queries_local_relative_probe() {
899 let local = did(100);
900 let successor = did(104);
901 let next_hop = did(106);
902 let next = step(
903 &state(
904 local,
905 vec![successor],
906 None,
907 vec![None, None, Some(next_hop), None],
908 2,
909 ),
910 TopologyEvent::FixFinger,
911 DEFAULT_SUCCESSOR_CAPACITY,
912 );
913
914 assert_eq!(next.state.fix_finger_index, 3);
915 assert_eq!(next.actions, vec![TopologyAction::FindSuccessorForFix {
916 next: next_hop,
917 did: local + Did::power_of_two(3),
918 index: 3
919 }]);
920 }
921
922 #[test]
923 fn test_apply_finger_step_updates_exact_slot() {
924 let local = did(0);
925 let successor = did(8);
926 let next = step(
927 &state(local, vec![], None, vec![None; 4], 0),
928 TopologyEvent::ApplyFinger {
929 index: 2,
930 successor,
931 },
932 DEFAULT_SUCCESSOR_CAPACITY,
933 );
934
935 assert_eq!(next.state.fingers, vec![None, None, Some(successor), None]);
936 assert!(next.actions.is_empty());
937 }
938
939 #[test]
940 fn test_apply_finger_step_ignores_self_and_out_of_range_slot() {
941 let local = did(0);
942 let current = state(local, vec![], None, vec![None; 2], 0);
943 let self_update = step(
944 ¤t,
945 TopologyEvent::ApplyFinger {
946 index: 1,
947 successor: local,
948 },
949 DEFAULT_SUCCESSOR_CAPACITY,
950 );
951 let out_of_range = step(
952 ¤t,
953 TopologyEvent::ApplyFinger {
954 index: 9,
955 successor: did(9),
956 },
957 DEFAULT_SUCCESSOR_CAPACITY,
958 );
959
960 assert_eq!(self_update.state, current);
961 assert_eq!(out_of_range.state, current);
962 }
963}