lib

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

error.rs (13212B)


      1 #![forbid(unsafe_code)]
      2 
      3 use core::fmt;
      4 
      5 #[cfg(not(feature = "std"))]
      6 use alloc::string::String;
      7 #[cfg(feature = "std")]
      8 use std::string::String;
      9 
     10 #[derive(Debug, PartialEq, Eq)]
     11 pub enum RadrootsAuthorityError {
     12     InvalidActorPubkey,
     13 
     14     InvalidActorAccountIdEmpty,
     15 
     16     InvalidActorAccountIdUntrimmed,
     17 
     18     InvalidActorAccountIdControlCharacter,
     19 
     20     InvalidActorAccountIdTooLong {
     21         max_len: usize,
     22     },
     23 
     24     InvalidSignerPubkey,
     25 
     26     UnknownContract {
     27         contract_id: String,
     28     },
     29 
     30     DraftKindMismatch {
     31         contract_id: String,
     32         expected_kind: u32,
     33         actual_kind: u32,
     34     },
     35 
     36     ActorRoleUnsatisfied {
     37         contract_id: String,
     38         required_role: radroots_events::contract::RadrootsActorRole,
     39     },
     40 
     41     ActorPubkeyMismatch {
     42         expected_pubkey: String,
     43         actor_pubkey: String,
     44     },
     45 
     46     SignerPubkeyMismatch {
     47         expected_pubkey: String,
     48         signer_pubkey: String,
     49     },
     50 
     51     SignedEventPubkeyMismatch {
     52         expected_pubkey: String,
     53         actual_pubkey: String,
     54     },
     55 
     56     SignedEventIdMismatch {
     57         expected_event_id: String,
     58         actual_event_id: String,
     59     },
     60 
     61     SignedEventCreatedAtMismatch {
     62         expected_created_at: u32,
     63         actual_created_at: u32,
     64     },
     65 
     66     SignedEventKindMismatch {
     67         expected_kind: u32,
     68         actual_kind: u32,
     69     },
     70 
     71     SignedEventTagsMismatch {
     72         expected_len: usize,
     73         actual_len: usize,
     74     },
     75 
     76     SignedEventContentMismatch {
     77         expected_len: usize,
     78         actual_len: usize,
     79     },
     80 
     81     SignedEventComputedIdInvalid {
     82         message: String,
     83     },
     84 
     85     SignedEventComputedIdMismatch {
     86         expected_event_id: String,
     87         computed_event_id: String,
     88     },
     89 
     90     Signer(RadrootsSignerError),
     91 }
     92 
     93 impl fmt::Display for RadrootsAuthorityError {
     94     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     95         match self {
     96             Self::InvalidActorPubkey => write!(f, "invalid actor public key"),
     97             Self::InvalidActorAccountIdEmpty => write!(f, "invalid actor account id: empty"),
     98             Self::InvalidActorAccountIdUntrimmed => {
     99                 write!(
    100                     f,
    101                     "invalid actor account id: contains leading or trailing whitespace"
    102                 )
    103             }
    104             Self::InvalidActorAccountIdControlCharacter => {
    105                 write!(f, "invalid actor account id: contains a control character")
    106             }
    107             Self::InvalidActorAccountIdTooLong { max_len } => {
    108                 write!(
    109                     f,
    110                     "invalid actor account id: longer than {max_len} characters"
    111                 )
    112             }
    113             Self::InvalidSignerPubkey => write!(f, "invalid signer public key"),
    114             Self::UnknownContract { contract_id } => {
    115                 write!(f, "unknown event contract `{contract_id}`")
    116             }
    117             Self::DraftKindMismatch {
    118                 contract_id,
    119                 expected_kind,
    120                 actual_kind,
    121             } => write!(
    122                 f,
    123                 "event contract `{contract_id}` expects kind {expected_kind}, got {actual_kind}"
    124             ),
    125             Self::ActorRoleUnsatisfied {
    126                 contract_id,
    127                 required_role,
    128             } => write!(
    129                 f,
    130                 "actor does not satisfy role {required_role:?} for contract `{contract_id}`"
    131             ),
    132             Self::ActorPubkeyMismatch {
    133                 expected_pubkey,
    134                 actor_pubkey,
    135             } => write!(
    136                 f,
    137                 "actor pubkey mismatch: expected {expected_pubkey}, got {actor_pubkey}"
    138             ),
    139             Self::SignerPubkeyMismatch {
    140                 expected_pubkey,
    141                 signer_pubkey,
    142             } => write!(
    143                 f,
    144                 "signer pubkey mismatch: expected {expected_pubkey}, got {signer_pubkey}"
    145             ),
    146             Self::SignedEventPubkeyMismatch {
    147                 expected_pubkey,
    148                 actual_pubkey,
    149             } => write!(
    150                 f,
    151                 "signed event pubkey mismatch: expected {expected_pubkey}, got {actual_pubkey}"
    152             ),
    153             Self::SignedEventIdMismatch {
    154                 expected_event_id,
    155                 actual_event_id,
    156             } => write!(
    157                 f,
    158                 "signed event id mismatch: expected {expected_event_id}, got {actual_event_id}"
    159             ),
    160             Self::SignedEventCreatedAtMismatch {
    161                 expected_created_at,
    162                 actual_created_at,
    163             } => write!(
    164                 f,
    165                 "signed event created_at mismatch: expected {expected_created_at}, got {actual_created_at}"
    166             ),
    167             Self::SignedEventKindMismatch {
    168                 expected_kind,
    169                 actual_kind,
    170             } => write!(
    171                 f,
    172                 "signed event kind mismatch: expected {expected_kind}, got {actual_kind}"
    173             ),
    174             Self::SignedEventTagsMismatch {
    175                 expected_len,
    176                 actual_len,
    177             } => write!(
    178                 f,
    179                 "signed event tags mismatch: expected {expected_len} tags, got {actual_len} tags"
    180             ),
    181             Self::SignedEventContentMismatch {
    182                 expected_len,
    183                 actual_len,
    184             } => write!(
    185                 f,
    186                 "signed event content mismatch: expected {expected_len} bytes, got {actual_len} bytes"
    187             ),
    188             Self::SignedEventComputedIdInvalid { message } => {
    189                 write!(
    190                     f,
    191                     "signed event computed id could not be derived: {message}"
    192                 )
    193             }
    194             Self::SignedEventComputedIdMismatch {
    195                 expected_event_id,
    196                 computed_event_id,
    197             } => write!(
    198                 f,
    199                 "signed event computed id mismatch: expected {expected_event_id}, computed {computed_event_id}"
    200             ),
    201             Self::Signer(error) => write!(f, "signer error: {error}"),
    202         }
    203     }
    204 }
    205 
    206 #[cfg(feature = "std")]
    207 impl std::error::Error for RadrootsAuthorityError {
    208     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
    209         match self {
    210             Self::Signer(error) => Some(error),
    211             _ => None,
    212         }
    213     }
    214 }
    215 
    216 impl From<RadrootsSignerError> for RadrootsAuthorityError {
    217     fn from(error: RadrootsSignerError) -> Self {
    218         Self::Signer(error)
    219     }
    220 }
    221 
    222 #[derive(Debug, PartialEq, Eq)]
    223 pub enum RadrootsSignerError {
    224     Unavailable,
    225 
    226     Rejected,
    227 
    228     SigningFailed { message: String },
    229 }
    230 
    231 impl fmt::Display for RadrootsSignerError {
    232     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    233         match self {
    234             Self::Unavailable => write!(f, "signer unavailable"),
    235             Self::Rejected => write!(f, "signer rejected draft"),
    236             Self::SigningFailed { message } => write!(f, "signing failed: {message}"),
    237         }
    238     }
    239 }
    240 
    241 #[cfg(feature = "std")]
    242 impl std::error::Error for RadrootsSignerError {}
    243 
    244 #[cfg(test)]
    245 mod tests {
    246     use super::*;
    247     use radroots_events::contract::RadrootsActorRole;
    248     use std::error::Error as _;
    249 
    250     #[test]
    251     fn authority_error_display_uses_contract_messages() {
    252         let cases = [
    253             (
    254                 RadrootsAuthorityError::InvalidActorPubkey,
    255                 "invalid actor public key",
    256             ),
    257             (
    258                 RadrootsAuthorityError::InvalidActorAccountIdEmpty,
    259                 "invalid actor account id: empty",
    260             ),
    261             (
    262                 RadrootsAuthorityError::InvalidActorAccountIdUntrimmed,
    263                 "invalid actor account id: contains leading or trailing whitespace",
    264             ),
    265             (
    266                 RadrootsAuthorityError::InvalidActorAccountIdControlCharacter,
    267                 "invalid actor account id: contains a control character",
    268             ),
    269             (
    270                 RadrootsAuthorityError::InvalidActorAccountIdTooLong { max_len: 128 },
    271                 "invalid actor account id: longer than 128 characters",
    272             ),
    273             (
    274                 RadrootsAuthorityError::InvalidSignerPubkey,
    275                 "invalid signer public key",
    276             ),
    277             (
    278                 RadrootsAuthorityError::UnknownContract {
    279                     contract_id: "radroots.unknown.v1".to_owned(),
    280                 },
    281                 "unknown event contract `radroots.unknown.v1`",
    282             ),
    283             (
    284                 RadrootsAuthorityError::DraftKindMismatch {
    285                     contract_id: "radroots.social.post.v1".to_owned(),
    286                     expected_kind: 1,
    287                     actual_kind: 2,
    288                 },
    289                 "event contract `radroots.social.post.v1` expects kind 1, got 2",
    290             ),
    291             (
    292                 RadrootsAuthorityError::ActorRoleUnsatisfied {
    293                     contract_id: "radroots.listing.published.v1".to_owned(),
    294                     required_role: RadrootsActorRole::Seller,
    295                 },
    296                 "actor does not satisfy role Seller for contract `radroots.listing.published.v1`",
    297             ),
    298             (
    299                 RadrootsAuthorityError::ActorPubkeyMismatch {
    300                     expected_pubkey: "expected".to_owned(),
    301                     actor_pubkey: "actor".to_owned(),
    302                 },
    303                 "actor pubkey mismatch: expected expected, got actor",
    304             ),
    305             (
    306                 RadrootsAuthorityError::SignerPubkeyMismatch {
    307                     expected_pubkey: "expected".to_owned(),
    308                     signer_pubkey: "signer".to_owned(),
    309                 },
    310                 "signer pubkey mismatch: expected expected, got signer",
    311             ),
    312             (
    313                 RadrootsAuthorityError::SignedEventPubkeyMismatch {
    314                     expected_pubkey: "expected".to_owned(),
    315                     actual_pubkey: "actual".to_owned(),
    316                 },
    317                 "signed event pubkey mismatch: expected expected, got actual",
    318             ),
    319             (
    320                 RadrootsAuthorityError::SignedEventIdMismatch {
    321                     expected_event_id: "expected".to_owned(),
    322                     actual_event_id: "actual".to_owned(),
    323                 },
    324                 "signed event id mismatch: expected expected, got actual",
    325             ),
    326             (
    327                 RadrootsAuthorityError::SignedEventCreatedAtMismatch {
    328                     expected_created_at: 1,
    329                     actual_created_at: 2,
    330                 },
    331                 "signed event created_at mismatch: expected 1, got 2",
    332             ),
    333             (
    334                 RadrootsAuthorityError::SignedEventKindMismatch {
    335                     expected_kind: 1,
    336                     actual_kind: 2,
    337                 },
    338                 "signed event kind mismatch: expected 1, got 2",
    339             ),
    340             (
    341                 RadrootsAuthorityError::SignedEventTagsMismatch {
    342                     expected_len: 2,
    343                     actual_len: 1,
    344                 },
    345                 "signed event tags mismatch: expected 2 tags, got 1 tags",
    346             ),
    347             (
    348                 RadrootsAuthorityError::SignedEventContentMismatch {
    349                     expected_len: 17,
    350                     actual_len: 2,
    351                 },
    352                 "signed event content mismatch: expected 17 bytes, got 2 bytes",
    353             ),
    354             (
    355                 RadrootsAuthorityError::SignedEventComputedIdInvalid {
    356                     message: "invalid event id".to_owned(),
    357                 },
    358                 "signed event computed id could not be derived: invalid event id",
    359             ),
    360             (
    361                 RadrootsAuthorityError::SignedEventComputedIdMismatch {
    362                     expected_event_id: "expected".to_owned(),
    363                     computed_event_id: "computed".to_owned(),
    364                 },
    365                 "signed event computed id mismatch: expected expected, computed computed",
    366             ),
    367         ];
    368 
    369         for (error, expected) in cases {
    370             assert_eq!(error.to_string(), expected);
    371             assert!(error.source().is_none());
    372         }
    373     }
    374 
    375     #[test]
    376     fn signer_error_display_and_source_are_stable() {
    377         assert_eq!(
    378             RadrootsSignerError::Unavailable.to_string(),
    379             "signer unavailable"
    380         );
    381         assert_eq!(
    382             RadrootsSignerError::Rejected.to_string(),
    383             "signer rejected draft"
    384         );
    385 
    386         let signer_error = RadrootsSignerError::SigningFailed {
    387             message: "deterministic failure".to_owned(),
    388         };
    389         assert_eq!(
    390             signer_error.to_string(),
    391             "signing failed: deterministic failure"
    392         );
    393 
    394         let authority_error = RadrootsAuthorityError::from(signer_error);
    395         assert_eq!(
    396             authority_error.to_string(),
    397             "signer error: signing failed: deterministic failure"
    398         );
    399         assert_eq!(
    400             authority_error.source().expect("signer source").to_string(),
    401             "signing failed: deterministic failure"
    402         );
    403     }
    404 }