1use std::collections::HashSet;
5use std::fmt::Display;
6use std::{fmt::Debug, marker::PhantomData};
7
8use petgraph::algo::toposort;
9
10use crate::Access;
11use crate::graph::{concurrent_bubbles, has_path};
12use crate::group::{
13 GroupAction, GroupControlMessage, GroupCrdt, GroupCrdtError, GroupCrdtState, StateChangeResult,
14};
15use crate::traits::{GroupStore, IdentityHandle, Operation, OperationId, Orderer, Resolver};
16
17#[derive(Clone, Debug, Default)]
58pub struct StrongRemove<ID, OP, C, ORD, GS> {
59 _phantom: PhantomData<(ID, OP, C, ORD, GS)>,
60}
61
62impl<ID, OP, C, ORD, GS> Resolver<ID, OP, C, ORD, GS> for StrongRemove<ID, OP, C, ORD, GS>
63where
64 ID: IdentityHandle + Display + Ord,
65 OP: OperationId + Display + Ord,
66 C: Clone + Debug + PartialEq + PartialOrd,
67 ORD: Orderer<ID, OP, GroupControlMessage<ID, C>> + Clone + Debug,
68 ORD::Operation: Clone,
69 ORD::State: Clone,
70 GS: GroupStore<ID, OP, C, Self, ORD> + Debug + Clone,
71{
72 fn rebuild_required(
74 y: &GroupCrdtState<ID, OP, C, Self, ORD, GS>,
75 operation: &ORD::Operation,
76 ) -> Result<bool, GroupCrdtError<ID, OP, C, Self, ORD, GS>> {
77 let control_message = operation.payload();
78 let group_id = control_message.group_id();
79
80 if y.group_id != group_id {
82 return Err(GroupCrdtError::IncorrectGroupId(group_id, y.group_id));
84 }
85
86 let transitive_heads = y.transitive_heads().unwrap();
87
88 Ok(transitive_heads.into_iter().collect::<Vec<_>>() != operation.dependencies())
90 }
91
92 fn process(
95 mut y: GroupCrdtState<ID, OP, C, Self, ORD, GS>,
96 ) -> Result<GroupCrdtState<ID, OP, C, Self, ORD, GS>, GroupCrdtError<ID, OP, C, Self, ORD, GS>>
97 {
98 y.ignore.drain();
100 let mut y = GroupCrdt::rebuild(y).expect("no errors when re-building a group");
101 let mut filter: HashSet<OP> = Default::default();
102
103 let operations = y.operations.clone();
105
106 let mut mutual_removes = HashSet::new();
109
110 let mut bubbles = concurrent_bubbles(&y.graph);
115
116 let topo_sort =
117 toposort(&y.graph, None).expect("group operation sets can be ordered topologically");
118 let mut visited = HashSet::new();
119
120 for target_operation_id in topo_sort.iter() {
122 let Some(target_operation) = operations.get(target_operation_id) else {
123 return Err(GroupCrdtError::MissingOperation(*target_operation_id));
124 };
125
126 let bubble = bubbles
127 .iter()
128 .find(|bubble| bubble.contains(target_operation_id))
129 .cloned();
130
131 visited.insert(*target_operation_id);
132
133 let removed_manager = y.removed_manager(target_operation);
134
135 if let (Some(removed_manager), Some(bubble)) = (removed_manager, &bubble) {
139 for bubble_operation_id in bubble.iter() {
140 if has_path(&y.graph, *bubble_operation_id, *target_operation_id) {
143 continue;
144 }
145
146 let Some(bubble_operation) = operations.get(bubble_operation_id) else {
147 return Err(GroupCrdtError::MissingOperation(*bubble_operation_id));
148 };
149
150 if bubble_operation.author() != removed_manager {
153 continue;
154 }
155
156 filter.insert(*bubble_operation_id);
158
159 if let Some(concurrent_removed_admin) = y.removed_manager(bubble_operation) {
161 if concurrent_removed_admin == target_operation.author() {
164 mutual_removes.insert(*bubble_operation_id);
165 }
166 }
167 }
168 }
169
170 match bubble {
171 Some(bubble) => {
172 if bubble.is_subset(&visited) {
180 let mut filter_tmp = filter.clone();
181 filter_tmp.retain(|op: &OP| !mutual_removes.contains(op));
182 y.ignore = filter_tmp;
183 y = GroupCrdt::rebuild(y).expect("no errors when re-building a group");
184 bubbles.retain(|b| *b != bubble);
186
187 visited.drain();
189 }
190 }
191 None => {
192 let dependencies = HashSet::from_iter(target_operation.dependencies().clone());
193
194 let result = GroupCrdt::apply_action(
196 y,
197 target_operation.id(),
198 target_operation.author(),
199 &dependencies,
200 &target_operation.payload().action,
201 )?;
202
203 y = match result {
204 StateChangeResult::Ok { state } => state,
205 StateChangeResult::Noop { error, .. } => {
206 return Err(GroupCrdtError::StateChangeError(
207 target_operation.id(),
208 error,
209 ));
210 }
211 StateChangeResult::Filtered { state } => state,
212 };
213 visited.drain();
214 }
215 }
216 }
217
218 assert!(bubbles.is_empty(), "{:?}", bubbles);
220 Ok(y)
221 }
222}
223
224impl<ID, OP, C, RS, ORD, GS> GroupCrdtState<ID, OP, C, RS, ORD, GS>
225where
226 ID: IdentityHandle,
227 OP: OperationId + Ord,
228 C: Clone + Debug + PartialEq + PartialOrd,
229 RS: Resolver<ID, OP, C, ORD, GS> + Debug,
230 ORD: Orderer<ID, OP, GroupControlMessage<ID, C>> + Debug,
231 GS: GroupStore<ID, OP, C, RS, ORD> + Debug + Clone,
232{
233 fn removed_manager(&self, operation: &ORD::Operation) -> Option<ID> {
236 let action = operation.payload().action;
237
238 let removed_or_demoted_member = match action {
239 GroupAction::Remove { member } => member,
240 GroupAction::Demote { member, .. } => member,
241 _ => return None,
242 };
243
244 let was_manager = self
248 .transitive_members_at(&HashSet::from_iter(operation.dependencies()))
249 .expect("get transitive members")
250 .contains(&(removed_or_demoted_member.id(), Access::manage()));
251
252 if was_manager {
253 Some(removed_or_demoted_member.id())
254 } else {
255 None
256 }
257 }
258}
259
260#[cfg(test)]
261mod tests {
262 use rand::SeedableRng;
263 use rand::rngs::StdRng;
264
265 use crate::Access;
266 use crate::group::GroupMember;
267 use crate::group::crdt::tests::{
268 add_member, assert_members, create_group, from_create, remove_member, sync,
269 };
270 use crate::test_utils::Network;
271 use crate::traits::OperationId;
272
273 impl OperationId for &str {}
274
275 #[test]
276 fn mutual_removal_filter() {
277 let alice = 'A';
288 let bob = 'B';
289 let claire = 'C';
290
291 let group = '1';
292
293 let rng = StdRng::from_os_rng();
294
295 let mut network = Network::new([alice, bob, claire], rng);
296
297 network.create(
299 group,
300 alice,
301 vec![
302 (GroupMember::Individual(alice), Access::manage()),
303 (GroupMember::Individual(bob), Access::manage()),
304 (GroupMember::Individual(claire), Access::manage()),
305 ],
306 );
307
308 network.process();
310
311 network.remove(alice, GroupMember::Individual(bob), group);
313 network.remove(bob, GroupMember::Individual(alice), group);
314
315 network.process();
317
318 let alice_members = network.members(&alice, &group);
320 assert_eq!(
321 alice_members,
322 vec![(GroupMember::Individual(claire), Access::manage()),]
323 );
324
325 let bob_members = network.members(&bob, &group);
326 assert_eq!(
327 bob_members,
328 vec![(GroupMember::Individual(claire), Access::manage()),]
329 );
330
331 let claire_members = network.members(&claire, &group);
332 assert_eq!(
333 claire_members,
334 vec![(GroupMember::Individual(claire), Access::manage()),]
335 );
336
337 let alice_filter = network.get_y(&alice, &group).ignore;
340 assert!(alice_filter.is_empty());
341
342 let bob_filter = network.get_y(&bob, &group).ignore;
343 assert!(bob_filter.is_empty());
344
345 let claire_filter = network.get_y(&claire, &group).ignore;
346 assert!(claire_filter.is_empty());
347 }
348
349 #[test]
350 fn demote_remove_filter() {
351 let alice = 'A';
362 let bob = 'B';
363 let claire = 'C';
364
365 let group = '1';
366
367 let rng = StdRng::from_os_rng();
368
369 let mut network = Network::new([alice, bob, claire], rng);
370
371 network.create(
373 group,
374 alice,
375 vec![
376 (GroupMember::Individual(alice), Access::manage()),
377 (GroupMember::Individual(bob), Access::manage()),
378 (GroupMember::Individual(claire), Access::manage()),
379 ],
380 );
381
382 network.process();
384
385 network.demote(alice, GroupMember::Individual(bob), group, Access::write());
387
388 network.remove(bob, GroupMember::Individual(claire), group);
390
391 network.process();
393
394 let alice_members = network.members(&alice, &group);
399 assert_eq!(
400 alice_members,
401 vec![
402 (GroupMember::Individual(alice), Access::manage()),
403 (GroupMember::Individual(bob), Access::write()),
404 (GroupMember::Individual(claire), Access::manage()),
405 ]
406 );
407
408 let bob_members = network.members(&bob, &group);
409 assert_eq!(
410 bob_members,
411 vec![
412 (GroupMember::Individual(alice), Access::manage()),
413 (GroupMember::Individual(bob), Access::write()),
414 (GroupMember::Individual(claire), Access::manage()),
415 ]
416 );
417
418 let claire_members = network.members(&claire, &group);
419 assert_eq!(
420 claire_members,
421 vec![
422 (GroupMember::Individual(alice), Access::manage()),
423 (GroupMember::Individual(bob), Access::write()),
424 (GroupMember::Individual(claire), Access::manage()),
425 ]
426 );
427
428 let alice_filter = network.get_y(&alice, &group).ignore;
431 let bob_filter = network.get_y(&bob, &group).ignore;
432 let claire_filter = network.get_y(&claire, &group).ignore;
433 assert_eq!(alice_filter.len(), 1);
434 assert_eq!(alice_filter, bob_filter);
435 assert_eq!(bob_filter, claire_filter);
436 }
437
438 #[test]
439 fn demote_add_filter() {
440 let alice = 'A';
451 let bob = 'B';
452 let claire = 'C';
453 let dave = 'D';
454
455 let group = '1';
456
457 let rng = StdRng::from_os_rng();
458
459 let mut network = Network::new([alice, bob, claire, dave], rng);
460
461 network.create(
463 group,
464 alice,
465 vec![
466 (GroupMember::Individual(alice), Access::manage()),
467 (GroupMember::Individual(bob), Access::manage()),
468 (GroupMember::Individual(claire), Access::manage()),
469 ],
470 );
471
472 network.process();
474
475 network.demote(alice, GroupMember::Individual(bob), group, Access::write());
477
478 network.add(bob, GroupMember::Individual(dave), group, Access::read());
480
481 network.process();
483
484 let expected_members = vec![
489 (GroupMember::Individual(alice), Access::manage()),
490 (GroupMember::Individual(bob), Access::write()),
491 (GroupMember::Individual(claire), Access::manage()),
492 ];
493
494 let alice_members = network.members(&alice, &group);
495 assert_eq!(alice_members, expected_members);
496
497 let bob_members = network.members(&bob, &group);
498 assert_eq!(bob_members, expected_members);
499
500 let claire_members = network.members(&claire, &group);
501 assert_eq!(claire_members, expected_members);
502
503 let alice_filter = network.get_y(&alice, &group).ignore;
505 assert_eq!(alice_filter.len(), 1);
506
507 let bob_filter = network.get_y(&bob, &group).ignore;
508 assert_eq!(bob_filter.len(), 1);
509
510 let claire_filter = network.get_y(&claire, &group).ignore;
511 assert_eq!(claire_filter.len(), 1);
512 }
513
514 #[test]
515 fn remove_dependencies_filter() {
516 let group_id = '1';
524 let alice = 'A';
525 let bob = 'B';
526 let claire = 'C';
527 let dave = 'D';
528
529 let mut rng = StdRng::from_os_rng();
530
531 let (alice_group, op_create) = create_group(
533 alice,
534 group_id,
535 vec![(alice, Access::manage()), (bob, Access::manage())],
536 &mut rng,
537 );
538
539 let bob_group = from_create(bob, group_id, &op_create, &mut rng);
540 let claire_group = from_create(claire, group_id, &op_create, &mut rng);
541
542 assert_members(
543 &alice_group,
544 &[
545 (GroupMember::Individual(alice), Access::manage()),
546 (GroupMember::Individual(bob), Access::manage()),
547 ],
548 );
549
550 let (alice_group, op_remove_bob) = remove_member(alice_group, group_id, bob);
552
553 assert_members(
554 &alice_group,
555 &[(GroupMember::Individual(alice), Access::manage())],
556 );
557
558 let (bob_group, op_add_claire) = add_member(bob_group, group_id, claire, Access::manage());
560 let claire_group = sync(claire_group, &[op_add_claire.clone()]);
561
562 assert_members(
563 &bob_group,
564 &[
565 (GroupMember::Individual(alice), Access::manage()),
566 (GroupMember::Individual(bob), Access::manage()),
567 (GroupMember::Individual(claire), Access::manage()),
568 ],
569 );
570
571 let (claire_group, op_add_dave) = add_member(claire_group, group_id, dave, Access::read());
573 let bob_group = sync(bob_group, &[op_add_dave.clone()]);
574
575 assert_members(
576 &bob_group,
577 &[
578 (GroupMember::Individual(alice), Access::manage()),
579 (GroupMember::Individual(bob), Access::manage()),
580 (GroupMember::Individual(claire), Access::manage()),
581 (GroupMember::Individual(dave), Access::read()),
582 ],
583 );
584
585 let alice_group = sync(alice_group, &[op_add_claire.clone(), op_add_dave.clone()]);
587 let bob_group = sync(bob_group, &[op_remove_bob.clone()]);
588 let claire_group = sync(claire_group, &[op_remove_bob.clone()]);
589
590 let expected_members = vec![(GroupMember::Individual(alice), Access::manage())];
591
592 assert_members(&alice_group, &expected_members);
593 assert_members(&bob_group, &expected_members);
594 assert_members(&claire_group, &expected_members);
595 }
596
597 #[test]
598 fn remove_readd_dependencies_filter() {
599 let group_id = 'G';
617 let alice = 'A';
618 let bob = 'B';
619 let claire = 'C';
620 let dave = 'D';
621 let eve = 'E';
622
623 let mut rng = StdRng::from_os_rng();
624
625 let (alice_group, op_create) = create_group(
627 alice,
628 group_id,
629 vec![
630 (alice, Access::manage()),
631 (bob, Access::manage()),
632 (claire, Access::manage()),
633 ],
634 &mut rng,
635 );
636
637 let bob_group = from_create(bob, group_id, &op_create, &mut rng);
638
639 assert_members(
640 &alice_group,
641 &[
642 (GroupMember::Individual(alice), Access::manage()),
643 (GroupMember::Individual(bob), Access::manage()),
644 (GroupMember::Individual(claire), Access::manage()),
645 ],
646 );
647
648 let (alice_group, op_remove_bob) = remove_member(alice_group, group_id, bob);
650
651 assert_members(
652 &alice_group,
653 &[
654 (GroupMember::Individual(alice), Access::manage()),
655 (GroupMember::Individual(claire), Access::manage()),
656 ],
657 );
658
659 let (alice_group, op_readd_bob) = add_member(alice_group, group_id, bob, Access::manage());
661
662 assert_members(
663 &alice_group,
664 &[
665 (GroupMember::Individual(alice), Access::manage()),
666 (GroupMember::Individual(bob), Access::manage()),
667 (GroupMember::Individual(claire), Access::manage()),
668 ],
669 );
670
671 let (bob_group, op_add_dave) = add_member(bob_group, group_id, dave, Access::read());
673
674 assert_members(
675 &bob_group,
676 &[
677 (GroupMember::Individual(alice), Access::manage()),
678 (GroupMember::Individual(bob), Access::manage()),
679 (GroupMember::Individual(claire), Access::manage()),
680 (GroupMember::Individual(dave), Access::read()),
681 ],
682 );
683
684 let alice_group = sync(alice_group, &[op_add_dave.clone()]);
686 let bob_group = sync(bob_group, &[op_remove_bob.clone(), op_readd_bob.clone()]);
687
688 let (bob_group, op_add_eve) = add_member(bob_group, group_id, eve, Access::read());
690 let alice_group = sync(alice_group, &[op_add_eve.clone()]);
691
692 let expected = vec![
694 (GroupMember::Individual(alice), Access::manage()),
695 (GroupMember::Individual(bob), Access::manage()),
696 (GroupMember::Individual(claire), Access::manage()),
697 (GroupMember::Individual(eve), Access::read()),
698 ];
699
700 assert_members(&alice_group, &expected);
701 assert_members(&bob_group, &expected);
702 }
703
704 #[test]
705 fn two_bubbles() {
706 let group_id = '0';
732 let alice = 'A';
733 let bob = 'B';
734 let claire = 'C';
735 let dave = 'D';
736 let eve = 'E';
737 let frank = 'F';
738 let grace = 'G';
739
740 let mut rng = StdRng::from_os_rng();
741
742 let (alice_group, op_create) = create_group(
744 alice,
745 group_id,
746 vec![(alice, Access::manage()), (bob, Access::manage())],
747 &mut rng,
748 );
749
750 let bob_group = from_create(bob, group_id, &op_create, &mut rng);
752 let dave_group = from_create(dave, group_id, &op_create, &mut rng);
753 let frank_group = from_create(frank, group_id, &op_create, &mut rng);
754
755 assert_members(
756 &alice_group,
757 &[
758 (GroupMember::Individual(alice), Access::manage()),
759 (GroupMember::Individual(bob), Access::manage()),
760 ],
761 );
762
763 let (alice_group, op_remove_bob) = remove_member(alice_group, group_id, bob);
765
766 let (_bob_group, op_add_claire) = add_member(bob_group, group_id, claire, Access::read());
768
769 let alice_group = sync(alice_group, &[op_add_claire.clone()]);
771
772 let (alice_group, op_add_dave) = add_member(alice_group, group_id, dave, Access::manage());
774
775 let dave_group = sync(
777 dave_group,
778 &[
779 op_remove_bob.clone(),
780 op_add_claire.clone(),
781 op_add_dave.clone(),
782 ],
783 );
784
785 let (dave_group, op_add_eve) = add_member(dave_group, group_id, eve, Access::read());
787
788 let alice_group = sync(alice_group, &[op_add_eve.clone()]);
789
790 let (_alice_group, op_add_frank) =
792 add_member(alice_group, group_id, frank, Access::manage());
793
794 let frank_group = sync(
795 frank_group,
796 &[
797 op_remove_bob.clone(),
798 op_add_claire.clone(),
799 op_add_dave.clone(),
800 op_add_eve.clone(),
801 op_add_frank.clone(),
802 ],
803 );
804
805 let (_, op_add_grace) = add_member(frank_group, group_id, grace, Access::read());
807
808 let (dave_group, _op_remove_alice) = remove_member(dave_group, group_id, alice);
810
811 let dave_group = sync(dave_group, &[op_add_frank.clone(), op_add_grace.clone()]);
812
813 let expected_members = vec![
814 (GroupMember::Individual(dave), Access::manage()),
815 (GroupMember::Individual(eve), Access::read()),
816 ];
817
818 let mut dave_members = dave_group.members();
819 dave_members.sort();
820 assert_eq!(expected_members, dave_members);
821 }
822}