lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

authorization.rs (21599B)


      1 #![forbid(unsafe_code)]
      2 
      3 use crate::{RadrootsActorContext, RadrootsAuthorityError, RadrootsEventSigner};
      4 use radroots_events::contract::{RadrootsEventContract, event_contract};
      5 #[cfg(test)]
      6 use radroots_events::draft::RadrootsSignedNostrEventParts;
      7 use radroots_events::draft::{
      8     RadrootsDraftError, RadrootsFrozenEventDraft, RadrootsSignedNostrEvent,
      9     validate_signed_nostr_event_matches_draft,
     10 };
     11 
     12 #[cfg(not(feature = "std"))]
     13 use alloc::{borrow::ToOwned, string::ToString};
     14 #[cfg(feature = "std")]
     15 use std::{borrow::ToOwned, string::ToString};
     16 
     17 pub fn authorize_actor_for_contract(
     18     actor: &RadrootsActorContext,
     19     contract: &RadrootsEventContract,
     20 ) -> Result<(), RadrootsAuthorityError> {
     21     if actor.satisfies(contract.author_role) {
     22         Ok(())
     23     } else {
     24         Err(RadrootsAuthorityError::ActorRoleUnsatisfied {
     25             contract_id: contract.id.to_owned(),
     26             required_role: contract.author_role,
     27         })
     28     }
     29 }
     30 
     31 pub fn authorize_actor_for_draft(
     32     actor: &RadrootsActorContext,
     33     draft: &RadrootsFrozenEventDraft,
     34 ) -> Result<&'static RadrootsEventContract, RadrootsAuthorityError> {
     35     let contract = event_contract(draft.contract_id.as_str()).ok_or_else(|| {
     36         RadrootsAuthorityError::UnknownContract {
     37             contract_id: draft.contract_id.clone(),
     38         }
     39     })?;
     40     if contract.kind != draft.kind {
     41         return Err(RadrootsAuthorityError::DraftKindMismatch {
     42             contract_id: draft.contract_id.clone(),
     43             expected_kind: contract.kind,
     44             actual_kind: draft.kind,
     45         });
     46     }
     47     authorize_actor_for_contract(actor, contract)?;
     48     if actor.pubkey().as_str() != draft.expected_pubkey.as_str() {
     49         return Err(RadrootsAuthorityError::ActorPubkeyMismatch {
     50             expected_pubkey: draft.expected_pubkey.clone(),
     51             actor_pubkey: actor.pubkey().as_str().to_owned(),
     52         });
     53     }
     54     Ok(contract)
     55 }
     56 
     57 pub fn authorize_signer_for_draft<S>(
     58     signer: &S,
     59     draft: &RadrootsFrozenEventDraft,
     60 ) -> Result<(), RadrootsAuthorityError>
     61 where
     62     S: RadrootsEventSigner + ?Sized,
     63 {
     64     if signer.pubkey().as_str() == draft.expected_pubkey.as_str() {
     65         Ok(())
     66     } else {
     67         Err(RadrootsAuthorityError::SignerPubkeyMismatch {
     68             expected_pubkey: draft.expected_pubkey.clone(),
     69             signer_pubkey: signer.pubkey().as_str().to_owned(),
     70         })
     71     }
     72 }
     73 
     74 pub fn sign_authorized_draft<S>(
     75     actor: &RadrootsActorContext,
     76     signer: &S,
     77     draft: &RadrootsFrozenEventDraft,
     78 ) -> Result<RadrootsSignedNostrEvent, RadrootsAuthorityError>
     79 where
     80     S: RadrootsEventSigner + ?Sized,
     81 {
     82     authorize_actor_for_draft(actor, draft)?;
     83     authorize_signer_for_draft(signer, draft)?;
     84     let signed_event = signer.sign_frozen_draft(draft)?;
     85     validate_signed_event_matches_draft(&signed_event, draft)?;
     86     Ok(signed_event)
     87 }
     88 
     89 pub fn validate_signed_event_matches_draft(
     90     signed_event: &RadrootsSignedNostrEvent,
     91     draft: &RadrootsFrozenEventDraft,
     92 ) -> Result<(), RadrootsAuthorityError> {
     93     validate_signed_nostr_event_matches_draft(signed_event, draft)
     94         .map_err(authority_error_from_draft_validation)
     95 }
     96 
     97 fn authority_error_from_draft_validation(error: RadrootsDraftError) -> RadrootsAuthorityError {
     98     match error {
     99         RadrootsDraftError::SignedEventPubkeyMismatch {
    100             expected_pubkey,
    101             actual_pubkey,
    102         } => RadrootsAuthorityError::SignedEventPubkeyMismatch {
    103             expected_pubkey,
    104             actual_pubkey,
    105         },
    106         RadrootsDraftError::SignedEventIdMismatch {
    107             expected_event_id,
    108             actual_event_id,
    109         } => RadrootsAuthorityError::SignedEventIdMismatch {
    110             expected_event_id,
    111             actual_event_id,
    112         },
    113         RadrootsDraftError::SignedEventCreatedAtMismatch {
    114             expected_created_at,
    115             actual_created_at,
    116         } => RadrootsAuthorityError::SignedEventCreatedAtMismatch {
    117             expected_created_at,
    118             actual_created_at,
    119         },
    120         RadrootsDraftError::SignedEventKindMismatch {
    121             expected_kind,
    122             actual_kind,
    123         } => RadrootsAuthorityError::SignedEventKindMismatch {
    124             expected_kind,
    125             actual_kind,
    126         },
    127         RadrootsDraftError::SignedEventTagsMismatch {
    128             expected_len,
    129             actual_len,
    130         } => RadrootsAuthorityError::SignedEventTagsMismatch {
    131             expected_len,
    132             actual_len,
    133         },
    134         RadrootsDraftError::SignedEventContentMismatch {
    135             expected_len,
    136             actual_len,
    137         } => RadrootsAuthorityError::SignedEventContentMismatch {
    138             expected_len,
    139             actual_len,
    140         },
    141         RadrootsDraftError::SignedEventComputedIdMismatch {
    142             expected_event_id,
    143             computed_event_id,
    144         } => RadrootsAuthorityError::SignedEventComputedIdMismatch {
    145             expected_event_id,
    146             computed_event_id,
    147         },
    148         error => RadrootsAuthorityError::SignedEventComputedIdInvalid {
    149             message: error.to_string(),
    150         },
    151     }
    152 }
    153 
    154 #[cfg(test)]
    155 mod tests {
    156     use super::*;
    157     use crate::RadrootsSignerError;
    158     use radroots_events::contract::{RadrootsActorRole, event_contract};
    159     use radroots_events::ids::RadrootsPublicKey;
    160     use radroots_events::kinds::{KIND_LISTING, KIND_ORDER_REQUEST, KIND_POST};
    161 
    162     fn hex_64(character: char) -> String {
    163         std::iter::repeat_n(character, 64).collect()
    164     }
    165 
    166     fn hex_128(character: char) -> String {
    167         std::iter::repeat_n(character, 128).collect()
    168     }
    169 
    170     fn seller_actor(pubkey: &str) -> RadrootsActorContext {
    171         RadrootsActorContext::explicit_pubkey(pubkey, [RadrootsActorRole::Seller]).expect("seller")
    172     }
    173 
    174     fn buyer_actor(pubkey: &str) -> RadrootsActorContext {
    175         RadrootsActorContext::explicit_pubkey(pubkey, [RadrootsActorRole::Buyer]).expect("buyer")
    176     }
    177 
    178     fn listing_draft(pubkey: &str) -> RadrootsFrozenEventDraft {
    179         RadrootsFrozenEventDraft::new(
    180             "radroots.listing.published.v1",
    181             KIND_LISTING,
    182             1_700_000_000,
    183             vec![vec!["d".to_owned(), "listing-a".to_owned()]],
    184             "{}",
    185             pubkey,
    186         )
    187         .expect("listing draft")
    188     }
    189 
    190     #[derive(Default)]
    191     struct SignedEventOverrides {
    192         event_id: Option<String>,
    193         created_at: Option<u32>,
    194         kind: Option<u32>,
    195         tags: Option<Vec<Vec<String>>>,
    196         content: Option<String>,
    197     }
    198 
    199     struct StaticSigner {
    200         pubkey: RadrootsPublicKey,
    201         overrides: SignedEventOverrides,
    202     }
    203 
    204     impl StaticSigner {
    205         fn new(pubkey: &str) -> Self {
    206             Self {
    207                 pubkey: RadrootsPublicKey::parse(pubkey).expect("pubkey"),
    208                 overrides: SignedEventOverrides::default(),
    209             }
    210         }
    211 
    212         fn with_event_id(pubkey: &str, event_id: String) -> Self {
    213             Self::with_overrides(
    214                 pubkey,
    215                 SignedEventOverrides {
    216                     event_id: Some(event_id),
    217                     ..SignedEventOverrides::default()
    218                 },
    219             )
    220         }
    221 
    222         fn with_overrides(pubkey: &str, overrides: SignedEventOverrides) -> Self {
    223             Self {
    224                 pubkey: RadrootsPublicKey::parse(pubkey).expect("pubkey"),
    225                 overrides,
    226             }
    227         }
    228     }
    229 
    230     impl RadrootsEventSigner for StaticSigner {
    231         fn pubkey(&self) -> &RadrootsPublicKey {
    232             &self.pubkey
    233         }
    234 
    235         fn sign_frozen_draft(
    236             &self,
    237             draft: &RadrootsFrozenEventDraft,
    238         ) -> Result<RadrootsSignedNostrEvent, RadrootsSignerError> {
    239             RadrootsSignedNostrEvent::new(RadrootsSignedNostrEventParts {
    240                 id: self
    241                     .overrides
    242                     .event_id
    243                     .as_deref()
    244                     .unwrap_or(draft.expected_event_id.as_str())
    245                     .to_owned(),
    246                 pubkey: self.pubkey.to_string(),
    247                 created_at: self.overrides.created_at.unwrap_or(draft.created_at),
    248                 kind: self.overrides.kind.unwrap_or(draft.kind),
    249                 tags: self
    250                     .overrides
    251                     .tags
    252                     .clone()
    253                     .unwrap_or_else(|| draft.tags.clone()),
    254                 content: self
    255                     .overrides
    256                     .content
    257                     .clone()
    258                     .unwrap_or_else(|| draft.content.clone()),
    259                 sig: hex_128('f'),
    260                 raw_json: "{}".to_owned(),
    261             })
    262             .map_err(|error| RadrootsSignerError::SigningFailed {
    263                 message: error.to_string(),
    264             })
    265         }
    266     }
    267 
    268     fn signed_event_from_draft(draft: &RadrootsFrozenEventDraft) -> RadrootsSignedNostrEvent {
    269         RadrootsSignedNostrEvent::new(RadrootsSignedNostrEventParts {
    270             id: draft.expected_event_id.clone(),
    271             pubkey: draft.expected_pubkey.clone(),
    272             created_at: draft.created_at,
    273             kind: draft.kind,
    274             tags: draft.tags.clone(),
    275             content: draft.content.clone(),
    276             sig: hex_128('f'),
    277             raw_json: "{}".to_owned(),
    278         })
    279         .expect("signed event")
    280     }
    281 
    282     #[test]
    283     fn buyer_and_seller_contract_roles_match_current_contracts() {
    284         let listing = event_contract("radroots.listing.published.v1").expect("listing contract");
    285         let order_request = event_contract("radroots.order.request.v1").expect("order contract");
    286         let order_revision_proposal =
    287             event_contract("radroots.order.revision_proposal.v1").expect("revision proposal");
    288         let order_revision_decision =
    289             event_contract("radroots.order.revision_decision.v1").expect("revision decision");
    290         let seller = seller_actor(hex_64('a').as_str());
    291         let buyer = buyer_actor(hex_64('b').as_str());
    292 
    293         assert_eq!(listing.author_role, RadrootsActorRole::Seller);
    294         assert!(authorize_actor_for_contract(&seller, listing).is_ok());
    295         assert!(matches!(
    296             authorize_actor_for_contract(&buyer, listing),
    297             Err(RadrootsAuthorityError::ActorRoleUnsatisfied { .. })
    298         ));
    299         assert!(authorize_actor_for_contract(&buyer, order_request).is_ok());
    300         assert!(matches!(
    301             authorize_actor_for_contract(&seller, order_request),
    302             Err(RadrootsAuthorityError::ActorRoleUnsatisfied { .. })
    303         ));
    304         assert_eq!(
    305             order_revision_proposal.author_role,
    306             RadrootsActorRole::Seller
    307         );
    308         assert!(authorize_actor_for_contract(&seller, order_revision_proposal).is_ok());
    309         assert!(matches!(
    310             authorize_actor_for_contract(&buyer, order_revision_proposal),
    311             Err(RadrootsAuthorityError::ActorRoleUnsatisfied { .. })
    312         ));
    313         assert_eq!(
    314             order_revision_decision.author_role,
    315             RadrootsActorRole::Buyer
    316         );
    317         assert!(authorize_actor_for_contract(&buyer, order_revision_decision).is_ok());
    318         assert!(matches!(
    319             authorize_actor_for_contract(&seller, order_revision_decision),
    320             Err(RadrootsAuthorityError::ActorRoleUnsatisfied { .. })
    321         ));
    322     }
    323 
    324     #[test]
    325     fn actor_pubkey_mismatch_fails() {
    326         let draft = listing_draft(hex_64('a').as_str());
    327         let actor = seller_actor(hex_64('b').as_str());
    328 
    329         assert!(matches!(
    330             authorize_actor_for_draft(&actor, &draft),
    331             Err(RadrootsAuthorityError::ActorPubkeyMismatch { .. })
    332         ));
    333     }
    334 
    335     #[test]
    336     fn signer_pubkey_mismatch_fails() {
    337         let draft = listing_draft(hex_64('a').as_str());
    338         let signer = StaticSigner::new(hex_64('b').as_str());
    339 
    340         assert!(matches!(
    341             authorize_signer_for_draft(&signer, &draft),
    342             Err(RadrootsAuthorityError::SignerPubkeyMismatch { .. })
    343         ));
    344     }
    345 
    346     #[test]
    347     fn unknown_contract_and_kind_mismatch_fail() {
    348         let actor = seller_actor(hex_64('a').as_str());
    349         let unknown = RadrootsFrozenEventDraft {
    350             contract_id: "radroots.unknown.v1".to_owned(),
    351             contract_registry_version: 1,
    352             kind: KIND_LISTING,
    353             created_at: 1_700_000_000,
    354             tags: Vec::new(),
    355             content: "{}".to_owned(),
    356             expected_pubkey: hex_64('a'),
    357             expected_event_id: hex_64('e'),
    358         };
    359         assert!(matches!(
    360             authorize_actor_for_draft(&actor, &unknown),
    361             Err(RadrootsAuthorityError::UnknownContract { .. })
    362         ));
    363 
    364         let wrong_kind = RadrootsFrozenEventDraft {
    365             contract_id: "radroots.listing.published.v1".to_owned(),
    366             contract_registry_version: 1,
    367             kind: KIND_POST,
    368             created_at: 1_700_000_000,
    369             tags: Vec::new(),
    370             content: "{}".to_owned(),
    371             expected_pubkey: hex_64('a'),
    372             expected_event_id: hex_64('e'),
    373         };
    374         assert!(matches!(
    375             authorize_actor_for_draft(&actor, &wrong_kind),
    376             Err(RadrootsAuthorityError::DraftKindMismatch {
    377                 expected_kind: KIND_LISTING,
    378                 actual_kind: KIND_POST,
    379                 ..
    380             })
    381         ));
    382     }
    383 
    384     #[test]
    385     fn signed_event_id_mismatch_fails() {
    386         let pubkey = hex_64('a');
    387         let draft = listing_draft(pubkey.as_str());
    388         let actor = seller_actor(pubkey.as_str());
    389         let signer = StaticSigner::with_event_id(pubkey.as_str(), hex_64('e'));
    390 
    391         assert!(matches!(
    392             sign_authorized_draft(&actor, &signer, &draft),
    393             Err(RadrootsAuthorityError::SignedEventIdMismatch { .. })
    394         ));
    395     }
    396 
    397     #[test]
    398     fn signed_event_created_at_mismatch_fails() {
    399         let pubkey = hex_64('a');
    400         let draft = listing_draft(pubkey.as_str());
    401         let actor = seller_actor(pubkey.as_str());
    402         let signer = StaticSigner::with_overrides(
    403             pubkey.as_str(),
    404             SignedEventOverrides {
    405                 created_at: Some(draft.created_at + 1),
    406                 ..SignedEventOverrides::default()
    407             },
    408         );
    409 
    410         assert!(matches!(
    411             sign_authorized_draft(&actor, &signer, &draft),
    412             Err(RadrootsAuthorityError::SignedEventCreatedAtMismatch { .. })
    413         ));
    414     }
    415 
    416     #[test]
    417     fn signed_event_kind_mismatch_fails() {
    418         let pubkey = hex_64('a');
    419         let draft = listing_draft(pubkey.as_str());
    420         let actor = seller_actor(pubkey.as_str());
    421         let signer = StaticSigner::with_overrides(
    422             pubkey.as_str(),
    423             SignedEventOverrides {
    424                 kind: Some(KIND_POST),
    425                 ..SignedEventOverrides::default()
    426             },
    427         );
    428 
    429         assert!(matches!(
    430             sign_authorized_draft(&actor, &signer, &draft),
    431             Err(RadrootsAuthorityError::SignedEventKindMismatch {
    432                 expected_kind: KIND_LISTING,
    433                 actual_kind: KIND_POST
    434             })
    435         ));
    436     }
    437 
    438     #[test]
    439     fn signed_event_tags_mismatch_fails() {
    440         let pubkey = hex_64('a');
    441         let draft = listing_draft(pubkey.as_str());
    442         let actor = seller_actor(pubkey.as_str());
    443         let signer = StaticSigner::with_overrides(
    444             pubkey.as_str(),
    445             SignedEventOverrides {
    446                 tags: Some(vec![vec!["d".to_owned(), "listing-b".to_owned()]]),
    447                 ..SignedEventOverrides::default()
    448             },
    449         );
    450 
    451         let error = sign_authorized_draft(&actor, &signer, &draft).unwrap_err();
    452 
    453         assert_eq!(
    454             error,
    455             RadrootsAuthorityError::SignedEventTagsMismatch {
    456                 expected_len: 1,
    457                 actual_len: 1
    458             }
    459         );
    460         assert!(!format!("{error:?}").contains("listing-b"));
    461         assert!(!error.to_string().contains("listing-b"));
    462     }
    463 
    464     #[test]
    465     fn signed_event_content_mismatch_fails() {
    466         let pubkey = hex_64('a');
    467         let draft = listing_draft(pubkey.as_str());
    468         let actor = seller_actor(pubkey.as_str());
    469         let signer = StaticSigner::with_overrides(
    470             pubkey.as_str(),
    471             SignedEventOverrides {
    472                 content: Some("{\"changed\":true}".to_owned()),
    473                 ..SignedEventOverrides::default()
    474             },
    475         );
    476 
    477         let error = sign_authorized_draft(&actor, &signer, &draft).unwrap_err();
    478 
    479         assert_eq!(
    480             error,
    481             RadrootsAuthorityError::SignedEventContentMismatch {
    482                 expected_len: 2,
    483                 actual_len: 16
    484             }
    485         );
    486         assert!(!format!("{error:?}").contains("changed"));
    487         assert!(!error.to_string().contains("changed"));
    488     }
    489 
    490     #[test]
    491     fn signed_event_exactly_matching_draft_passes() {
    492         let pubkey = hex_64('a');
    493         let draft = listing_draft(pubkey.as_str());
    494         let signed = signed_event_from_draft(&draft);
    495 
    496         validate_signed_event_matches_draft(&signed, &draft).expect("signed event matches draft");
    497     }
    498 
    499     #[test]
    500     fn signed_event_pubkey_mismatch_fails() {
    501         let pubkey = hex_64('a');
    502         let draft = listing_draft(pubkey.as_str());
    503         let signed = RadrootsSignedNostrEvent::new(RadrootsSignedNostrEventParts {
    504             id: draft.expected_event_id.clone(),
    505             pubkey: hex_64('b'),
    506             created_at: draft.created_at,
    507             kind: draft.kind,
    508             tags: draft.tags.clone(),
    509             content: draft.content.clone(),
    510             sig: hex_128('f'),
    511             raw_json: "{}".to_owned(),
    512         })
    513         .expect("signed event");
    514 
    515         assert!(matches!(
    516             validate_signed_event_matches_draft(&signed, &draft),
    517             Err(RadrootsAuthorityError::SignedEventPubkeyMismatch { .. })
    518         ));
    519     }
    520 
    521     #[test]
    522     fn draft_validation_fallback_errors_map_to_computed_id_invalid() {
    523         let error = authority_error_from_draft_validation(RadrootsDraftError::UnknownContract(
    524             "radroots.unknown.v1".to_owned(),
    525         ));
    526 
    527         assert!(matches!(
    528             error,
    529             RadrootsAuthorityError::SignedEventComputedIdInvalid { .. }
    530         ));
    531     }
    532 
    533     #[test]
    534     fn signed_event_computed_id_mismatch_fails() {
    535         let pubkey = hex_64('a');
    536         let inconsistent_draft = RadrootsFrozenEventDraft {
    537             contract_id: "radroots.listing.published.v1".to_owned(),
    538             contract_registry_version: 1,
    539             kind: KIND_LISTING,
    540             created_at: 1_700_000_000,
    541             tags: vec![vec!["d".to_owned(), "listing-a".to_owned()]],
    542             content: "{}".to_owned(),
    543             expected_pubkey: pubkey,
    544             expected_event_id: hex_64('e'),
    545         };
    546         let signed = signed_event_from_draft(&inconsistent_draft);
    547 
    548         assert!(matches!(
    549             validate_signed_event_matches_draft(&signed, &inconsistent_draft),
    550             Err(RadrootsAuthorityError::SignedEventComputedIdMismatch { .. })
    551         ));
    552     }
    553 
    554     #[test]
    555     fn sign_authorized_draft_calls_full_integrity_check() {
    556         let pubkey = hex_64('a');
    557         let inconsistent_draft = RadrootsFrozenEventDraft {
    558             contract_id: "radroots.listing.published.v1".to_owned(),
    559             contract_registry_version: 1,
    560             kind: KIND_LISTING,
    561             created_at: 1_700_000_000,
    562             tags: vec![vec!["d".to_owned(), "listing-a".to_owned()]],
    563             content: "{}".to_owned(),
    564             expected_pubkey: pubkey.clone(),
    565             expected_event_id: hex_64('e'),
    566         };
    567         let actor = seller_actor(pubkey.as_str());
    568         let signer = StaticSigner::new(pubkey.as_str());
    569 
    570         assert!(matches!(
    571             sign_authorized_draft(&actor, &signer, &inconsistent_draft),
    572             Err(RadrootsAuthorityError::SignedEventComputedIdMismatch { .. })
    573         ));
    574     }
    575 
    576     #[test]
    577     fn static_signer_maps_invalid_signed_event_parts() {
    578         let pubkey = hex_64('a');
    579         let mut draft = listing_draft(pubkey.as_str());
    580         draft.expected_event_id = "bad-id".to_owned();
    581         let signer = StaticSigner::new(pubkey.as_str());
    582 
    583         assert!(matches!(
    584             signer.sign_frozen_draft(&draft),
    585             Err(RadrootsSignerError::SigningFailed { .. })
    586         ));
    587     }
    588 
    589     #[test]
    590     fn authorized_actor_and_signer_return_signed_event() {
    591         let pubkey = hex_64('a');
    592         let draft = listing_draft(pubkey.as_str());
    593         let actor = seller_actor(pubkey.as_str());
    594         let signer = StaticSigner::new(pubkey.as_str());
    595 
    596         let signed = sign_authorized_draft(&actor, &signer, &draft).expect("signed");
    597 
    598         assert_eq!(signed.id, draft.expected_event_id);
    599         assert_eq!(signed.pubkey, draft.expected_pubkey);
    600         assert_eq!(signed.kind, KIND_LISTING);
    601     }
    602 
    603     #[test]
    604     fn order_request_draft_requires_buyer_role() {
    605         let pubkey = hex_64('a');
    606         let draft = RadrootsFrozenEventDraft::new(
    607             "radroots.order.request.v1",
    608             KIND_ORDER_REQUEST,
    609             1_700_000_000,
    610             Vec::new(),
    611             "{}",
    612             pubkey.as_str(),
    613         )
    614         .expect("order draft");
    615         let buyer = buyer_actor(pubkey.as_str());
    616         let seller = seller_actor(pubkey.as_str());
    617 
    618         assert!(authorize_actor_for_draft(&buyer, &draft).is_ok());
    619         assert!(matches!(
    620             authorize_actor_for_draft(&seller, &draft),
    621             Err(RadrootsAuthorityError::ActorRoleUnsatisfied { .. })
    622         ));
    623     }
    624 }