nostr-rs-relay

My dev fork of nostr-rs-relay
git clone git://jb55.com/nostr-rs-relay
Log | Files | Refs | README | LICENSE

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 }