event.rs (17196B)
1 //! Event parsing and validation 2 use crate::delegation::validate_delegation; 3 use crate::error::Error::*; 4 use crate::error::Result; 5 use crate::nip05; 6 use crate::utils::unix_time; 7 use bitcoin_hashes::{sha256, Hash}; 8 use lazy_static::lazy_static; 9 use secp256k1::{schnorr, Secp256k1, VerifyOnly, XOnlyPublicKey}; 10 use serde::{Deserialize, Deserializer, Serialize}; 11 use serde_json::value::Value; 12 use serde_json::Number; 13 use std::collections::HashMap; 14 use std::collections::HashSet; 15 use std::str::FromStr; 16 use tracing::{debug, info}; 17 18 lazy_static! { 19 /// Secp256k1 verification instance. 20 pub static ref SECP: Secp256k1<VerifyOnly> = Secp256k1::verification_only(); 21 } 22 23 /// Event command in network format. 24 #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] 25 pub struct EventCmd { 26 cmd: String, // expecting static "EVENT" 27 event: Event, 28 } 29 30 /// Parsed nostr event. 31 #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] 32 pub struct Event { 33 pub id: String, 34 pub(crate) pubkey: String, 35 #[serde(skip)] 36 pub(crate) delegated_by: Option<String>, 37 pub(crate) created_at: u64, 38 pub(crate) kind: u64, 39 #[serde(deserialize_with = "tag_from_string")] 40 // NOTE: array-of-arrays may need to be more general than a string container 41 pub(crate) tags: Vec<Vec<String>>, 42 pub(crate) content: String, 43 pub(crate) sig: String, 44 // Optimization for tag search, built on demand. 45 #[serde(skip)] 46 pub(crate) tagidx: Option<HashMap<char, HashSet<String>>>, 47 } 48 49 /// Simple tag type for array of array of strings. 50 type Tag = Vec<Vec<String>>; 51 52 /// Deserializer that ensures we always have a [`Tag`]. 53 fn tag_from_string<'de, D>(deserializer: D) -> Result<Tag, D::Error> 54 where 55 D: Deserializer<'de>, 56 { 57 let opt = Option::deserialize(deserializer)?; 58 Ok(opt.unwrap_or_default()) 59 } 60 61 /// Attempt to form a single-char tag name. 62 pub fn single_char_tagname(tagname: &str) -> Option<char> { 63 // We return the tag character if and only if the tagname consists 64 // of a single char. 65 let mut tagnamechars = tagname.chars(); 66 let firstchar = tagnamechars.next(); 67 match firstchar { 68 Some(_) => { 69 // check second char 70 if tagnamechars.next().is_none() { 71 firstchar 72 } else { 73 None 74 } 75 } 76 None => None, 77 } 78 } 79 80 /// Convert network event to parsed/validated event. 81 impl From<EventCmd> for Result<Event> { 82 fn from(ec: EventCmd) -> Result<Event> { 83 // ensure command is correct 84 if ec.cmd != "EVENT" { 85 Err(CommandUnknownError) 86 } else if ec.event.is_valid() { 87 let mut e = ec.event; 88 e.build_index(); 89 e.update_delegation(); 90 Ok(e) 91 } else { 92 Err(EventInvalid) 93 } 94 } 95 } 96 97 impl Event { 98 pub fn is_kind_metadata(&self) -> bool { 99 self.kind == 0 100 } 101 102 /// Pull a NIP-05 Name out of the event, if one exists 103 pub fn get_nip05_addr(&self) -> Option<nip05::Nip05Name> { 104 if self.is_kind_metadata() { 105 // very quick check if we should attempt to parse this json 106 if self.content.contains("\"nip05\"") { 107 // Parse into JSON 108 let md_parsed: Value = serde_json::from_str(&self.content).ok()?; 109 let md_map = md_parsed.as_object()?; 110 let nip05_str = md_map.get("nip05")?.as_str()?; 111 return nip05::Nip05Name::try_from(nip05_str).ok(); 112 } 113 } 114 None 115 } 116 117 // is this event delegated (properly)? 118 // does the signature match, and are conditions valid? 119 // if so, return an alternate author for the event 120 pub fn delegated_author(&self) -> Option<String> { 121 // is there a delegation tag? 122 let delegation_tag: Vec<String> = self 123 .tags 124 .iter() 125 .filter(|x| x.len() == 4) 126 .filter(|x| x.get(0).unwrap() == "delegation") 127 .take(1) 128 .next()? 129 .to_vec(); // get first tag 130 131 //let delegation_tag = self.tag_values_by_name("delegation"); 132 // delegation tags should have exactly 3 elements after the name (pubkey, condition, sig) 133 // the event is signed by the delagatee 134 let delegatee = &self.pubkey; 135 // the delegation tag references the claimed delagator 136 let delegator: &str = delegation_tag.get(1)?; 137 let querystr: &str = delegation_tag.get(2)?; 138 let sig: &str = delegation_tag.get(3)?; 139 140 // attempt to get a condition query; this requires the delegation to have a valid signature. 141 if let Some(cond_query) = validate_delegation(delegator, delegatee, querystr, sig) { 142 // The signature was valid, now we ensure the delegation 143 // condition is valid for this event: 144 if cond_query.allows_event(self) { 145 // since this is allowed, we will provide the delegatee 146 Some(delegator.into()) 147 } else { 148 debug!("an event failed to satisfy delegation conditions"); 149 None 150 } 151 } else { 152 debug!("event had had invalid delegation signature"); 153 None 154 } 155 } 156 157 /// Update delegation status 158 fn update_delegation(&mut self) { 159 self.delegated_by = self.delegated_author(); 160 } 161 /// Build an event tag index 162 fn build_index(&mut self) { 163 // if there are no tags; just leave the index as None 164 if self.tags.is_empty() { 165 return; 166 } 167 // otherwise, build an index 168 let mut idx: HashMap<char, HashSet<String>> = HashMap::new(); 169 // iterate over tags that have at least 2 elements 170 for t in self.tags.iter().filter(|x| x.len() > 1) { 171 let tagname = t.get(0).unwrap(); 172 let tagnamechar_opt = single_char_tagname(tagname); 173 if tagnamechar_opt.is_none() { 174 continue; 175 } 176 let tagnamechar = tagnamechar_opt.unwrap(); 177 let tagval = t.get(1).unwrap(); 178 // ensure a vector exists for this tag 179 idx.entry(tagnamechar).or_insert_with(HashSet::new); 180 // get the tag vec and insert entry 181 let idx_tag_vec = idx.get_mut(&tagnamechar).expect("could not get tag vector"); 182 idx_tag_vec.insert(tagval.clone()); 183 } 184 // save the tag structure 185 self.tagidx = Some(idx); 186 } 187 188 /// Create a short event identifier, suitable for logging. 189 pub fn get_event_id_prefix(&self) -> String { 190 self.id.chars().take(8).collect() 191 } 192 pub fn get_author_prefix(&self) -> String { 193 self.pubkey.chars().take(8).collect() 194 } 195 196 /// Retrieve tag initial values across all tags matching the name 197 pub fn tag_values_by_name(&self, tag_name: &str) -> Vec<String> { 198 self.tags 199 .iter() 200 .filter(|x| x.len() > 1) 201 .filter(|x| x.get(0).unwrap() == tag_name) 202 .map(|x| x.get(1).unwrap().to_owned()) 203 .collect() 204 } 205 206 pub fn is_valid_timestamp(&self, reject_future_seconds: Option<usize>) -> bool { 207 if let Some(allowable_future) = reject_future_seconds { 208 let curr_time = unix_time(); 209 // calculate difference, plus how far future we allow 210 if curr_time + (allowable_future as u64) < self.created_at { 211 let delta = self.created_at - curr_time; 212 debug!( 213 "event is too far in the future ({} seconds), rejecting", 214 delta 215 ); 216 return false; 217 } 218 } 219 true 220 } 221 222 /// Check if this event has a valid signature. 223 fn is_valid(&self) -> bool { 224 // TODO: return a Result with a reason for invalid events 225 // validation is performed by: 226 // * parsing JSON string into event fields 227 // * create an array: 228 // ** [0, pubkey-hex-string, created-at-num, kind-num, tags-array-of-arrays, content-string] 229 // * serialize with no spaces/newlines 230 let c_opt = self.to_canonical(); 231 if c_opt.is_none() { 232 debug!("event could not be canonicalized"); 233 return false; 234 } 235 let c = c_opt.unwrap(); 236 // * compute the sha256sum. 237 let digest: sha256::Hash = sha256::Hash::hash(c.as_bytes()); 238 let hex_digest = format!("{:x}", digest); 239 // * ensure the id matches the computed sha256sum. 240 if self.id != hex_digest { 241 debug!("event id does not match digest"); 242 return false; 243 } 244 // * validate the message digest (sig) using the pubkey & computed sha256 message hash. 245 let sig = schnorr::Signature::from_str(&self.sig).unwrap(); 246 if let Ok(msg) = secp256k1::Message::from_slice(digest.as_ref()) { 247 if let Ok(pubkey) = XOnlyPublicKey::from_str(&self.pubkey) { 248 let verify = SECP.verify_schnorr(&sig, &msg, &pubkey); 249 matches!(verify, Ok(())) 250 } else { 251 debug!("client sent malformed pubkey"); 252 false 253 } 254 } else { 255 info!("error converting digest to secp256k1 message"); 256 false 257 } 258 } 259 260 /// Convert event to canonical representation for signing. 261 fn to_canonical(&self) -> Option<String> { 262 // create a JsonValue for each event element 263 let mut c: Vec<Value> = vec![]; 264 // id must be set to 0 265 let id = Number::from(0_u64); 266 c.push(serde_json::Value::Number(id)); 267 // public key 268 c.push(Value::String(self.pubkey.to_owned())); 269 // creation time 270 let created_at = Number::from(self.created_at); 271 c.push(serde_json::Value::Number(created_at)); 272 // kind 273 let kind = Number::from(self.kind); 274 c.push(serde_json::Value::Number(kind)); 275 // tags 276 c.push(self.tags_to_canonical()); 277 // content 278 c.push(Value::String(self.content.to_owned())); 279 serde_json::to_string(&Value::Array(c)).ok() 280 } 281 282 /// Convert tags to a canonical form for signing. 283 fn tags_to_canonical(&self) -> Value { 284 let mut tags = Vec::<Value>::new(); 285 // iterate over self tags, 286 for t in self.tags.iter() { 287 // each tag is a vec of strings 288 let mut a = Vec::<Value>::new(); 289 for v in t.iter() { 290 a.push(serde_json::Value::String(v.to_owned())); 291 } 292 tags.push(serde_json::Value::Array(a)); 293 } 294 serde_json::Value::Array(tags) 295 } 296 297 /// Determine if the given tag and value set intersect with tags in this event. 298 pub fn generic_tag_val_intersect(&self, tagname: char, check: &HashSet<String>) -> bool { 299 match &self.tagidx { 300 // check if this is indexable tagname 301 Some(idx) => match idx.get(&tagname) { 302 Some(valset) => { 303 let common = valset.intersection(check); 304 common.count() > 0 305 } 306 None => false, 307 }, 308 None => false, 309 } 310 } 311 } 312 313 #[cfg(test)] 314 mod tests { 315 use super::*; 316 fn simple_event() -> Event { 317 Event { 318 id: "0".to_owned(), 319 pubkey: "0".to_owned(), 320 delegated_by: None, 321 created_at: 0, 322 kind: 0, 323 tags: vec![], 324 content: "".to_owned(), 325 sig: "0".to_owned(), 326 tagidx: None, 327 } 328 } 329 330 #[test] 331 fn event_creation() { 332 // create an event 333 let event = simple_event(); 334 assert_eq!(event.id, "0"); 335 } 336 337 #[test] 338 fn event_serialize() -> Result<()> { 339 // serialize an event to JSON string 340 let event = simple_event(); 341 let j = serde_json::to_string(&event)?; 342 assert_eq!(j, "{\"id\":\"0\",\"pubkey\":\"0\",\"created_at\":0,\"kind\":0,\"tags\":[],\"content\":\"\",\"sig\":\"0\"}"); 343 Ok(()) 344 } 345 346 #[test] 347 fn empty_event_tag_match() { 348 let event = simple_event(); 349 assert!(!event 350 .generic_tag_val_intersect('e', &HashSet::from(["foo".to_owned(), "bar".to_owned()]))); 351 } 352 353 #[test] 354 fn single_event_tag_match() { 355 let mut event = simple_event(); 356 event.tags = vec![vec!["e".to_owned(), "foo".to_owned()]]; 357 event.build_index(); 358 assert_eq!( 359 event.generic_tag_val_intersect( 360 'e', 361 &HashSet::from(["foo".to_owned(), "bar".to_owned()]) 362 ), 363 true 364 ); 365 } 366 367 #[test] 368 fn event_tags_serialize() -> Result<()> { 369 // serialize an event with tags to JSON string 370 let mut event = simple_event(); 371 event.tags = vec![ 372 vec![ 373 "e".to_owned(), 374 "xxxx".to_owned(), 375 "wss://example.com".to_owned(), 376 ], 377 vec![ 378 "p".to_owned(), 379 "yyyyy".to_owned(), 380 "wss://example.com:3033".to_owned(), 381 ], 382 ]; 383 let j = serde_json::to_string(&event)?; 384 assert_eq!(j, "{\"id\":\"0\",\"pubkey\":\"0\",\"created_at\":0,\"kind\":0,\"tags\":[[\"e\",\"xxxx\",\"wss://example.com\"],[\"p\",\"yyyyy\",\"wss://example.com:3033\"]],\"content\":\"\",\"sig\":\"0\"}"); 385 Ok(()) 386 } 387 388 #[test] 389 fn event_deserialize() -> Result<()> { 390 let raw_json = r#"{"id":"1384757da583e6129ce831c3d7afc775a33a090578f888dd0d010328ad047d0c","pubkey":"bbbd9711d357df4f4e498841fd796535c95c8e751fa35355008a911c41265fca","created_at":1612650459,"kind":1,"tags":null,"content":"hello world","sig":"59d0cc47ab566e81f72fe5f430bcfb9b3c688cb0093d1e6daa49201c00d28ecc3651468b7938642869ed98c0f1b262998e49a05a6ed056c0d92b193f4e93bc21"}"#; 391 let e: Event = serde_json::from_str(raw_json)?; 392 assert_eq!(e.kind, 1); 393 assert_eq!(e.tags.len(), 0); 394 Ok(()) 395 } 396 397 #[test] 398 fn event_canonical() { 399 let e = Event { 400 id: "999".to_owned(), 401 pubkey: "012345".to_owned(), 402 delegated_by: None, 403 created_at: 501234, 404 kind: 1, 405 tags: vec![], 406 content: "this is a test".to_owned(), 407 sig: "abcde".to_owned(), 408 tagidx: None, 409 }; 410 let c = e.to_canonical(); 411 let expected = Some(r#"[0,"012345",501234,1,[],"this is a test"]"#.to_owned()); 412 assert_eq!(c, expected); 413 } 414 415 #[test] 416 fn event_tag_select() { 417 let e = Event { 418 id: "999".to_owned(), 419 pubkey: "012345".to_owned(), 420 delegated_by: None, 421 created_at: 501234, 422 kind: 1, 423 tags: vec![ 424 vec!["j".to_owned(), "abc".to_owned()], 425 vec!["e".to_owned(), "foo".to_owned()], 426 vec!["e".to_owned(), "bar".to_owned()], 427 vec!["e".to_owned(), "baz".to_owned()], 428 vec![ 429 "p".to_owned(), 430 "aaaa".to_owned(), 431 "ws://example.com".to_owned(), 432 ], 433 ], 434 content: "this is a test".to_owned(), 435 sig: "abcde".to_owned(), 436 tagidx: None, 437 }; 438 let v = e.tag_values_by_name("e"); 439 assert_eq!(v, vec!["foo", "bar", "baz"]); 440 } 441 442 #[test] 443 fn event_no_tag_select() { 444 let e = Event { 445 id: "999".to_owned(), 446 pubkey: "012345".to_owned(), 447 delegated_by: None, 448 created_at: 501234, 449 kind: 1, 450 tags: vec![ 451 vec!["j".to_owned(), "abc".to_owned()], 452 vec!["e".to_owned(), "foo".to_owned()], 453 vec!["e".to_owned(), "baz".to_owned()], 454 vec![ 455 "p".to_owned(), 456 "aaaa".to_owned(), 457 "ws://example.com".to_owned(), 458 ], 459 ], 460 content: "this is a test".to_owned(), 461 sig: "abcde".to_owned(), 462 tagidx: None, 463 }; 464 let v = e.tag_values_by_name("x"); 465 // asking for tags that don't exist just returns zero-length vector 466 assert_eq!(v.len(), 0); 467 } 468 469 #[test] 470 fn event_canonical_with_tags() { 471 let e = Event { 472 id: "999".to_owned(), 473 pubkey: "012345".to_owned(), 474 delegated_by: None, 475 created_at: 501234, 476 kind: 1, 477 tags: vec![ 478 vec!["#e".to_owned(), "aoeu".to_owned()], 479 vec![ 480 "#p".to_owned(), 481 "aaaa".to_owned(), 482 "ws://example.com".to_owned(), 483 ], 484 ], 485 content: "this is a test".to_owned(), 486 sig: "abcde".to_owned(), 487 tagidx: None, 488 }; 489 let c = e.to_canonical(); 490 let expected_json = r###"[0,"012345",501234,1,[["#e","aoeu"],["#p","aaaa","ws://example.com"]],"this is a test"]"###; 491 let expected = Some(expected_json.to_owned()); 492 assert_eq!(c, expected); 493 } 494 }