signer.rs (5900B)
1 #![forbid(unsafe_code)] 2 3 use crate::{RadrootsAuthorityError, RadrootsSignerError}; 4 #[cfg(test)] 5 use radroots_events::draft::RadrootsSignedNostrEventParts; 6 use radroots_events::draft::{RadrootsFrozenEventDraft, RadrootsSignedNostrEvent}; 7 use radroots_events::ids::RadrootsPublicKey; 8 9 #[derive(Clone, Debug, PartialEq, Eq)] 10 pub struct RadrootsSignerIdentity { 11 pub pubkey: RadrootsPublicKey, 12 } 13 14 impl RadrootsSignerIdentity { 15 pub fn new(pubkey: impl AsRef<str>) -> Result<Self, RadrootsAuthorityError> { 16 let pubkey = RadrootsPublicKey::parse(pubkey.as_ref()) 17 .map_err(|_| RadrootsAuthorityError::InvalidSignerPubkey)?; 18 Ok(Self { pubkey }) 19 } 20 21 pub fn pubkey(&self) -> &RadrootsPublicKey { 22 &self.pubkey 23 } 24 } 25 26 pub trait RadrootsEventSigner { 27 fn pubkey(&self) -> &RadrootsPublicKey; 28 29 fn sign_frozen_draft( 30 &self, 31 draft: &RadrootsFrozenEventDraft, 32 ) -> Result<RadrootsSignedNostrEvent, RadrootsSignerError>; 33 } 34 35 #[cfg(test)] 36 mod tests { 37 use super::*; 38 use radroots_events::kinds::KIND_POST; 39 40 fn hex_64(character: char) -> String { 41 std::iter::repeat_n(character, 64).collect() 42 } 43 44 fn hex_128(character: char) -> String { 45 std::iter::repeat_n(character, 128).collect() 46 } 47 48 fn draft_for(pubkey: &str) -> RadrootsFrozenEventDraft { 49 RadrootsFrozenEventDraft::new( 50 "radroots.social.post.v1", 51 KIND_POST, 52 1_700_000_000, 53 vec![vec!["t".to_owned(), "soil".to_owned()]], 54 "hello", 55 pubkey, 56 ) 57 .expect("draft") 58 } 59 60 struct MockSigner { 61 pubkey: RadrootsPublicKey, 62 failure: Option<RadrootsSignerError>, 63 } 64 65 impl MockSigner { 66 fn new(pubkey: &str) -> Self { 67 Self { 68 pubkey: RadrootsPublicKey::parse(pubkey).expect("pubkey"), 69 failure: None, 70 } 71 } 72 73 fn failing(pubkey: &str, failure: RadrootsSignerError) -> Self { 74 Self { 75 pubkey: RadrootsPublicKey::parse(pubkey).expect("pubkey"), 76 failure: Some(failure), 77 } 78 } 79 } 80 81 impl RadrootsEventSigner for MockSigner { 82 fn pubkey(&self) -> &RadrootsPublicKey { 83 &self.pubkey 84 } 85 86 fn sign_frozen_draft( 87 &self, 88 draft: &RadrootsFrozenEventDraft, 89 ) -> Result<RadrootsSignedNostrEvent, RadrootsSignerError> { 90 if let Some(failure) = &self.failure { 91 return Err(match failure { 92 RadrootsSignerError::Unavailable => RadrootsSignerError::Unavailable, 93 RadrootsSignerError::Rejected => RadrootsSignerError::Rejected, 94 RadrootsSignerError::SigningFailed { message } => { 95 RadrootsSignerError::SigningFailed { 96 message: message.clone(), 97 } 98 } 99 }); 100 } 101 RadrootsSignedNostrEvent::new(RadrootsSignedNostrEventParts { 102 id: draft.expected_event_id.to_string(), 103 pubkey: self.pubkey.to_string(), 104 created_at: draft.created_at, 105 kind: draft.kind, 106 tags: draft.tags.clone(), 107 content: draft.content.clone(), 108 sig: hex_128('f'), 109 raw_json: "{}".to_owned(), 110 }) 111 .map_err(|error| RadrootsSignerError::SigningFailed { 112 message: error.to_string(), 113 }) 114 } 115 } 116 117 #[test] 118 fn mock_signer_reports_public_key() { 119 let pubkey = hex_64('a'); 120 let signer = MockSigner::new(pubkey.as_str()); 121 122 assert_eq!(signer.pubkey().as_str(), pubkey); 123 } 124 125 #[test] 126 fn signer_identity_validates_public_key() { 127 let pubkey = hex_64('a'); 128 let identity = RadrootsSignerIdentity::new(pubkey.as_str()).expect("identity"); 129 assert_eq!(identity.pubkey().as_str(), pubkey); 130 131 assert!(matches!( 132 RadrootsSignerIdentity::new("bad-pubkey"), 133 Err(RadrootsAuthorityError::InvalidSignerPubkey) 134 )); 135 } 136 137 #[test] 138 fn mock_signer_returns_signed_frozen_draft() { 139 let pubkey = hex_64('a'); 140 let signer = MockSigner::new(pubkey.as_str()); 141 let draft = draft_for(pubkey.as_str()); 142 143 let signed = signer.sign_frozen_draft(&draft).expect("signed"); 144 145 assert_eq!(signed.id, draft.expected_event_id); 146 assert_eq!(signed.pubkey, pubkey); 147 assert_eq!(signed.kind, KIND_POST); 148 } 149 150 #[test] 151 fn mock_signer_propagates_signing_errors() { 152 let pubkey = hex_64('a'); 153 let draft = draft_for(pubkey.as_str()); 154 155 for failure in [ 156 RadrootsSignerError::Unavailable, 157 RadrootsSignerError::Rejected, 158 RadrootsSignerError::SigningFailed { 159 message: "deterministic failure".to_owned(), 160 }, 161 ] { 162 let signer = MockSigner::failing(pubkey.as_str(), failure); 163 let err = signer.sign_frozen_draft(&draft).expect_err("failure"); 164 165 match err { 166 RadrootsSignerError::Unavailable => {} 167 RadrootsSignerError::Rejected => {} 168 RadrootsSignerError::SigningFailed { message } => { 169 assert_eq!(message, "deterministic failure"); 170 } 171 } 172 } 173 } 174 175 #[test] 176 fn mock_signer_maps_invalid_signed_event_parts() { 177 let pubkey = hex_64('a'); 178 let signer = MockSigner::new(pubkey.as_str()); 179 let mut draft = draft_for(pubkey.as_str()); 180 draft.expected_event_id = "bad-id".to_string(); 181 182 let err = signer.sign_frozen_draft(&draft).expect_err("failure"); 183 184 assert!(matches!(err, RadrootsSignerError::SigningFailed { .. })); 185 } 186 }