1use std::collections::{HashMap, HashSet};
2
3use crate::{
4 governance::model::Quorum,
5 model::common::{
6 CeilingMap, Interval, IntervalSet, emit_fail, purge_storage,
7 },
8};
9use async_trait::async_trait;
10use ave_actors::{
11 Actor, ActorContext, ActorError, ActorPath, Event, Handler,
12 LightPersistence, Message, PersistentActor, Response,
13};
14
15use ave_common::{Namespace, SchemaType, identity::PublicKey};
16use borsh::{BorshDeserialize, BorshSerialize};
17use serde::{Deserialize, Serialize};
18use tracing::{Span, debug, error, info_span};
19
20use crate::db::Storable;
21
22#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
23pub struct SearchRole {
24 pub schema_id: SchemaType,
25 pub namespace: Namespace,
26}
27
28#[derive(
29 Debug,
30 Clone,
31 Serialize,
32 Deserialize,
33 Hash,
34 Eq,
35 PartialEq,
36 Ord,
37 PartialOrd,
38 BorshDeserialize,
39 BorshSerialize,
40)]
41pub struct RoleData {
42 pub key: PublicKey,
43 pub namespace: Namespace,
44}
45
46#[derive(
47 Debug,
48 Clone,
49 Serialize,
50 Deserialize,
51 BorshDeserialize,
52 BorshSerialize,
53 Default,
54)]
55pub struct RoleRegister {
56 version: u64,
57
58 appr_quorum: Quorum,
59 approvers: HashSet<PublicKey>,
60
61 eval_quorum: HashMap<SchemaType, Quorum>,
62 evaluators: HashMap<SchemaType, HashSet<(PublicKey, Namespace)>>,
63
64 vali_quorum: HashMap<SchemaType, CeilingMap<Quorum>>,
65 validators:
66 HashMap<SchemaType, HashMap<(PublicKey, Namespace), IntervalData>>,
67}
68
69type IntervalData = (IntervalSet, Option<u64>);
70
71impl RoleRegister {
72 pub fn new() -> Self {
73 Self {
74 version: 0,
75 appr_quorum: Quorum::Majority,
76 eval_quorum: HashMap::new(),
77 vali_quorum: HashMap::new(),
78 evaluators: HashMap::new(),
79 validators: HashMap::new(),
80 approvers: HashSet::new(),
81 }
82 }
83}
84
85#[derive(
86 Debug, Clone, Deserialize, Serialize, BorshDeserialize, BorshSerialize,
87)]
88pub struct UpdateRole {
89 pub schema_id: SchemaType,
90 pub role: HashSet<RoleData>,
91}
92
93#[derive(
94 Debug, Clone, Deserialize, Serialize, BorshDeserialize, BorshSerialize,
95)]
96pub struct UpdateQuorum {
97 pub schema_id: SchemaType,
98 pub quorum: Quorum,
99}
100
101#[derive(
102 Debug, Clone, Serialize, Deserialize, BorshDeserialize, BorshSerialize,
103)]
104pub struct RoleDataRegister {
105 pub workers: HashSet<PublicKey>,
106 pub quorum: Quorum,
107}
108
109#[derive(Debug, Clone)]
110pub struct CurrentSchemaRoles {
111 pub evaluation: HashSet<RoleData>,
112 pub evaluation_quorum: Quorum,
113 pub validation: HashSet<RoleData>,
114 pub validation_quorum: Quorum,
115}
116
117#[derive(Debug, Clone)]
118pub struct CurrentValidationRoles {
119 pub approval: RoleDataRegister,
120 pub schema: CurrentSchemaRoles,
121}
122
123#[derive(Debug, Clone)]
124pub enum RoleRegisterMessage {
125 PurgeStorage,
126 GetCurrentValidationRoles {
127 schema_id: SchemaType,
128 },
129 SearchActualRoles {
130 version: u64,
131 evaluation: SearchRole,
132 approval: bool,
133 },
134 SearchValidators {
135 search: SearchRole,
136 version: u64,
137 },
138 UpdateVersion {
139 version: u64,
140 },
141 UpdateFact {
142 version: u64,
143
144 appr_quorum: Option<Quorum>,
145 eval_quorum: HashMap<SchemaType, Quorum>,
146 vali_quorum: HashMap<SchemaType, Quorum>,
147
148 new_approvers: Vec<PublicKey>,
149 remove_approvers: Vec<PublicKey>,
150
151 new_evaluators: HashMap<(SchemaType, PublicKey), Vec<Namespace>>,
152 remove_evaluators: HashMap<(SchemaType, PublicKey), Vec<Namespace>>,
153
154 new_validators: HashMap<(SchemaType, PublicKey), Vec<Namespace>>,
155 remove_validators: HashMap<(SchemaType, PublicKey), Vec<Namespace>>,
156 },
157 UpdateConfirm {
158 version: u64,
159
160 new_approver: Option<PublicKey>,
161 remove_approver: PublicKey,
162
163 new_evaluator: Option<PublicKey>,
164 remove_evaluators: HashMap<(SchemaType, PublicKey), Vec<Namespace>>,
165
166 new_validator: Option<PublicKey>,
167 remove_validators: HashMap<(SchemaType, PublicKey), Vec<Namespace>>,
168 },
169}
170impl Message for RoleRegisterMessage {
171 fn is_critical(&self) -> bool {
172 matches!(
173 self,
174 Self::PurgeStorage
175 | Self::UpdateVersion { .. }
176 | Self::UpdateFact { .. }
177 | Self::UpdateConfirm { .. }
178 )
179 }
180}
181
182#[derive(Debug, Clone)]
183pub enum RoleRegisterResponse {
184 CurrentValidationRoles(CurrentValidationRoles),
185 ActualRoles {
186 evaluation: RoleDataRegister,
187 approval: Option<RoleDataRegister>,
188 },
189 Validation(RoleDataRegister),
190 MissingData,
191 OutOfVersion,
192 Ok,
193}
194
195impl Response for RoleRegisterResponse {}
196
197#[derive(
198 Debug, Clone, Deserialize, Serialize, BorshDeserialize, BorshSerialize,
199)]
200pub enum RoleRegisterEvent {
201 UpdateVersion {
202 version: u64,
203 },
204 UpdateFact {
205 version: u64,
206
207 appr_quorum: Option<Quorum>,
208 eval_quorum: HashMap<SchemaType, Quorum>,
209 vali_quorum: HashMap<SchemaType, Quorum>,
210
211 new_approvers: Vec<PublicKey>,
212 remove_approvers: Vec<PublicKey>,
213
214 new_evaluators: HashMap<(SchemaType, PublicKey), Vec<Namespace>>,
215 remove_evaluators: HashMap<(SchemaType, PublicKey), Vec<Namespace>>,
216
217 new_validators: HashMap<(SchemaType, PublicKey), Vec<Namespace>>,
218 remove_validators: HashMap<(SchemaType, PublicKey), Vec<Namespace>>,
219 },
220 UpdateConfirm {
221 version: u64,
222
223 new_approver: Option<PublicKey>,
224 remove_approver: PublicKey,
225
226 new_evaluator: Option<PublicKey>,
227 remove_evaluators: HashMap<(SchemaType, PublicKey), Vec<Namespace>>,
228
229 new_validator: Option<PublicKey>,
230 remove_validators: HashMap<(SchemaType, PublicKey), Vec<Namespace>>,
231 },
232}
233
234impl Event for RoleRegisterEvent {}
235
236#[async_trait]
237impl Actor for RoleRegister {
238 type Event = RoleRegisterEvent;
239 type Message = RoleRegisterMessage;
240 type Response = RoleRegisterResponse;
241
242 fn get_span(_id: &str, parent_span: Option<Span>) -> tracing::Span {
243 parent_span.map_or_else(
244 || info_span!("RoleRegister"),
245 |parent_span| info_span!(parent: parent_span, "RoleRegister"),
246 )
247 }
248
249 async fn pre_start(
250 &mut self,
251 ctx: &mut ActorContext<Self>,
252 ) -> Result<(), ActorError> {
253 let prefix = ctx.path().parent().key();
254 if let Err(e) = self
255 .init_store("role_register", Some(prefix), true, ctx)
256 .await
257 {
258 error!(
259 error = %e,
260 "Failed to initialize role_register store"
261 );
262 return Err(e);
263 }
264 Ok(())
265 }
266}
267
268#[async_trait]
269impl Handler<Self> for RoleRegister {
270 async fn handle_message(
271 &mut self,
272 _sender: ActorPath,
273 msg: RoleRegisterMessage,
274 ctx: &mut ActorContext<Self>,
275 ) -> Result<RoleRegisterResponse, ActorError> {
276 match msg {
277 RoleRegisterMessage::PurgeStorage => {
278 purge_storage(ctx).await?;
279
280 debug!(
281 msg_type = "PurgeStorage",
282 "Role register storage purged"
283 );
284
285 Ok(RoleRegisterResponse::Ok)
286 }
287 RoleRegisterMessage::GetCurrentValidationRoles { schema_id } => {
288 let approval = RoleDataRegister {
289 workers: self.approvers.clone(),
290 quorum: self.appr_quorum.clone(),
291 };
292
293 let Some(evaluation_quorum) =
294 self.eval_quorum.get(&schema_id).cloned()
295 else {
296 return Ok(RoleRegisterResponse::MissingData);
297 };
298
299 let Some(validation_quorum) = self
300 .vali_quorum
301 .get(&schema_id)
302 .and_then(|quorum| quorum.get_prev_or_equal(self.version))
303 else {
304 return Ok(RoleRegisterResponse::MissingData);
305 };
306
307 let mut evaluation = HashSet::new();
308 if !schema_id.is_gov()
309 && let Some(evaluators) =
310 self.evaluators.get(&SchemaType::TrackerSchemas)
311 {
312 for (key, namespace) in evaluators {
313 evaluation.insert(RoleData {
314 key: key.clone(),
315 namespace: namespace.clone(),
316 });
317 }
318 }
319
320 if let Some(evaluators) = self.evaluators.get(&schema_id) {
321 for (key, namespace) in evaluators {
322 evaluation.insert(RoleData {
323 key: key.clone(),
324 namespace: namespace.clone(),
325 });
326 }
327 }
328
329 let mut validation = HashSet::new();
330 if !schema_id.is_gov()
331 && let Some(validators) =
332 self.validators.get(&SchemaType::TrackerSchemas)
333 {
334 for ((key, namespace), (_, last)) in validators {
335 if let Some(last) = last
336 && *last <= self.version
337 {
338 validation.insert(RoleData {
339 key: key.clone(),
340 namespace: namespace.clone(),
341 });
342 }
343 }
344 }
345
346 if let Some(validators) = self.validators.get(&schema_id) {
347 for ((key, namespace), (_, last)) in validators {
348 if let Some(last) = last
349 && *last <= self.version
350 {
351 validation.insert(RoleData {
352 key: key.clone(),
353 namespace: namespace.clone(),
354 });
355 }
356 }
357 }
358
359 Ok(RoleRegisterResponse::CurrentValidationRoles(
360 CurrentValidationRoles {
361 approval,
362 schema: CurrentSchemaRoles {
363 evaluation,
364 evaluation_quorum,
365 validation,
366 validation_quorum,
367 },
368 },
369 ))
370 }
371 RoleRegisterMessage::SearchActualRoles {
372 version,
373 evaluation,
374 approval,
375 } => {
376 if version != self.version {
377 debug!(
378 msg_type = "SearchActualRoles",
379 version = version,
380 current_version = self.version,
381 schema_id = %evaluation.schema_id,
382 namespace = %evaluation.namespace,
383 "Request version exceeds current version"
384 );
385 return Ok(RoleRegisterResponse::OutOfVersion);
386 }
387
388 'data: {
389 let approvers = if approval {
390 if self.approvers.is_empty() {
391 break 'data;
392 } else {
393 Some(RoleDataRegister {
394 workers: self.approvers.clone(),
395 quorum: self.appr_quorum.clone(),
396 })
397 }
398 } else {
399 None
400 };
401
402 let mut all_eval = if !evaluation.schema_id.is_gov()
403 && let Some(evaluators) =
404 self.evaluators.get(&SchemaType::TrackerSchemas)
405 {
406 let mut schema_eval = vec![];
407 for (key, namespace) in evaluators {
408 if namespace
409 .is_ancestor_or_equal_of(&evaluation.namespace)
410 {
411 schema_eval.push(key.clone());
412 }
413 }
414
415 schema_eval
416 } else {
417 vec![]
418 };
419
420 let mut schema_eval = if let Some(evaluators) =
421 self.evaluators.get(&evaluation.schema_id)
422 {
423 let mut schema_eval = vec![];
424 for (key, namespace) in evaluators {
425 if namespace
426 .is_ancestor_or_equal_of(&evaluation.namespace)
427 {
428 schema_eval.push(key.clone());
429 }
430 }
431
432 schema_eval
433 } else {
434 vec![]
435 };
436
437 let quorum = if let Some(quorum_schema) =
438 self.eval_quorum.get(&evaluation.schema_id)
439 {
440 quorum_schema.clone()
441 } else {
442 break 'data;
443 };
444
445 if schema_eval.is_empty() && all_eval.is_empty() {
446 break 'data;
447 }
448
449 let mut evaluators = vec![];
450 evaluators.append(&mut schema_eval);
451 evaluators.append(&mut all_eval);
452
453 debug!(
454 msg_type = "SearchActualRoles",
455 version = version,
456 schema_id = %evaluation.schema_id,
457 namespace = %evaluation.namespace,
458 evaluators_count = evaluators.len(),
459 has_approvers = approvers.is_some(),
460 "Found actual roles successfully"
461 );
462
463 return Ok(RoleRegisterResponse::ActualRoles {
464 evaluation: RoleDataRegister {
465 workers: evaluators.iter().cloned().collect(),
466 quorum,
467 },
468 approval: approvers,
469 });
470 }
471
472 debug!(
473 msg_type = "SearchActualRoles",
474 version = version,
475 schema_id = %evaluation.schema_id,
476 namespace = %evaluation.namespace,
477 "Missing role data for version"
478 );
479 Ok(RoleRegisterResponse::MissingData)
480 }
481 RoleRegisterMessage::SearchValidators { search, version } => {
482 if version > self.version {
483 debug!(
484 msg_type = "SearchValidators",
485 version = version,
486 current_version = self.version,
487 schema_id = %search.schema_id,
488 namespace = %search.namespace,
489 "Request version exceeds current version"
490 );
491 return Ok(RoleRegisterResponse::OutOfVersion);
492 }
493
494 let mut all_val = if !search.schema_id.is_gov()
495 && let Some(validators) =
496 self.validators.get(&SchemaType::TrackerSchemas)
497 {
498 let mut schema_val = vec![];
500 for ((key, namespace), (interval, last)) in validators {
501 if namespace.is_ancestor_or_equal_of(&search.namespace)
502 {
503 if let Some(last) = last
504 && last <= &version
505 {
506 schema_val.push(key.clone());
507 } else if interval.contains(version) {
508 schema_val.push(key.clone());
509 }
510 }
511 }
512
513 schema_val
514 } else {
515 vec![]
516 };
517
518 let mut schema_val = if let Some(validators) =
519 self.validators.get(&search.schema_id)
520 {
521 let mut schema_val = vec![];
522 for ((key, namespace), (interval, last)) in validators {
523 if namespace.is_ancestor_or_equal_of(&search.namespace)
524 {
525 if let Some(last) = last
526 && last <= &version
527 {
528 schema_val.push(key.clone());
529 } else if interval.contains(version) {
530 schema_val.push(key.clone());
531 }
532 }
533 }
534
535 schema_val
536 } else {
537 vec![]
538 };
539
540 'data: {
541 let quorum = if let Some(quorum_schema) =
542 self.vali_quorum.get(&search.schema_id)
543 {
544 let Some(quorum) =
545 quorum_schema.get_prev_or_equal(version)
546 else {
547 break 'data;
548 };
549
550 quorum
551 } else {
552 break 'data;
553 };
554
555 if schema_val.is_empty() && all_val.is_empty() {
556 break 'data;
557 }
558
559 let mut validators = vec![];
560 validators.append(&mut schema_val);
561 validators.append(&mut all_val);
562
563 debug!(
564 msg_type = "SearchValidators",
565 version = version,
566 schema_id = %search.schema_id,
567 namespace = %search.namespace,
568 validators_count = validators.len(),
569 "Found validators successfully"
570 );
571
572 return Ok(RoleRegisterResponse::Validation(
573 RoleDataRegister {
574 workers: validators.iter().cloned().collect(),
575 quorum,
576 },
577 ));
578 }
579
580 debug!(
581 msg_type = "SearchValidators",
582 version = version,
583 schema_id = %search.schema_id,
584 namespace = %search.namespace,
585 "Missing validator data for version"
586 );
587 Ok(RoleRegisterResponse::MissingData)
588 }
589 RoleRegisterMessage::UpdateVersion { version } => {
590 if version > self.version || self.version == 0 {
591 self.on_event(
592 RoleRegisterEvent::UpdateVersion { version },
593 ctx,
594 )
595 .await;
596
597 debug!(
598 msg_type = "UpdateVersion",
599 version = version,
600 "Roles register updated successfully"
601 );
602 } else {
603 debug!(
604 msg_type = "UpdateVersion",
605 version = version,
606 current_version = self.version,
607 "Update skipped, version not greater than current"
608 );
609 }
610
611 Ok(RoleRegisterResponse::Ok)
612 }
613 RoleRegisterMessage::UpdateConfirm {
614 version,
615 new_approver,
616 remove_approver,
617 new_evaluator,
618 remove_evaluators,
619 new_validator,
620 remove_validators,
621 } => {
622 if version > self.version || self.version == 0 {
623 self.on_event(
624 RoleRegisterEvent::UpdateConfirm {
625 version,
626 new_approver,
627 remove_approver,
628 new_evaluator,
629 remove_evaluators,
630 new_validator,
631 remove_validators,
632 },
633 ctx,
634 )
635 .await;
636
637 debug!(
638 msg_type = "UpdateConfirm",
639 version = version,
640 "Roles register updated successfully"
641 );
642 } else {
643 debug!(
644 msg_type = "UpdateConfirm",
645 version = version,
646 current_version = self.version,
647 "Update skipped, version not greater than current"
648 );
649 }
650
651 Ok(RoleRegisterResponse::Ok)
652 }
653 RoleRegisterMessage::UpdateFact {
654 version,
655 appr_quorum,
656 eval_quorum,
657 vali_quorum,
658 new_approvers,
659 remove_approvers,
660 new_evaluators,
661 remove_evaluators,
662 new_validators,
663 remove_validators,
664 } => {
665 if version > self.version || self.version == 0 {
666 self.on_event(
667 RoleRegisterEvent::UpdateFact {
668 version,
669 appr_quorum,
670 eval_quorum,
671 vali_quorum,
672 new_approvers,
673 remove_approvers,
674 new_evaluators,
675 remove_evaluators,
676 new_validators,
677 remove_validators,
678 },
679 ctx,
680 )
681 .await;
682
683 debug!(
684 msg_type = "UpdateFact",
685 version = version,
686 "Roles register updated successfully"
687 );
688 } else {
689 debug!(
690 msg_type = "UpdateFact",
691 version = version,
692 current_version = self.version,
693 "Update skipped, version not greater than current"
694 );
695 }
696
697 Ok(RoleRegisterResponse::Ok)
698 }
699 }
700 }
701
702 async fn on_event(
703 &mut self,
704 event: RoleRegisterEvent,
705 ctx: &mut ActorContext<Self>,
706 ) {
707 if let Err(e) = self.persist(&event, ctx).await {
708 let version = match &event {
709 RoleRegisterEvent::UpdateFact { version, .. } => *version,
710 RoleRegisterEvent::UpdateVersion { version } => *version,
711 RoleRegisterEvent::UpdateConfirm { version, .. } => *version,
712 };
713 error!(
714 version = version,
715 error = %e,
716 "Failed to persist role register event"
717 );
718 emit_fail(ctx, e).await;
719 }
720 }
721}
722
723#[async_trait]
724impl PersistentActor for RoleRegister {
725 type Persistence = LightPersistence;
726 type InitParams = ();
727
728 fn create_initial(_params: Self::InitParams) -> Self {
729 Self::default()
730 }
731
732 fn apply(&mut self, event: &Self::Event) -> Result<(), ActorError> {
733 match event {
734 RoleRegisterEvent::UpdateVersion { version } => {
735 self.version = *version;
736 }
737 RoleRegisterEvent::UpdateConfirm {
738 version,
739 new_approver,
740 remove_approver,
741 new_evaluator,
742 remove_evaluators,
743 new_validator,
744 remove_validators,
745 } => {
746 self.version = *version;
747 if let Some(approver) = new_approver {
748 self.approvers.insert(approver.clone());
749 }
750
751 if let Some(evaluator) = new_evaluator {
752 self.evaluators
753 .entry(SchemaType::Governance)
754 .or_default()
755 .insert((evaluator.clone(), Namespace::new()));
756 }
757
758 if let Some(validator) = new_validator {
759 self.validators
760 .entry(SchemaType::Governance)
761 .or_default()
762 .entry((validator.clone(), Namespace::new()))
763 .or_default()
764 .1 = Some(*version);
765 }
766
767 self.approvers.remove(remove_approver);
768
769 for ((schema_id, evaluator), namespaces) in
770 remove_evaluators.iter()
771 {
772 for ns in namespaces.iter() {
773 self.evaluators
774 .entry(schema_id.clone())
775 .or_default()
776 .remove(&(evaluator.clone(), ns.clone()));
777 }
778 }
779
780 for ((schema_id, validator), namespaces) in
781 remove_validators.iter()
782 {
783 for ns in namespaces.iter() {
784 let (interval, last) = self
785 .validators
786 .entry(schema_id.clone())
787 .or_default()
788 .entry((validator.clone(), ns.clone()))
789 .or_default();
790 if let Some(last) = last.take() {
791 interval.insert(Interval {
792 lo: last,
793 hi: *version - 1,
794 });
795 }
796 }
797 }
798
799 debug!(
800 event_type = "UpdateFact",
801 version = version,
802 new_approver = new_approver.is_some(),
803 remove_approvers_count = 1,
804 new_evaluator = new_evaluator.is_some(),
805 remove_evaluators_count = remove_evaluators.len(),
806 new_validator = new_validator.is_some(),
807 remove_validators_count = remove_validators.len(),
808 "Role register state updated"
809 );
810 }
811 RoleRegisterEvent::UpdateFact {
812 version,
813 appr_quorum,
814 eval_quorum,
815 vali_quorum,
816 new_approvers,
817 remove_approvers,
818 new_evaluators,
819 remove_evaluators,
820 new_validators,
821 remove_validators,
822 } => {
823 self.version = *version;
824
825 if let Some(appr_quorum) = appr_quorum {
826 self.appr_quorum = appr_quorum.clone();
827 }
828
829 for (schema_id, quorum) in vali_quorum.iter() {
830 self.vali_quorum
831 .entry(schema_id.clone())
832 .or_default()
833 .insert(*version, quorum.clone());
834 }
835
836 for (schema_id, quorum) in eval_quorum.iter() {
837 self.eval_quorum.insert(schema_id.clone(), quorum.clone());
838 }
839
840 for approver in new_approvers.iter() {
841 self.approvers.insert(approver.clone());
842 }
843
844 for approver in remove_approvers.iter() {
845 self.approvers.remove(approver);
846 }
847
848 for ((schema_id, evaluator), namespaces) in
849 new_evaluators.iter()
850 {
851 for ns in namespaces.iter() {
852 self.evaluators
853 .entry(schema_id.clone())
854 .or_default()
855 .insert((evaluator.clone(), ns.clone()));
856 }
857 }
858
859 for ((schema_id, evaluator), namespaces) in
860 remove_evaluators.iter()
861 {
862 for ns in namespaces.iter() {
863 self.evaluators
864 .entry(schema_id.clone())
865 .or_default()
866 .remove(&(evaluator.clone(), ns.clone()));
867 }
868 }
869
870 for ((schema_id, validator), namespaces) in
871 new_validators.iter()
872 {
873 for ns in namespaces.iter() {
874 self.validators
875 .entry(schema_id.clone())
876 .or_default()
877 .entry((validator.clone(), ns.clone()))
878 .or_default()
879 .1 = Some(*version);
880 }
881 }
882
883 for ((schema_id, validator), namespaces) in
884 remove_validators.iter()
885 {
886 for ns in namespaces.iter() {
887 let (interval, last) = self
888 .validators
889 .entry(schema_id.clone())
890 .or_default()
891 .entry((validator.clone(), ns.clone()))
892 .or_default();
893 if let Some(last) = last.take() {
894 interval.insert(Interval {
895 lo: last,
896 hi: *version - 1,
897 });
898 }
899 }
900 }
901
902 debug!(
903 event_type = "UpdateFact",
904 version = version,
905 new_approvers_count = new_approvers.len(),
906 remove_approvers_count = remove_approvers.len(),
907 new_evaluators_count = new_evaluators.len(),
908 remove_evaluators_count = remove_evaluators.len(),
909 new_validators_count = new_validators.len(),
910 remove_validators_count = remove_validators.len(),
911 "Role register state updated"
912 );
913 }
914 }
915 Ok(())
916 }
917}
918
919impl Storable for RoleRegister {}