delegation.rs (13765B)
1 //! Event parsing and validation 2 use crate::error::Error; 3 use crate::error::Result; 4 use crate::event::Event; 5 use bitcoin_hashes::{sha256, Hash}; 6 use lazy_static::lazy_static; 7 use regex::Regex; 8 use secp256k1::{schnorr, Secp256k1, VerifyOnly, XOnlyPublicKey}; 9 use serde::{Deserialize, Serialize}; 10 use std::str::FromStr; 11 use tracing::{debug, info}; 12 13 // This handles everything related to delegation, in particular the 14 // condition/rune parsing and logic. 15 16 // Conditions are poorly specified, so we will implement the minimum 17 // necessary for now. 18 19 // fields MUST be either "kind" or "created_at". 20 // operators supported are ">", "<", "=", "!". 21 // no operations on 'content' are supported. 22 23 // this allows constraints for: 24 // valid date ranges (valid from X->Y dates). 25 // specific kinds (publish kind=1,5) 26 // kind ranges (publish ephemeral events, kind>19999&kind<30001) 27 28 // for more complex scenarios (allow delegatee to publish ephemeral 29 // AND replacement events), it may be necessary to generate and use 30 // different condition strings, since we do not support grouping or 31 // "OR" logic. 32 33 lazy_static! { 34 /// Secp256k1 verification instance. 35 pub static ref SECP: Secp256k1<VerifyOnly> = Secp256k1::verification_only(); 36 } 37 38 #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] 39 pub enum Field { 40 Kind, 41 CreatedAt, 42 } 43 44 impl FromStr for Field { 45 type Err = Error; 46 fn from_str(value: &str) -> Result<Self, Self::Err> { 47 if value == "kind" { 48 Ok(Field::Kind) 49 } else if value == "created_at" { 50 Ok(Field::CreatedAt) 51 } else { 52 Err(Error::DelegationParseError) 53 } 54 } 55 } 56 57 #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] 58 pub enum Operator { 59 LessThan, 60 GreaterThan, 61 Equals, 62 NotEquals, 63 } 64 impl FromStr for Operator { 65 type Err = Error; 66 fn from_str(value: &str) -> Result<Self, Self::Err> { 67 if value == "<" { 68 Ok(Operator::LessThan) 69 } else if value == ">" { 70 Ok(Operator::GreaterThan) 71 } else if value == "=" { 72 Ok(Operator::Equals) 73 } else if value == "!" { 74 Ok(Operator::NotEquals) 75 } else { 76 Err(Error::DelegationParseError) 77 } 78 } 79 } 80 81 #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] 82 pub struct ConditionQuery { 83 pub(crate) conditions: Vec<Condition>, 84 } 85 86 impl ConditionQuery { 87 pub fn allows_event(&self, event: &Event) -> bool { 88 // check each condition, to ensure that the event complies 89 // with the restriction. 90 for c in &self.conditions { 91 if !c.allows_event(event) { 92 // any failing conditions invalidates the delegation 93 // on this event 94 return false; 95 } 96 } 97 // delegation was permitted unconditionally, or all conditions 98 // were true 99 true 100 } 101 } 102 103 // Verify that the delegator approved the delegation; return a ConditionQuery if so. 104 pub fn validate_delegation( 105 delegator: &str, 106 delegatee: &str, 107 cond_query: &str, 108 sigstr: &str, 109 ) -> Option<ConditionQuery> { 110 // form the token 111 let tok = format!("nostr:delegation:{}:{}", delegatee, cond_query); 112 // form SHA256 hash 113 let digest: sha256::Hash = sha256::Hash::hash(tok.as_bytes()); 114 let sig = schnorr::Signature::from_str(sigstr).unwrap(); 115 if let Ok(msg) = secp256k1::Message::from_slice(digest.as_ref()) { 116 if let Ok(pubkey) = XOnlyPublicKey::from_str(delegator) { 117 let verify = SECP.verify_schnorr(&sig, &msg, &pubkey); 118 if verify.is_ok() { 119 // return the parsed condition query 120 cond_query.parse::<ConditionQuery>().ok() 121 } else { 122 debug!("client sent an delegation signature that did not validate"); 123 None 124 } 125 } else { 126 debug!("client sent malformed delegation pubkey"); 127 None 128 } 129 } else { 130 info!("error converting delegation digest to secp256k1 message"); 131 None 132 } 133 } 134 135 /// Parsed delegation condition 136 /// see https://github.com/nostr-protocol/nips/pull/28#pullrequestreview-1084903800 137 /// An example complex condition would be: kind=1,2,3&created_at<1665265999 138 #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] 139 pub struct Condition { 140 pub(crate) field: Field, 141 pub(crate) operator: Operator, 142 pub(crate) values: Vec<u64>, 143 } 144 145 impl Condition { 146 /// Check if this condition allows the given event to be delegated 147 pub fn allows_event(&self, event: &Event) -> bool { 148 // determine what the right-hand side of the operator is 149 let resolved_field = match &self.field { 150 Field::Kind => event.kind, 151 Field::CreatedAt => event.created_at, 152 }; 153 match &self.operator { 154 Operator::LessThan => { 155 // the less-than operator is only valid for single values. 156 if self.values.len() == 1 { 157 if let Some(v) = self.values.first() { 158 return resolved_field < *v; 159 } 160 } 161 } 162 Operator::GreaterThan => { 163 // the greater-than operator is only valid for single values. 164 if self.values.len() == 1 { 165 if let Some(v) = self.values.first() { 166 return resolved_field > *v; 167 } 168 } 169 } 170 Operator::Equals => { 171 // equals is interpreted as "must be equal to at least one provided value" 172 return self.values.iter().any(|&x| resolved_field == x); 173 } 174 Operator::NotEquals => { 175 // not-equals is interpreted as "must not be equal to any provided value" 176 // this is the one case where an empty list of values could be allowed; even though it is a pointless restriction. 177 return self.values.iter().all(|&x| resolved_field != x); 178 } 179 } 180 false 181 } 182 } 183 184 fn str_to_condition(cs: &str) -> Option<Condition> { 185 // a condition is a string (alphanum+underscore), an operator (<>=!), and values (num+comma) 186 lazy_static! { 187 static ref RE: Regex = Regex::new("([[:word:]]+)([<>=!]+)([,[[:digit:]]]*)").unwrap(); 188 } 189 // match against the regex 190 let caps = RE.captures(cs)?; 191 let field = caps.get(1)?.as_str().parse::<Field>().ok()?; 192 let operator = caps.get(2)?.as_str().parse::<Operator>().ok()?; 193 // values are just comma separated numbers, but all must be parsed 194 let rawvals = caps.get(3)?.as_str(); 195 let values = rawvals 196 .split_terminator(',') 197 .map(|n| n.parse::<u64>().ok()) 198 .collect::<Option<Vec<_>>>()?; 199 // convert field string into Field 200 Some(Condition { 201 field, 202 operator, 203 values, 204 }) 205 } 206 207 /// Parse a condition query from a string slice 208 impl FromStr for ConditionQuery { 209 type Err = Error; 210 fn from_str(value: &str) -> Result<Self, Self::Err> { 211 // split the string with '&' 212 let mut conditions = vec![]; 213 let condstrs = value.split_terminator('&'); 214 // parse each individual condition 215 for c in condstrs { 216 conditions.push(str_to_condition(c).ok_or(Error::DelegationParseError)?); 217 } 218 Ok(ConditionQuery { conditions }) 219 } 220 } 221 222 #[cfg(test)] 223 mod tests { 224 use super::*; 225 226 // parse condition strings 227 #[test] 228 fn parse_empty() -> Result<()> { 229 // given an empty condition query, produce an empty vector 230 let empty_cq = ConditionQuery { conditions: vec![] }; 231 let parsed = "".parse::<ConditionQuery>()?; 232 assert_eq!(parsed, empty_cq); 233 Ok(()) 234 } 235 236 // parse field 'kind' 237 #[test] 238 fn test_kind_field_parse() -> Result<()> { 239 let field = "kind".parse::<Field>()?; 240 assert_eq!(field, Field::Kind); 241 Ok(()) 242 } 243 // parse field 'created_at' 244 #[test] 245 fn test_created_at_field_parse() -> Result<()> { 246 let field = "created_at".parse::<Field>()?; 247 assert_eq!(field, Field::CreatedAt); 248 Ok(()) 249 } 250 // parse unknown field 251 #[test] 252 fn unknown_field_parse() { 253 let field = "unk".parse::<Field>(); 254 assert!(field.is_err()); 255 } 256 257 // parse a full conditional query with an empty array 258 #[test] 259 fn parse_kind_equals_empty() -> Result<()> { 260 // given an empty condition query, produce an empty vector 261 let kind_cq = ConditionQuery { 262 conditions: vec![Condition { 263 field: Field::Kind, 264 operator: Operator::Equals, 265 values: vec![], 266 }], 267 }; 268 let parsed = "kind=".parse::<ConditionQuery>()?; 269 assert_eq!(parsed, kind_cq); 270 Ok(()) 271 } 272 // parse a full conditional query with a single value 273 #[test] 274 fn parse_kind_equals_singleval() -> Result<()> { 275 // given an empty condition query, produce an empty vector 276 let kind_cq = ConditionQuery { 277 conditions: vec![Condition { 278 field: Field::Kind, 279 operator: Operator::Equals, 280 values: vec![1], 281 }], 282 }; 283 let parsed = "kind=1".parse::<ConditionQuery>()?; 284 assert_eq!(parsed, kind_cq); 285 Ok(()) 286 } 287 // parse a full conditional query with multiple values 288 #[test] 289 fn parse_kind_equals_multival() -> Result<()> { 290 // given an empty condition query, produce an empty vector 291 let kind_cq = ConditionQuery { 292 conditions: vec![Condition { 293 field: Field::Kind, 294 operator: Operator::Equals, 295 values: vec![1, 2, 4], 296 }], 297 }; 298 let parsed = "kind=1,2,4".parse::<ConditionQuery>()?; 299 assert_eq!(parsed, kind_cq); 300 Ok(()) 301 } 302 // parse multiple conditions 303 #[test] 304 fn parse_multi_conditions() -> Result<()> { 305 // given an empty condition query, produce an empty vector 306 let cq = ConditionQuery { 307 conditions: vec![ 308 Condition { 309 field: Field::Kind, 310 operator: Operator::GreaterThan, 311 values: vec![10000], 312 }, 313 Condition { 314 field: Field::Kind, 315 operator: Operator::LessThan, 316 values: vec![20000], 317 }, 318 Condition { 319 field: Field::Kind, 320 operator: Operator::NotEquals, 321 values: vec![10001], 322 }, 323 Condition { 324 field: Field::CreatedAt, 325 operator: Operator::LessThan, 326 values: vec![1665867123], 327 }, 328 ], 329 }; 330 let parsed = 331 "kind>10000&kind<20000&kind!10001&created_at<1665867123".parse::<ConditionQuery>()?; 332 assert_eq!(parsed, cq); 333 Ok(()) 334 } 335 fn simple_event() -> Event { 336 Event { 337 id: "0".to_owned(), 338 pubkey: "0".to_owned(), 339 delegated_by: None, 340 created_at: 0, 341 kind: 0, 342 tags: vec![], 343 content: "".to_owned(), 344 sig: "0".to_owned(), 345 tagidx: None, 346 } 347 } 348 // Check for condition logic on event w/ empty values 349 #[test] 350 fn condition_with_empty_values() { 351 let mut c = Condition { 352 field: Field::Kind, 353 operator: Operator::GreaterThan, 354 values: vec![], 355 }; 356 let e = simple_event(); 357 assert!(!c.allows_event(&e)); 358 c.operator = Operator::LessThan; 359 assert!(!c.allows_event(&e)); 360 c.operator = Operator::Equals; 361 assert!(!c.allows_event(&e)); 362 // Not Equals applied to an empty list *is* allowed 363 // (pointless, but logically valid). 364 c.operator = Operator::NotEquals; 365 assert!(c.allows_event(&e)); 366 } 367 368 // Check for condition logic on event w/ single value 369 #[test] 370 fn condition_kind_gt_event_single() { 371 let c = Condition { 372 field: Field::Kind, 373 operator: Operator::GreaterThan, 374 values: vec![10], 375 }; 376 let mut e = simple_event(); 377 // kind is not greater than 10, not allowed 378 e.kind = 1; 379 assert!(!c.allows_event(&e)); 380 // kind is greater than 10, allowed 381 e.kind = 100; 382 assert!(c.allows_event(&e)); 383 // kind is 10, not allowed 384 e.kind = 10; 385 assert!(!c.allows_event(&e)); 386 } 387 // Check for condition logic on event w/ multi values 388 #[test] 389 fn condition_with_multi_values() { 390 let mut c = Condition { 391 field: Field::Kind, 392 operator: Operator::Equals, 393 values: vec![0, 10, 20], 394 }; 395 let mut e = simple_event(); 396 // Allow if event kind is in list for Equals 397 e.kind = 10; 398 assert!(c.allows_event(&e)); 399 // Deny if event kind is not in list for Equals 400 e.kind = 11; 401 assert!(!c.allows_event(&e)); 402 // Deny if event kind is in list for NotEquals 403 e.kind = 10; 404 c.operator = Operator::NotEquals; 405 assert!(!c.allows_event(&e)); 406 // Allow if event kind is not in list for NotEquals 407 e.kind = 99; 408 c.operator = Operator::NotEquals; 409 assert!(c.allows_event(&e)); 410 // Always deny if GreaterThan/LessThan for a list 411 c.operator = Operator::LessThan; 412 assert!(!c.allows_event(&e)); 413 c.operator = Operator::GreaterThan; 414 assert!(!c.allows_event(&e)); 415 } 416 }