lib.rs (98806B)
1 #![forbid(unsafe_code)] 2 3 use core::fmt; 4 use core::str::FromStr; 5 use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; 6 use std::collections::BTreeMap; 7 8 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 9 pub struct EventId(String); 10 11 impl EventId { 12 pub const HEX_LENGTH: usize = 64; 13 14 pub fn new(value: &str) -> Result<Self, String> { 15 require_lowercase_hex("event id", value, Self::HEX_LENGTH)?; 16 Ok(Self(value.to_owned())) 17 } 18 19 pub fn as_str(&self) -> &str { 20 &self.0 21 } 22 } 23 24 impl fmt::Display for EventId { 25 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 26 formatter.write_str(self.as_str()) 27 } 28 } 29 30 impl FromStr for EventId { 31 type Err = String; 32 33 fn from_str(value: &str) -> Result<Self, Self::Err> { 34 Self::new(value) 35 } 36 } 37 38 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 39 pub struct PublicKeyHex(String); 40 41 impl PublicKeyHex { 42 pub const HEX_LENGTH: usize = 64; 43 44 pub fn new(value: &str) -> Result<Self, String> { 45 require_lowercase_hex("public key", value, Self::HEX_LENGTH)?; 46 Ok(Self(value.to_owned())) 47 } 48 49 pub fn as_str(&self) -> &str { 50 &self.0 51 } 52 } 53 54 impl fmt::Display for PublicKeyHex { 55 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 56 formatter.write_str(self.as_str()) 57 } 58 } 59 60 impl FromStr for PublicKeyHex { 61 type Err = String; 62 63 fn from_str(value: &str) -> Result<Self, Self::Err> { 64 Self::new(value) 65 } 66 } 67 68 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 69 pub struct SignatureHex(String); 70 71 impl SignatureHex { 72 pub const HEX_LENGTH: usize = 128; 73 74 pub fn new(value: &str) -> Result<Self, String> { 75 require_lowercase_hex("signature", value, Self::HEX_LENGTH)?; 76 Ok(Self(value.to_owned())) 77 } 78 79 pub fn as_str(&self) -> &str { 80 &self.0 81 } 82 } 83 84 impl fmt::Display for SignatureHex { 85 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 86 formatter.write_str(self.as_str()) 87 } 88 } 89 90 impl FromStr for SignatureHex { 91 type Err = String; 92 93 fn from_str(value: &str) -> Result<Self, Self::Err> { 94 Self::new(value) 95 } 96 } 97 98 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 99 pub struct SubscriptionId(String); 100 101 impl SubscriptionId { 102 pub const MAX_LENGTH: usize = 64; 103 104 pub fn new(value: &str) -> Result<Self, String> { 105 let actual = value.chars().count(); 106 if actual == 0 { 107 return Err(empty_error("subscription id")); 108 } 109 if actual > Self::MAX_LENGTH { 110 return Err(too_long_error("subscription id", Self::MAX_LENGTH, actual)); 111 } 112 Ok(Self(value.to_owned())) 113 } 114 115 pub fn as_str(&self) -> &str { 116 &self.0 117 } 118 } 119 120 impl fmt::Display for SubscriptionId { 121 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 122 formatter.write_str(self.as_str()) 123 } 124 } 125 126 impl FromStr for SubscriptionId { 127 type Err = String; 128 129 fn from_str(value: &str) -> Result<Self, Self::Err> { 130 Self::new(value) 131 } 132 } 133 134 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 135 pub struct UnixTimestamp(u64); 136 137 impl UnixTimestamp { 138 pub fn new(value: u64) -> Self { 139 Self(value) 140 } 141 142 pub fn as_u64(self) -> u64 { 143 self.0 144 } 145 } 146 147 impl fmt::Display for UnixTimestamp { 148 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 149 write!(formatter, "{}", self.0) 150 } 151 } 152 153 impl From<u64> for UnixTimestamp { 154 fn from(value: u64) -> Self { 155 Self::new(value) 156 } 157 } 158 159 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 160 pub struct Kind(u32); 161 162 impl Kind { 163 pub fn new(value: u64) -> Result<Self, String> { 164 let value = u32::try_from(value).map_err(|_| kind_out_of_range_error(value))?; 165 Ok(Self(value)) 166 } 167 168 pub fn as_u32(self) -> u32 { 169 self.0 170 } 171 172 pub fn class(self) -> KindClass { 173 match self.0 { 174 0 | 3 | 10_000..=19_999 => KindClass::Replaceable, 175 20_000..=29_999 => KindClass::Ephemeral, 176 30_000..=39_999 => KindClass::Addressable, 177 _ => KindClass::Regular, 178 } 179 } 180 181 pub fn is_regular(self) -> bool { 182 self.class() == KindClass::Regular 183 } 184 185 pub fn is_replaceable(self) -> bool { 186 self.class() == KindClass::Replaceable 187 } 188 189 pub fn is_ephemeral(self) -> bool { 190 self.class() == KindClass::Ephemeral 191 } 192 193 pub fn is_addressable(self) -> bool { 194 self.class() == KindClass::Addressable 195 } 196 } 197 198 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 199 pub enum KindClass { 200 Regular, 201 Replaceable, 202 Ephemeral, 203 Addressable, 204 } 205 206 impl fmt::Display for Kind { 207 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 208 write!(formatter, "{}", self.0) 209 } 210 } 211 212 impl TryFrom<u64> for Kind { 213 type Error = String; 214 215 fn try_from(value: u64) -> Result<Self, Self::Error> { 216 Self::new(value) 217 } 218 } 219 220 #[derive(Debug, Clone, PartialEq, Eq, Hash)] 221 pub struct Tag { 222 values: Vec<String>, 223 } 224 225 impl Tag { 226 pub fn new(values: Vec<String>) -> Result<Self, String> { 227 let Some(name) = values.first() else { 228 return Err(empty_error("tag")); 229 }; 230 TagName::new(name)?; 231 Ok(Self { values }) 232 } 233 234 pub fn from_parts(name: &str, values: &[&str]) -> Result<Self, String> { 235 let values = core::iter::once(name) 236 .chain(values.iter().copied()) 237 .map(str::to_owned) 238 .collect(); 239 Self::new(values) 240 } 241 242 pub fn name(&self) -> TagName { 243 TagName(self.values[0].clone()) 244 } 245 246 pub fn value(&self) -> Option<TagValue> { 247 self.values.get(1).map(|value| TagValue(value.clone())) 248 } 249 250 pub fn values(&self) -> &[String] { 251 &self.values 252 } 253 254 pub fn indexed_pair(&self) -> Option<(&str, &str)> { 255 let name = self.values[0].as_str(); 256 if !TagName::is_indexable_name(name) { 257 return None; 258 } 259 self.values.get(1).map(|value| (name, value.as_str())) 260 } 261 } 262 263 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 264 pub struct TagName(String); 265 266 impl TagName { 267 pub fn new(value: &str) -> Result<Self, String> { 268 if value.is_empty() { 269 return Err(empty_error("tag name")); 270 } 271 Ok(Self(value.to_owned())) 272 } 273 274 pub fn as_str(&self) -> &str { 275 &self.0 276 } 277 278 pub fn is_indexable(&self) -> bool { 279 Self::is_indexable_name(self.as_str()) 280 } 281 282 pub fn is_indexable_name(value: &str) -> bool { 283 let mut bytes = value.bytes(); 284 let Some(byte) = bytes.next() else { 285 return false; 286 }; 287 bytes.next().is_none() && byte.is_ascii_alphabetic() 288 } 289 } 290 291 impl fmt::Display for TagName { 292 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 293 formatter.write_str(self.as_str()) 294 } 295 } 296 297 impl FromStr for TagName { 298 type Err = String; 299 300 fn from_str(value: &str) -> Result<Self, Self::Err> { 301 Self::new(value) 302 } 303 } 304 305 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 306 pub struct TagValue(String); 307 308 impl TagValue { 309 pub fn new(value: &str) -> Self { 310 Self(value.to_owned()) 311 } 312 313 pub fn as_str(&self) -> &str { 314 &self.0 315 } 316 } 317 318 impl fmt::Display for TagValue { 319 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 320 formatter.write_str(self.as_str()) 321 } 322 } 323 324 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 325 pub struct DTag(String); 326 327 impl DTag { 328 pub fn new(value: &str) -> Self { 329 Self(value.to_owned()) 330 } 331 332 pub fn as_str(&self) -> &str { 333 &self.0 334 } 335 } 336 337 impl fmt::Display for DTag { 338 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 339 formatter.write_str(self.as_str()) 340 } 341 } 342 343 impl FromStr for DTag { 344 type Err = String; 345 346 fn from_str(value: &str) -> Result<Self, Self::Err> { 347 Ok(Self::new(value)) 348 } 349 } 350 351 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 352 pub struct AddressKey(String); 353 354 impl AddressKey { 355 pub fn as_str(&self) -> &str { 356 &self.0 357 } 358 } 359 360 impl fmt::Display for AddressKey { 361 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 362 formatter.write_str(self.as_str()) 363 } 364 } 365 366 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 367 pub struct AddressCoordinate { 368 kind: Kind, 369 pubkey: PublicKeyHex, 370 d: DTag, 371 } 372 373 impl AddressCoordinate { 374 pub fn new(kind: Kind, pubkey: PublicKeyHex, d: DTag) -> Result<Self, String> { 375 if !kind.is_addressable() { 376 return Err(format!( 377 "address coordinate kind must be addressable, got {}", 378 kind.as_u32() 379 )); 380 } 381 Ok(Self { kind, pubkey, d }) 382 } 383 384 pub fn from_event(event: &Event) -> Result<Option<Self>, String> { 385 let kind = event.unsigned().kind(); 386 if !kind.is_addressable() { 387 return Ok(None); 388 } 389 let d = event 390 .unsigned() 391 .tags() 392 .iter() 393 .find_map(|tag| { 394 tag.indexed_pair().and_then(|(name, value)| { 395 if name == "d" { 396 Some(DTag::new(value)) 397 } else { 398 None 399 } 400 }) 401 }) 402 .ok_or_else(|| "addressable event must include a d tag".to_owned())?; 403 Self::new(kind, event.unsigned().pubkey().clone(), d).map(Some) 404 } 405 406 pub fn kind(&self) -> Kind { 407 self.kind 408 } 409 410 pub fn pubkey(&self) -> &PublicKeyHex { 411 &self.pubkey 412 } 413 414 pub fn d(&self) -> &DTag { 415 &self.d 416 } 417 418 pub fn key(&self) -> AddressKey { 419 AddressKey(self.to_string()) 420 } 421 } 422 423 impl fmt::Display for AddressCoordinate { 424 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 425 write!( 426 formatter, 427 "{}:{}:{}", 428 self.kind.as_u32(), 429 self.pubkey.as_str(), 430 self.d.as_str() 431 ) 432 } 433 } 434 435 impl FromStr for AddressCoordinate { 436 type Err = String; 437 438 fn from_str(value: &str) -> Result<Self, Self::Err> { 439 let mut parts = value.splitn(3, ':'); 440 let kind = parts 441 .next() 442 .unwrap_or_default() 443 .parse::<u64>() 444 .map_err(|_| "address coordinate kind must be an unsigned integer".to_owned()) 445 .and_then(Kind::new)?; 446 let pubkey = parts 447 .next() 448 .ok_or_else(|| "address coordinate pubkey is missing".to_owned()) 449 .and_then(PublicKeyHex::new)?; 450 let d = parts 451 .next() 452 .ok_or_else(|| "address coordinate d tag is missing".to_owned()) 453 .map(DTag::new)?; 454 Self::new(kind, pubkey, d) 455 } 456 } 457 458 #[derive(Debug, Clone, PartialEq, Eq)] 459 pub struct UnsignedEvent { 460 pubkey: PublicKeyHex, 461 created_at: UnixTimestamp, 462 kind: Kind, 463 tags: Vec<Tag>, 464 content: String, 465 } 466 467 impl UnsignedEvent { 468 pub fn new( 469 pubkey: PublicKeyHex, 470 created_at: UnixTimestamp, 471 kind: Kind, 472 tags: Vec<Tag>, 473 content: &str, 474 ) -> Self { 475 Self { 476 pubkey, 477 created_at, 478 kind, 479 tags, 480 content: content.to_owned(), 481 } 482 } 483 484 pub fn pubkey(&self) -> &PublicKeyHex { 485 &self.pubkey 486 } 487 488 pub fn created_at(&self) -> UnixTimestamp { 489 self.created_at 490 } 491 492 pub fn kind(&self) -> Kind { 493 self.kind 494 } 495 496 pub fn tags(&self) -> &[Tag] { 497 &self.tags 498 } 499 500 pub fn content(&self) -> &str { 501 &self.content 502 } 503 } 504 505 #[derive(Debug, Clone, PartialEq, Eq)] 506 pub struct Event { 507 id: EventId, 508 unsigned: UnsignedEvent, 509 sig: SignatureHex, 510 } 511 512 impl Event { 513 pub fn new(id: EventId, unsigned: UnsignedEvent, sig: SignatureHex) -> Self { 514 Self { id, unsigned, sig } 515 } 516 517 pub fn id(&self) -> &EventId { 518 &self.id 519 } 520 521 pub fn unsigned(&self) -> &UnsignedEvent { 522 &self.unsigned 523 } 524 525 pub fn sig(&self) -> &SignatureHex { 526 &self.sig 527 } 528 } 529 530 #[derive(Debug, Clone, PartialEq, Eq)] 531 pub struct RawEventJson(String); 532 533 impl RawEventJson { 534 pub fn new(value: &str) -> Result<Self, EventShapeError> { 535 if value.is_empty() { 536 return Err(EventShapeError::empty_raw_json()); 537 } 538 Ok(Self(value.to_owned())) 539 } 540 541 pub fn as_str(&self) -> &str { 542 &self.0 543 } 544 545 pub fn into_string(self) -> String { 546 self.0 547 } 548 } 549 550 pub struct EventShapeError { 551 message: String, 552 } 553 554 impl EventShapeError { 555 pub fn missing_field(field: &'static str) -> Self { 556 Self { 557 message: format!("event field `{field}` is missing"), 558 } 559 } 560 561 pub fn invalid_field(field: &'static str, reason: &str) -> Self { 562 Self { 563 message: format!("event field `{field}` is invalid: {reason}"), 564 } 565 } 566 567 fn empty_raw_json() -> Self { 568 Self { 569 message: "raw event JSON must not be empty".to_owned(), 570 } 571 } 572 } 573 574 impl fmt::Display for EventShapeError { 575 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 576 formatter.write_str(&self.message) 577 } 578 } 579 580 impl fmt::Debug for EventShapeError { 581 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 582 formatter 583 .debug_struct("EventShapeError") 584 .field("message", &self.message) 585 .finish() 586 } 587 } 588 589 impl std::error::Error for EventShapeError {} 590 591 pub fn canonical_event_json(event: &UnsignedEvent) -> String { 592 let tags: Vec<serde_json::Value> = event 593 .tags() 594 .iter() 595 .map(|tag| { 596 serde_json::Value::Array( 597 tag.values() 598 .iter() 599 .map(|value| serde_json::Value::String(value.clone())) 600 .collect(), 601 ) 602 }) 603 .collect(); 604 serde_json::json!([ 605 0, 606 event.pubkey().as_str(), 607 event.created_at().as_u64(), 608 event.kind().as_u32(), 609 tags, 610 event.content() 611 ]) 612 .to_string() 613 } 614 615 impl UnsignedEvent { 616 pub fn canonical_json(&self) -> String { 617 canonical_event_json(self) 618 } 619 } 620 621 #[derive(Debug, Clone, PartialEq)] 622 pub enum ClientMessage { 623 Event(Event), 624 Req { 625 subscription_id: SubscriptionId, 626 filters: Vec<Filter>, 627 }, 628 Count { 629 subscription_id: SubscriptionId, 630 filters: Vec<Filter>, 631 }, 632 Close(SubscriptionId), 633 Auth(Event), 634 NegOpen { 635 subscription_id: SubscriptionId, 636 filter: Filter, 637 message: String, 638 }, 639 NegMsg { 640 subscription_id: SubscriptionId, 641 message: String, 642 }, 643 NegClose(SubscriptionId), 644 } 645 646 #[derive(Debug, Clone, PartialEq, Eq)] 647 pub struct Filter { 648 ids: Vec<EventId>, 649 authors: Vec<PublicKeyHex>, 650 kinds: Vec<Kind>, 651 tag_filters: BTreeMap<TagName, Vec<TagValue>>, 652 since: Option<UnixTimestamp>, 653 until: Option<UnixTimestamp>, 654 limit: Option<u64>, 655 search: Option<String>, 656 } 657 658 impl Filter { 659 pub fn empty() -> Self { 660 Self { 661 ids: Vec::new(), 662 authors: Vec::new(), 663 kinds: Vec::new(), 664 tag_filters: BTreeMap::new(), 665 since: None, 666 until: None, 667 limit: None, 668 search: None, 669 } 670 } 671 672 #[allow(clippy::too_many_arguments)] 673 pub fn from_parts( 674 ids: Vec<EventId>, 675 authors: Vec<PublicKeyHex>, 676 kinds: Vec<Kind>, 677 tag_filters: BTreeMap<TagName, Vec<TagValue>>, 678 since: Option<UnixTimestamp>, 679 until: Option<UnixTimestamp>, 680 limit: Option<u64>, 681 search: Option<String>, 682 ) -> Result<Self, String> { 683 for (name, values) in &tag_filters { 684 if !name.is_indexable() { 685 return Err(format!( 686 "filter field `#{}` is invalid: tag name must be a single ASCII letter", 687 name.as_str() 688 )); 689 } 690 if values.is_empty() { 691 return Err(filter_array_error(&format!("#{}", name.as_str()))); 692 } 693 } 694 Ok(Self { 695 ids, 696 authors, 697 kinds, 698 tag_filters, 699 since, 700 until, 701 limit, 702 search, 703 }) 704 } 705 706 pub fn ids(&self) -> &[EventId] { 707 &self.ids 708 } 709 710 pub fn authors(&self) -> &[PublicKeyHex] { 711 &self.authors 712 } 713 714 pub fn kinds(&self) -> &[Kind] { 715 &self.kinds 716 } 717 718 pub fn tag_filters(&self) -> &BTreeMap<TagName, Vec<TagValue>> { 719 &self.tag_filters 720 } 721 722 pub fn since(&self) -> Option<UnixTimestamp> { 723 self.since 724 } 725 726 pub fn until(&self) -> Option<UnixTimestamp> { 727 self.until 728 } 729 730 pub fn limit(&self) -> Option<u64> { 731 self.limit 732 } 733 734 pub fn without_limit(&self) -> Self { 735 let mut filter = self.clone(); 736 filter.limit = None; 737 filter 738 } 739 740 pub fn with_limit(&self, limit: u64) -> Self { 741 let mut filter = self.clone(); 742 filter.limit = Some(limit); 743 filter 744 } 745 746 pub fn search(&self) -> Option<&str> { 747 self.search.as_deref() 748 } 749 750 pub fn is_complete(&self) -> bool { 751 !self.ids.is_empty() 752 } 753 754 pub fn matches(&self, event: &Event) -> bool { 755 if !self.ids.is_empty() && !self.ids.iter().any(|id| id == event.id()) { 756 return false; 757 } 758 if !self.authors.is_empty() 759 && !self 760 .authors 761 .iter() 762 .any(|author| author == event.unsigned().pubkey()) 763 { 764 return false; 765 } 766 if !self.kinds.is_empty() 767 && !self 768 .kinds 769 .iter() 770 .any(|kind| *kind == event.unsigned().kind()) 771 { 772 return false; 773 } 774 if let Some(since) = self.since 775 && event.unsigned().created_at().as_u64() < since.as_u64() 776 { 777 return false; 778 } 779 if let Some(until) = self.until 780 && event.unsigned().created_at().as_u64() > until.as_u64() 781 { 782 return false; 783 } 784 for (name, values) in &self.tag_filters { 785 let matched = event.unsigned().tags().iter().any(|tag| { 786 tag.indexed_pair().is_some_and(|(tag_name, tag_value)| { 787 tag_name == name.as_str() 788 && values.iter().any(|value| value.as_str() == tag_value) 789 }) 790 }); 791 if !matched { 792 return false; 793 } 794 } 795 true 796 } 797 } 798 799 #[derive(Debug, Clone, PartialEq)] 800 pub enum RelayMessage { 801 Event { 802 subscription_id: SubscriptionId, 803 event: Event, 804 }, 805 Ok { 806 event_id: EventId, 807 accepted: bool, 808 message: String, 809 }, 810 Eose(SubscriptionId), 811 Closed { 812 subscription_id: SubscriptionId, 813 message: String, 814 }, 815 Count { 816 subscription_id: SubscriptionId, 817 count: u64, 818 hll: Option<String>, 819 }, 820 Notice(String), 821 Auth(String), 822 NegErr { 823 subscription_id: SubscriptionId, 824 message: String, 825 }, 826 NegMsg { 827 subscription_id: SubscriptionId, 828 message: String, 829 }, 830 } 831 832 impl RelayMessage { 833 pub fn encode(&self) -> String { 834 encode_relay_message(self) 835 } 836 } 837 838 pub fn encode_relay_message(message: &RelayMessage) -> String { 839 relay_message_to_value(message).to_string() 840 } 841 842 pub fn relay_message_to_value(message: &RelayMessage) -> serde_json::Value { 843 match message { 844 RelayMessage::Event { 845 subscription_id, 846 event, 847 } => serde_json::json!(["EVENT", subscription_id.as_str(), event_to_value(event)]), 848 RelayMessage::Ok { 849 event_id, 850 accepted, 851 message, 852 } => serde_json::json!(["OK", event_id.as_str(), accepted, message]), 853 RelayMessage::Eose(subscription_id) => { 854 serde_json::json!(["EOSE", subscription_id.as_str()]) 855 } 856 RelayMessage::Closed { 857 subscription_id, 858 message, 859 } => serde_json::json!(["CLOSED", subscription_id.as_str(), message]), 860 RelayMessage::Count { 861 subscription_id, 862 count, 863 hll, 864 } => { 865 let mut payload = serde_json::Map::new(); 866 payload.insert( 867 "count".to_owned(), 868 serde_json::Value::Number((*count).into()), 869 ); 870 if let Some(hll) = hll { 871 payload.insert("hll".to_owned(), serde_json::Value::String(hll.clone())); 872 } 873 serde_json::json!(["COUNT", subscription_id.as_str(), payload]) 874 } 875 RelayMessage::Notice(message) => serde_json::json!(["NOTICE", message]), 876 RelayMessage::Auth(challenge) => serde_json::json!(["AUTH", challenge]), 877 RelayMessage::NegErr { 878 subscription_id, 879 message, 880 } => serde_json::json!(["NEG-ERR", subscription_id.as_str(), message]), 881 RelayMessage::NegMsg { 882 subscription_id, 883 message, 884 } => serde_json::json!(["NEG-MSG", subscription_id.as_str(), message]), 885 } 886 } 887 888 pub fn event_to_value(event: &Event) -> serde_json::Value { 889 let tags = event 890 .unsigned() 891 .tags() 892 .iter() 893 .map(|tag| { 894 serde_json::Value::Array( 895 tag.values() 896 .iter() 897 .map(|value| serde_json::Value::String(value.clone())) 898 .collect(), 899 ) 900 }) 901 .collect::<Vec<_>>(); 902 serde_json::json!({ 903 "id": event.id().as_str(), 904 "pubkey": event.unsigned().pubkey().as_str(), 905 "created_at": event.unsigned().created_at().as_u64(), 906 "kind": event.unsigned().kind().as_u32(), 907 "tags": tags, 908 "content": event.unsigned().content(), 909 "sig": event.sig().as_str() 910 }) 911 } 912 913 pub fn filter_to_value(filter: &Filter) -> serde_json::Value { 914 let mut object = serde_json::Map::new(); 915 if !filter.ids().is_empty() { 916 object.insert( 917 "ids".to_owned(), 918 serde_json::Value::Array( 919 filter 920 .ids() 921 .iter() 922 .map(|id| serde_json::Value::String(id.as_str().to_owned())) 923 .collect(), 924 ), 925 ); 926 } 927 if !filter.authors().is_empty() { 928 object.insert( 929 "authors".to_owned(), 930 serde_json::Value::Array( 931 filter 932 .authors() 933 .iter() 934 .map(|author| serde_json::Value::String(author.as_str().to_owned())) 935 .collect(), 936 ), 937 ); 938 } 939 if !filter.kinds().is_empty() { 940 object.insert( 941 "kinds".to_owned(), 942 serde_json::Value::Array( 943 filter 944 .kinds() 945 .iter() 946 .map(|kind| serde_json::Value::Number(kind.as_u32().into())) 947 .collect(), 948 ), 949 ); 950 } 951 for (name, values) in filter.tag_filters() { 952 object.insert( 953 format!("#{}", name.as_str()), 954 serde_json::Value::Array( 955 values 956 .iter() 957 .map(|value| serde_json::Value::String(value.as_str().to_owned())) 958 .collect(), 959 ), 960 ); 961 } 962 if let Some(since) = filter.since() { 963 object.insert( 964 "since".to_owned(), 965 serde_json::Value::Number(since.as_u64().into()), 966 ); 967 } 968 if let Some(until) = filter.until() { 969 object.insert( 970 "until".to_owned(), 971 serde_json::Value::Number(until.as_u64().into()), 972 ); 973 } 974 if let Some(limit) = filter.limit() { 975 object.insert("limit".to_owned(), serde_json::Value::Number(limit.into())); 976 } 977 if let Some(search) = filter.search() { 978 object.insert( 979 "search".to_owned(), 980 serde_json::Value::String(search.to_owned()), 981 ); 982 } 983 serde_json::Value::Object(object) 984 } 985 986 pub fn filter_from_value(value: &serde_json::Value) -> Result<Filter, String> { 987 let object = value 988 .as_object() 989 .ok_or_else(|| "filter must be a JSON object".to_owned())?; 990 let mut filter = Filter::empty(); 991 for (field, raw) in object { 992 match field.as_str() { 993 "ids" => filter.ids = parse_event_id_filter_array(field, raw)?, 994 "authors" => filter.authors = parse_pubkey_filter_array(field, raw)?, 995 "kinds" => filter.kinds = parse_kind_filter_array(field, raw)?, 996 "since" => filter.since = Some(UnixTimestamp::new(parse_u64_filter_field(field, raw)?)), 997 "until" => filter.until = Some(UnixTimestamp::new(parse_u64_filter_field(field, raw)?)), 998 "limit" => filter.limit = Some(parse_u64_filter_field(field, raw)?), 999 "search" => filter.search = Some(parse_string_filter_field(field, raw)?.to_owned()), 1000 tag_field if tag_field.starts_with('#') => { 1001 let (name, values) = parse_tag_filter_field(tag_field, raw)?; 1002 filter.tag_filters.insert(name, values); 1003 } 1004 unsupported => return Err(format!("filter field `{unsupported}` is unsupported")), 1005 } 1006 } 1007 Ok(filter) 1008 } 1009 1010 pub fn parse_client_message(raw: &str) -> Result<ClientMessage, String> { 1011 let value = json_value_without_duplicate_fields(raw) 1012 .map_err(|source| format!("client message JSON is invalid: {source}"))?; 1013 let array = value 1014 .as_array() 1015 .ok_or_else(|| "client message must be an array".to_owned())?; 1016 let command_value = array 1017 .first() 1018 .ok_or_else(|| "client message command is missing".to_owned())?; 1019 let command = command_value 1020 .as_str() 1021 .ok_or_else(|| "client message command must be a string".to_owned())?; 1022 match command { 1023 "EVENT" => parse_event_client_message(array), 1024 "REQ" => parse_req_client_message(array), 1025 "COUNT" => parse_count_client_message(array), 1026 "CLOSE" => parse_close_client_message(array), 1027 "AUTH" => parse_auth_client_message(array), 1028 unsupported => Err(format!( 1029 "client message command `{unsupported}` is unsupported" 1030 )), 1031 } 1032 } 1033 1034 pub fn parse_event_json(raw: &RawEventJson) -> Result<Event, EventShapeError> { 1035 let value = json_value_without_duplicate_fields(raw.as_str()).map_err(|source| { 1036 EventShapeError::invalid_field("event", &format!("invalid JSON: {source}")) 1037 })?; 1038 event_from_value(&value) 1039 } 1040 1041 fn json_value_without_duplicate_fields(raw: &str) -> Result<serde_json::Value, serde_json::Error> { 1042 let mut deserializer = serde_json::Deserializer::from_str(raw); 1043 let value = UniqueJsonValue::deserialize(&mut deserializer)?.0; 1044 deserializer.end()?; 1045 Ok(value) 1046 } 1047 1048 struct UniqueJsonValue(serde_json::Value); 1049 1050 impl<'de> Deserialize<'de> for UniqueJsonValue { 1051 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1052 where 1053 D: Deserializer<'de>, 1054 { 1055 deserializer 1056 .deserialize_any(UniqueJsonValueVisitor) 1057 .map(Self) 1058 } 1059 } 1060 1061 struct UniqueJsonValueVisitor; 1062 1063 impl<'de> Visitor<'de> for UniqueJsonValueVisitor { 1064 type Value = serde_json::Value; 1065 1066 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 1067 formatter.write_str("a JSON value without duplicate object fields") 1068 } 1069 1070 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> { 1071 Ok(serde_json::Value::Bool(value)) 1072 } 1073 1074 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> { 1075 Ok(serde_json::Value::Number(value.into())) 1076 } 1077 1078 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> { 1079 Ok(serde_json::Value::Number(value.into())) 1080 } 1081 1082 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E> 1083 where 1084 E: de::Error, 1085 { 1086 serde_json::Number::from_f64(value) 1087 .map(serde_json::Value::Number) 1088 .ok_or_else(|| E::custom("JSON number must be finite")) 1089 } 1090 1091 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> { 1092 Ok(serde_json::Value::String(value.to_owned())) 1093 } 1094 1095 fn visit_borrowed_str<E>(self, value: &'de str) -> Result<Self::Value, E> { 1096 Ok(serde_json::Value::String(value.to_owned())) 1097 } 1098 1099 fn visit_string<E>(self, value: String) -> Result<Self::Value, E> { 1100 Ok(serde_json::Value::String(value)) 1101 } 1102 1103 fn visit_none<E>(self) -> Result<Self::Value, E> { 1104 Ok(serde_json::Value::Null) 1105 } 1106 1107 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error> 1108 where 1109 D: Deserializer<'de>, 1110 { 1111 UniqueJsonValue::deserialize(deserializer).map(|value| value.0) 1112 } 1113 1114 fn visit_unit<E>(self) -> Result<Self::Value, E> { 1115 Ok(serde_json::Value::Null) 1116 } 1117 1118 fn visit_seq<A>(self, mut sequence: A) -> Result<Self::Value, A::Error> 1119 where 1120 A: SeqAccess<'de>, 1121 { 1122 let mut values = Vec::new(); 1123 while let Some(value) = sequence.next_element::<UniqueJsonValue>()? { 1124 values.push(value.0); 1125 } 1126 Ok(serde_json::Value::Array(values)) 1127 } 1128 1129 fn visit_map<A>(self, mut object: A) -> Result<Self::Value, A::Error> 1130 where 1131 A: MapAccess<'de>, 1132 { 1133 let mut values = serde_json::Map::new(); 1134 while let Some(field) = object.next_key::<String>()? { 1135 if values.contains_key(&field) { 1136 return Err(de::Error::custom(format!( 1137 "duplicate object field `{field}`" 1138 ))); 1139 } 1140 let value = object.next_value::<UniqueJsonValue>()?; 1141 values.insert(field, value.0); 1142 } 1143 Ok(serde_json::Value::Object(values)) 1144 } 1145 } 1146 1147 pub fn event_from_value(value: &serde_json::Value) -> Result<Event, EventShapeError> { 1148 let object = value 1149 .as_object() 1150 .ok_or_else(|| EventShapeError::invalid_field("event", "must be an object"))?; 1151 let id = EventId::new(field_string(object, "id")?) 1152 .map_err(|reason| EventShapeError::invalid_field("id", &reason))?; 1153 let pubkey = PublicKeyHex::new(field_string(object, "pubkey")?) 1154 .map_err(|reason| EventShapeError::invalid_field("pubkey", &reason))?; 1155 let created_at = UnixTimestamp::new(field_u64(object, "created_at")?); 1156 let kind = Kind::new(field_u64(object, "kind")?) 1157 .map_err(|reason| EventShapeError::invalid_field("kind", &reason))?; 1158 let tags = tags_from_value(field_value(object, "tags")?)?; 1159 let content = field_string(object, "content")?; 1160 let sig = SignatureHex::new(field_string(object, "sig")?) 1161 .map_err(|reason| EventShapeError::invalid_field("sig", &reason))?; 1162 Ok(Event::new( 1163 id, 1164 UnsignedEvent::new(pubkey, created_at, kind, tags, content), 1165 sig, 1166 )) 1167 } 1168 1169 fn parse_event_client_message(array: &[serde_json::Value]) -> Result<ClientMessage, String> { 1170 if array.len() != 2 { 1171 return Err("EVENT client message must contain exactly 2 elements".to_owned()); 1172 } 1173 event_from_value(&array[1]) 1174 .map(ClientMessage::Event) 1175 .map_err(|source| source.to_string()) 1176 } 1177 1178 fn parse_auth_client_message(array: &[serde_json::Value]) -> Result<ClientMessage, String> { 1179 if array.len() != 2 { 1180 return Err("AUTH client message must contain exactly 2 elements".to_owned()); 1181 } 1182 event_from_value(&array[1]) 1183 .map(ClientMessage::Auth) 1184 .map_err(|source| source.to_string()) 1185 } 1186 1187 fn parse_req_client_message(array: &[serde_json::Value]) -> Result<ClientMessage, String> { 1188 if array.len() < 3 { 1189 return Err("REQ client message must contain a subscription id and filters".to_owned()); 1190 } 1191 let subscription_id = array[1] 1192 .as_str() 1193 .ok_or_else(|| "REQ subscription id must be a string".to_owned()) 1194 .and_then(SubscriptionId::new)?; 1195 let filters = array[2..] 1196 .iter() 1197 .map(filter_from_value) 1198 .collect::<Result<Vec<_>, _>>()?; 1199 Ok(ClientMessage::Req { 1200 subscription_id, 1201 filters, 1202 }) 1203 } 1204 1205 fn parse_count_client_message(array: &[serde_json::Value]) -> Result<ClientMessage, String> { 1206 if array.len() < 3 { 1207 return Err("COUNT client message must contain a subscription id and filters".to_owned()); 1208 } 1209 let subscription_id = array[1] 1210 .as_str() 1211 .ok_or_else(|| "COUNT subscription id must be a string".to_owned()) 1212 .and_then(SubscriptionId::new)?; 1213 let filters = array[2..] 1214 .iter() 1215 .map(filter_from_value) 1216 .collect::<Result<Vec<_>, _>>()?; 1217 Ok(ClientMessage::Count { 1218 subscription_id, 1219 filters, 1220 }) 1221 } 1222 1223 fn parse_close_client_message(array: &[serde_json::Value]) -> Result<ClientMessage, String> { 1224 if array.len() != 2 { 1225 return Err("CLOSE client message must contain exactly 2 elements".to_owned()); 1226 } 1227 array[1] 1228 .as_str() 1229 .ok_or_else(|| "CLOSE subscription id must be a string".to_owned()) 1230 .and_then(SubscriptionId::new) 1231 .map(ClientMessage::Close) 1232 } 1233 1234 fn field_value<'a>( 1235 object: &'a serde_json::Map<String, serde_json::Value>, 1236 field: &'static str, 1237 ) -> Result<&'a serde_json::Value, EventShapeError> { 1238 object 1239 .get(field) 1240 .ok_or_else(|| EventShapeError::missing_field(field)) 1241 } 1242 1243 fn field_string<'a>( 1244 object: &'a serde_json::Map<String, serde_json::Value>, 1245 field: &'static str, 1246 ) -> Result<&'a str, EventShapeError> { 1247 field_value(object, field)? 1248 .as_str() 1249 .ok_or_else(|| EventShapeError::invalid_field(field, "must be a string")) 1250 } 1251 1252 fn field_u64( 1253 object: &serde_json::Map<String, serde_json::Value>, 1254 field: &'static str, 1255 ) -> Result<u64, EventShapeError> { 1256 field_value(object, field)? 1257 .as_u64() 1258 .ok_or_else(|| EventShapeError::invalid_field(field, "must be an unsigned integer")) 1259 } 1260 1261 fn tags_from_value(value: &serde_json::Value) -> Result<Vec<Tag>, EventShapeError> { 1262 let array = value 1263 .as_array() 1264 .ok_or_else(|| EventShapeError::invalid_field("tags", "must be an array"))?; 1265 array 1266 .iter() 1267 .map(|tag| { 1268 let values = tag 1269 .as_array() 1270 .ok_or_else(|| EventShapeError::invalid_field("tags", "tag must be an array"))? 1271 .iter() 1272 .map(|part| { 1273 part.as_str().map(str::to_owned).ok_or_else(|| { 1274 EventShapeError::invalid_field("tags", "tag elements must be strings") 1275 }) 1276 }) 1277 .collect::<Result<Vec<_>, _>>()?; 1278 Tag::new(values).map_err(|reason| EventShapeError::invalid_field("tags", &reason)) 1279 }) 1280 .collect() 1281 } 1282 1283 fn parse_event_id_filter_array( 1284 field: &str, 1285 value: &serde_json::Value, 1286 ) -> Result<Vec<EventId>, String> { 1287 parse_string_filter_array(field, value, |item| { 1288 EventId::new(item).map_err(|reason| format!("filter field `{field}` is invalid: {reason}")) 1289 }) 1290 } 1291 1292 fn parse_pubkey_filter_array( 1293 field: &str, 1294 value: &serde_json::Value, 1295 ) -> Result<Vec<PublicKeyHex>, String> { 1296 parse_string_filter_array(field, value, |item| { 1297 PublicKeyHex::new(item) 1298 .map_err(|reason| format!("filter field `{field}` is invalid: {reason}")) 1299 }) 1300 } 1301 1302 fn parse_kind_filter_array(field: &str, value: &serde_json::Value) -> Result<Vec<Kind>, String> { 1303 parse_u64_filter_array(field, value, |item| { 1304 Kind::new(item).map_err(|reason| format!("filter field `{field}` is invalid: {reason}")) 1305 }) 1306 } 1307 1308 fn parse_tag_filter_field( 1309 field: &str, 1310 value: &serde_json::Value, 1311 ) -> Result<(TagName, Vec<TagValue>), String> { 1312 let name = &field[1..]; 1313 let tag_name = TagName::new(name) 1314 .map_err(|reason| format!("filter field `{field}` is invalid: {reason}"))?; 1315 if !tag_name.is_indexable() { 1316 return Err(format!( 1317 "filter field `{field}` is invalid: tag name must be a single ASCII letter" 1318 )); 1319 } 1320 let values = parse_string_filter_array(field, value, |item| { 1321 if name == "e" { 1322 EventId::new(item) 1323 .map(|_| TagValue::new(item)) 1324 .map_err(|reason| format!("filter field `{field}` is invalid: {reason}")) 1325 } else if name == "p" { 1326 PublicKeyHex::new(item) 1327 .map(|_| TagValue::new(item)) 1328 .map_err(|reason| format!("filter field `{field}` is invalid: {reason}")) 1329 } else { 1330 Ok(TagValue::new(item)) 1331 } 1332 })?; 1333 Ok((tag_name, values)) 1334 } 1335 1336 fn parse_string_filter_array<T>( 1337 field: &str, 1338 value: &serde_json::Value, 1339 parse_item: impl Fn(&str) -> Result<T, String>, 1340 ) -> Result<Vec<T>, String> { 1341 let array = value.as_array().ok_or_else(|| filter_array_error(field))?; 1342 if array.is_empty() { 1343 return Err(filter_array_error(field)); 1344 } 1345 array 1346 .iter() 1347 .map(|item| { 1348 item.as_str() 1349 .ok_or_else(|| format!("filter field `{field}` values must be strings")) 1350 .and_then(&parse_item) 1351 }) 1352 .collect() 1353 } 1354 1355 fn parse_u64_filter_array<T>( 1356 field: &str, 1357 value: &serde_json::Value, 1358 parse_item: impl Fn(u64) -> Result<T, String>, 1359 ) -> Result<Vec<T>, String> { 1360 let array = value.as_array().ok_or_else(|| filter_array_error(field))?; 1361 if array.is_empty() { 1362 return Err(filter_array_error(field)); 1363 } 1364 array 1365 .iter() 1366 .map(|item| { 1367 item.as_u64() 1368 .ok_or_else(|| format!("filter field `{field}` values must be unsigned integers")) 1369 .and_then(&parse_item) 1370 }) 1371 .collect() 1372 } 1373 1374 fn parse_u64_filter_field(field: &str, value: &serde_json::Value) -> Result<u64, String> { 1375 value 1376 .as_u64() 1377 .ok_or_else(|| format!("filter field `{field}` must be an unsigned integer")) 1378 } 1379 1380 fn parse_string_filter_field<'a>( 1381 field: &str, 1382 value: &'a serde_json::Value, 1383 ) -> Result<&'a str, String> { 1384 value 1385 .as_str() 1386 .ok_or_else(|| format!("filter field `{field}` must be a string")) 1387 } 1388 1389 fn filter_array_error(field: &str) -> String { 1390 format!("filter field `{field}` must be a non-empty array") 1391 } 1392 1393 fn require_lowercase_hex(scalar: &'static str, value: &str, expected: usize) -> Result<(), String> { 1394 let actual = value.chars().count(); 1395 if actual != expected { 1396 return Err(invalid_length_error(scalar, expected, actual)); 1397 } 1398 if value 1399 .bytes() 1400 .any(|byte| !byte.is_ascii_hexdigit() || byte.is_ascii_uppercase()) 1401 { 1402 return Err(non_lowercase_hex_error(scalar)); 1403 } 1404 Ok(()) 1405 } 1406 1407 fn empty_error(scalar: &'static str) -> String { 1408 format!("{scalar} must not be empty") 1409 } 1410 1411 fn invalid_length_error(scalar: &'static str, expected: usize, actual: usize) -> String { 1412 format!("{scalar} must be {expected} characters, got {actual}") 1413 } 1414 1415 fn too_long_error(scalar: &'static str, max: usize, actual: usize) -> String { 1416 format!("{scalar} must be at most {max} characters, got {actual}") 1417 } 1418 1419 fn non_lowercase_hex_error(scalar: &'static str) -> String { 1420 format!("{scalar} must be lowercase hex") 1421 } 1422 1423 fn kind_out_of_range_error(value: u64) -> String { 1424 format!("kind must fit in u32, got {value}") 1425 } 1426 1427 #[cfg(test)] 1428 mod tests { 1429 use super::{ 1430 AddressCoordinate, AddressKey, ClientMessage, DTag, Event, EventId, EventShapeError, 1431 Filter, Kind, KindClass, PublicKeyHex, RawEventJson, RelayMessage, SignatureHex, 1432 SubscriptionId, Tag, TagName, TagValue, UnixTimestamp, UnsignedEvent, canonical_event_json, 1433 empty_error, encode_relay_message, event_from_value, event_to_value, filter_from_value, 1434 filter_to_value, invalid_length_error, kind_out_of_range_error, non_lowercase_hex_error, 1435 parse_client_message, parse_event_json, relay_message_to_value, too_long_error, 1436 }; 1437 use core::str::FromStr; 1438 use std::collections::{BTreeMap, hash_map::DefaultHasher}; 1439 use std::hash::{Hash, Hasher}; 1440 1441 #[test] 1442 fn event_id_accepts_lowercase_hex() { 1443 let value = "0".repeat(EventId::HEX_LENGTH); 1444 let event_id = EventId::new(&value).expect("event id"); 1445 1446 assert_eq!(event_id.as_str(), value); 1447 assert_eq!(event_id.to_string(), value); 1448 let cloned = event_id.clone(); 1449 let mut hasher = DefaultHasher::new(); 1450 cloned.hash(&mut hasher); 1451 assert_ne!(hasher.finish(), 0); 1452 assert_eq!(format!("{cloned:?}"), format!("EventId(\"{value}\")")); 1453 assert!(cloned <= event_id); 1454 assert_eq!(EventId::from_str(&value), Ok(event_id)); 1455 } 1456 1457 #[test] 1458 fn fixed_hex_scalars_reject_bad_lengths_and_characters() { 1459 assert_eq!( 1460 EventId::new(&"0".repeat(EventId::HEX_LENGTH - 1)), 1461 Err(format!( 1462 "event id must be {} characters, got {}", 1463 EventId::HEX_LENGTH, 1464 EventId::HEX_LENGTH - 1 1465 )) 1466 ); 1467 assert_eq!( 1468 EventId::new("bad"), 1469 Err("event id must be 64 characters, got 3".to_owned()) 1470 ); 1471 let invalid_public_key = format!("{}G", "0".repeat(PublicKeyHex::HEX_LENGTH - 1)); 1472 assert_eq!( 1473 PublicKeyHex::new(&invalid_public_key), 1474 Err("public key must be lowercase hex".to_owned()) 1475 ); 1476 let invalid_signature = format!("{}A", "0".repeat(SignatureHex::HEX_LENGTH - 1)); 1477 assert_eq!( 1478 SignatureHex::new(&invalid_signature), 1479 Err("signature must be lowercase hex".to_owned()) 1480 ); 1481 } 1482 1483 #[test] 1484 fn public_key_and_signature_display_values() { 1485 let public_key_value = "1".repeat(PublicKeyHex::HEX_LENGTH); 1486 let signature_value = "2".repeat(SignatureHex::HEX_LENGTH); 1487 let public_key = PublicKeyHex::new(&public_key_value).expect("pubkey"); 1488 let signature = SignatureHex::new(&signature_value).expect("sig"); 1489 1490 assert_eq!(public_key.as_str(), "1".repeat(PublicKeyHex::HEX_LENGTH)); 1491 assert_eq!(signature.as_str(), "2".repeat(SignatureHex::HEX_LENGTH)); 1492 assert_eq!(public_key.to_string(), public_key.as_str()); 1493 assert_eq!(signature.to_string(), signature.as_str()); 1494 assert_eq!(PublicKeyHex::from_str(public_key.as_str()), Ok(public_key)); 1495 assert_eq!(SignatureHex::from_str(signature.as_str()), Ok(signature)); 1496 } 1497 1498 #[test] 1499 fn subscription_id_rejects_empty_and_overlong_values() { 1500 assert_eq!( 1501 SubscriptionId::new(""), 1502 Err("subscription id must not be empty".to_owned()) 1503 ); 1504 assert_eq!( 1505 SubscriptionId::new(&"x".repeat(SubscriptionId::MAX_LENGTH + 1)), 1506 Err(format!( 1507 "subscription id must be at most {} characters, got {}", 1508 SubscriptionId::MAX_LENGTH, 1509 SubscriptionId::MAX_LENGTH + 1 1510 )) 1511 ); 1512 let subscription = SubscriptionId::new("radroots").expect("subscription"); 1513 assert_eq!(subscription.as_str(), "radroots"); 1514 assert_eq!(subscription.to_string(), "radroots"); 1515 assert_eq!(SubscriptionId::from_str("radroots"), Ok(subscription)); 1516 } 1517 1518 #[test] 1519 fn timestamp_and_kind_expose_numeric_values() { 1520 let timestamp = UnixTimestamp::new(1_714_124_433); 1521 let kind = Kind::new(30_402).expect("kind"); 1522 1523 assert_eq!(timestamp.as_u64(), 1_714_124_433); 1524 assert_eq!(timestamp.to_string(), "1714124433"); 1525 assert_eq!(UnixTimestamp::from(7).as_u64(), 7); 1526 assert_eq!(kind.as_u32(), 30_402); 1527 assert_eq!(kind.to_string(), "30402"); 1528 assert_eq!(Kind::try_from(30_402), Ok(kind)); 1529 } 1530 1531 #[test] 1532 fn kind_rejects_values_outside_u32() { 1533 let value = u64::from(u32::MAX) + 1; 1534 1535 assert_eq!( 1536 Kind::new(value), 1537 Err(format!("kind must fit in u32, got {value}")) 1538 ); 1539 assert_eq!( 1540 kind_out_of_range_error(value), 1541 format!("kind must fit in u32, got {value}") 1542 ); 1543 } 1544 1545 #[test] 1546 fn event_kind_classification_matches_nip01_ranges() { 1547 for value in [1, 2, 4, 44, 1_000, 9_999, 45, 40_000] { 1548 let kind = Kind::new(value).expect("regular"); 1549 assert_eq!(kind.class(), KindClass::Regular); 1550 assert!(kind.is_regular()); 1551 assert!(!kind.is_replaceable()); 1552 assert!(!kind.is_ephemeral()); 1553 assert!(!kind.is_addressable()); 1554 } 1555 1556 for value in [0, 3, 10_000, 19_999] { 1557 let kind = Kind::new(value).expect("replaceable"); 1558 assert_eq!(kind.class(), KindClass::Replaceable); 1559 assert!(kind.is_replaceable()); 1560 } 1561 1562 for value in [20_000, 29_999] { 1563 let kind = Kind::new(value).expect("ephemeral"); 1564 assert_eq!(kind.class(), KindClass::Ephemeral); 1565 assert!(kind.is_ephemeral()); 1566 } 1567 1568 for value in [30_000, 39_999] { 1569 let kind = Kind::new(value).expect("addressable"); 1570 assert_eq!(kind.class(), KindClass::Addressable); 1571 assert!(kind.is_addressable()); 1572 } 1573 1574 assert_eq!(format!("{:?}", KindClass::Regular), "Regular"); 1575 assert_eq!(KindClass::Regular, KindClass::Regular); 1576 } 1577 1578 #[test] 1579 fn address_coordinate_parses_formats_and_extracts_from_events() { 1580 let pubkey = PublicKeyHex::new(&"1".repeat(PublicKeyHex::HEX_LENGTH)).expect("pubkey"); 1581 let coordinate = AddressCoordinate::new( 1582 Kind::new(30_402).expect("kind"), 1583 pubkey.clone(), 1584 DTag::new("market-stall"), 1585 ) 1586 .expect("coordinate"); 1587 let key: AddressKey = coordinate.key(); 1588 let parsed = AddressCoordinate::from_str(&coordinate.to_string()).expect("parsed"); 1589 let empty_d = 1590 AddressCoordinate::from_str(&format!("30000:{}:", pubkey.as_str())).expect("empty d"); 1591 let event = addressable_event("market-stall", 30_402); 1592 1593 assert_eq!(coordinate.kind().as_u32(), 30_402); 1594 assert_eq!(coordinate.pubkey(), &pubkey); 1595 assert_eq!(coordinate.d().as_str(), "market-stall"); 1596 assert_eq!( 1597 coordinate.to_string(), 1598 format!("30402:{}:market-stall", pubkey.as_str()) 1599 ); 1600 assert_eq!(key.as_str(), coordinate.to_string()); 1601 assert_eq!(key.to_string(), coordinate.to_string()); 1602 assert_eq!(parsed, coordinate); 1603 assert_eq!(empty_d.d().as_str(), ""); 1604 assert_eq!( 1605 AddressCoordinate::from_event(&event).expect("event"), 1606 Some(coordinate) 1607 ); 1608 assert_eq!( 1609 AddressCoordinate::from_event(&event_for_filter( 1610 &"e".repeat(EventId::HEX_LENGTH), 1611 50, 1612 1 1613 )) 1614 .expect("regular"), 1615 None 1616 ); 1617 assert_eq!(format!("{:?}", DTag::new("d")), "DTag(\"d\")"); 1618 assert_eq!(DTag::new("d").to_string(), "d"); 1619 assert_eq!(DTag::from_str("d"), Ok(DTag::new("d"))); 1620 } 1621 1622 #[test] 1623 fn address_coordinate_rejects_invalid_coordinates() { 1624 let pubkey = "1".repeat(PublicKeyHex::HEX_LENGTH); 1625 1626 assert_eq!( 1627 AddressCoordinate::new( 1628 Kind::new(1).expect("kind"), 1629 PublicKeyHex::new(&pubkey).expect("pubkey"), 1630 DTag::new("d") 1631 ) 1632 .expect_err("regular kind"), 1633 "address coordinate kind must be addressable, got 1" 1634 ); 1635 assert_eq!( 1636 AddressCoordinate::from_str("bad").expect_err("kind parse"), 1637 "address coordinate kind must be an unsigned integer" 1638 ); 1639 assert_eq!( 1640 AddressCoordinate::from_str(&format!("{}:{pubkey}:d", u64::from(u32::MAX) + 1)) 1641 .expect_err("kind range"), 1642 format!("kind must fit in u32, got {}", u64::from(u32::MAX) + 1) 1643 ); 1644 assert_eq!( 1645 AddressCoordinate::from_str("1").expect_err("missing pubkey"), 1646 "address coordinate pubkey is missing" 1647 ); 1648 assert_eq!( 1649 AddressCoordinate::from_str("30000:bad").expect_err("bad pubkey"), 1650 "public key must be 64 characters, got 3" 1651 ); 1652 assert_eq!( 1653 AddressCoordinate::from_str(&format!("30000:{pubkey}")).expect_err("missing d"), 1654 "address coordinate d tag is missing" 1655 ); 1656 assert_eq!( 1657 AddressCoordinate::from_str(&format!("1:{pubkey}:d")).expect_err("regular parse"), 1658 "address coordinate kind must be addressable, got 1" 1659 ); 1660 assert_eq!( 1661 AddressCoordinate::from_event(&addressable_event_without_d()) 1662 .expect_err("missing event d"), 1663 "addressable event must include a d tag" 1664 ); 1665 } 1666 1667 #[test] 1668 fn scalar_errors_have_stable_messages() { 1669 assert_eq!(empty_error("id"), "id must not be empty"); 1670 assert_eq!( 1671 invalid_length_error("id", 64, 63), 1672 "id must be 64 characters, got 63" 1673 ); 1674 assert_eq!( 1675 too_long_error("id", 64, 65), 1676 "id must be at most 64 characters, got 65" 1677 ); 1678 assert_eq!(non_lowercase_hex_error("id"), "id must be lowercase hex"); 1679 } 1680 1681 #[test] 1682 fn tag_model_preserves_tag_arrays_and_extracts_first_values() { 1683 let tag = Tag::from_parts("e", &["event-id", "wss://relay.example"]).expect("tag"); 1684 1685 assert_eq!(tag.name(), TagName::new("e").expect("name")); 1686 assert_eq!(tag.value(), Some(TagValue::new("event-id"))); 1687 assert_eq!( 1688 tag.values(), 1689 &[ 1690 "e".to_owned(), 1691 "event-id".to_owned(), 1692 "wss://relay.example".to_owned() 1693 ] 1694 ); 1695 assert_eq!(tag.indexed_pair(), Some(("e", "event-id"))); 1696 assert_eq!(tag.name().to_string(), "e"); 1697 assert_eq!(tag.value().expect("value").to_string(), "event-id"); 1698 } 1699 1700 #[test] 1701 fn tag_model_rejects_empty_arrays_and_names() { 1702 assert_eq!( 1703 Tag::new(Vec::new()), 1704 Err("tag must not be empty".to_owned()) 1705 ); 1706 assert_eq!( 1707 Tag::new(vec![String::new()]), 1708 Err("tag name must not be empty".to_owned()) 1709 ); 1710 assert_eq!( 1711 TagName::new(""), 1712 Err("tag name must not be empty".to_owned()) 1713 ); 1714 } 1715 1716 #[test] 1717 fn tag_indexing_is_limited_to_single_ascii_letters() { 1718 let uppercase = Tag::from_parts("E", &["root"]).expect("uppercase"); 1719 let missing_value = Tag::from_parts("p", &[]).expect("missing value"); 1720 let long_name = Tag::from_parts("alt", &["reply"]).expect("long name"); 1721 let non_ascii = Tag::from_parts("é", &["value"]).expect("non ascii"); 1722 1723 assert!(TagName::from_str("A").expect("name").is_indexable()); 1724 assert!(TagName::is_indexable_name("z")); 1725 assert_eq!(uppercase.indexed_pair(), Some(("E", "root"))); 1726 assert_eq!(missing_value.indexed_pair(), None); 1727 assert_eq!(long_name.indexed_pair(), None); 1728 assert_eq!(non_ascii.indexed_pair(), None); 1729 assert!(!TagName::is_indexable_name("")); 1730 assert!(!TagName::is_indexable_name("aa")); 1731 assert!(!TagName::is_indexable_name("1")); 1732 assert_eq!(TagValue::new("").as_str(), ""); 1733 } 1734 1735 #[test] 1736 fn unsigned_event_exposes_nostr_event_shape() { 1737 let pubkey = PublicKeyHex::new(&"1".repeat(PublicKeyHex::HEX_LENGTH)).expect("pubkey"); 1738 let tag = Tag::from_parts("p", &["peer"]).expect("tag"); 1739 let event = UnsignedEvent::new( 1740 pubkey.clone(), 1741 UnixTimestamp::new(1_714_124_433), 1742 Kind::new(1).expect("kind"), 1743 vec![tag.clone()], 1744 "hello", 1745 ); 1746 1747 assert_eq!(event.pubkey(), &pubkey); 1748 assert_eq!(event.created_at().as_u64(), 1_714_124_433); 1749 assert_eq!(event.kind().as_u32(), 1); 1750 assert_eq!(event.tags(), &[tag]); 1751 assert_eq!(event.content(), "hello"); 1752 assert!(format!("{event:?}").contains("UnsignedEvent")); 1753 } 1754 1755 #[test] 1756 fn signed_event_wraps_id_unsigned_shape_and_signature() { 1757 let id = EventId::new(&"a".repeat(EventId::HEX_LENGTH)).expect("id"); 1758 let sig = SignatureHex::new(&"b".repeat(SignatureHex::HEX_LENGTH)).expect("sig"); 1759 let unsigned = UnsignedEvent::new( 1760 PublicKeyHex::new(&"c".repeat(PublicKeyHex::HEX_LENGTH)).expect("pubkey"), 1761 UnixTimestamp::new(1), 1762 Kind::new(1).expect("kind"), 1763 Vec::new(), 1764 "", 1765 ); 1766 let event = Event::new(id.clone(), unsigned.clone(), sig.clone()); 1767 1768 assert_eq!(event.id(), &id); 1769 assert_eq!(event.unsigned(), &unsigned); 1770 assert_eq!(event.sig(), &sig); 1771 assert_eq!(event.clone(), Event::new(id, unsigned, sig)); 1772 assert!(format!("{event:?}").contains("Event")); 1773 } 1774 1775 #[test] 1776 fn raw_event_json_rejects_empty_input_and_preserves_bytes() { 1777 assert_eq!( 1778 RawEventJson::new("").expect_err("empty").to_string(), 1779 "raw event JSON must not be empty" 1780 ); 1781 let raw = RawEventJson::new("{\"kind\":1}").expect("raw"); 1782 1783 assert_eq!(raw.as_str(), "{\"kind\":1}"); 1784 assert_eq!(raw.clone().into_string(), "{\"kind\":1}"); 1785 assert_eq!(format!("{raw:?}"), "RawEventJson(\"{\\\"kind\\\":1}\")"); 1786 } 1787 1788 #[test] 1789 fn event_shape_errors_have_stable_messages() { 1790 let missing = EventShapeError::missing_field("pubkey"); 1791 let invalid = EventShapeError::invalid_field("kind", "must be unsigned integer"); 1792 1793 assert_eq!(missing.to_string(), "event field `pubkey` is missing"); 1794 assert_eq!( 1795 invalid.to_string(), 1796 "event field `kind` is invalid: must be unsigned integer" 1797 ); 1798 assert_eq!( 1799 format!("{missing:?}"), 1800 "EventShapeError { message: \"event field `pubkey` is missing\" }" 1801 ); 1802 } 1803 1804 #[test] 1805 fn parse_event_json_builds_typed_event_shape() { 1806 let raw = RawEventJson::new(&event_json("a", "b", 1, tags_json())).expect("raw"); 1807 let event = parse_event_json(&raw).expect("event"); 1808 1809 assert_eq!(event.id().as_str(), "a".repeat(EventId::HEX_LENGTH)); 1810 assert_eq!( 1811 event.unsigned().pubkey().as_str(), 1812 "1".repeat(PublicKeyHex::HEX_LENGTH) 1813 ); 1814 assert_eq!(event.unsigned().created_at().as_u64(), 1_714_124_433); 1815 assert_eq!(event.unsigned().kind().as_u32(), 1); 1816 assert_eq!(event.unsigned().tags().len(), 2); 1817 assert_eq!(event.unsigned().content(), "hello"); 1818 assert_eq!(event.sig().as_str(), "b".repeat(SignatureHex::HEX_LENGTH)); 1819 } 1820 1821 #[test] 1822 fn parse_client_message_accepts_event_auth_req_count_and_close() { 1823 let event_payload = event_json("a", "b", 1, tags_json()); 1824 let auth_event_json = event_json("c", "d", 22242, "[]"); 1825 let event_message = format!("[\"EVENT\",{event_payload}]"); 1826 let auth_message = format!("[\"AUTH\",{auth_event_json}]"); 1827 let req_message = "[\"REQ\",\"sub-a\",{\"ids\":[\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"]},{\"kinds\":[1]}]"; 1828 let count_message = "[\"COUNT\",\"sub-a\",{\"kinds\":[1]}]"; 1829 let close_message = "[\"CLOSE\",\"sub-a\"]"; 1830 let event = 1831 parse_event_json(&RawEventJson::new(&event_payload).expect("raw")).expect("event"); 1832 let auth_event = 1833 parse_event_json(&RawEventJson::new(&auth_event_json).expect("raw")).expect("auth"); 1834 1835 assert_eq!( 1836 parse_client_message(&event_message), 1837 Ok(ClientMessage::Event(event)) 1838 ); 1839 assert_eq!( 1840 parse_client_message(&auth_message), 1841 Ok(ClientMessage::Auth(auth_event)) 1842 ); 1843 assert_eq!( 1844 parse_client_message(req_message), 1845 Ok(ClientMessage::Req { 1846 subscription_id: SubscriptionId::new("sub-a").expect("sub"), 1847 filters: vec![ 1848 filter_from_value(&serde_json::json!({"ids":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"]})).expect("ids"), 1849 filter_from_value(&serde_json::json!({"kinds":[1]})).expect("kinds") 1850 ] 1851 }) 1852 ); 1853 assert_eq!( 1854 parse_client_message(count_message), 1855 Ok(ClientMessage::Count { 1856 subscription_id: SubscriptionId::new("sub-a").expect("sub"), 1857 filters: vec![filter_from_value(&serde_json::json!({"kinds":[1]})).expect("kinds")] 1858 }) 1859 ); 1860 assert_eq!( 1861 parse_client_message(close_message).expect("close"), 1862 ClientMessage::Close(SubscriptionId::new("sub-a").expect("sub")) 1863 ); 1864 } 1865 1866 #[test] 1867 fn parser_rejects_duplicate_json_object_fields() { 1868 let duplicate_filter_field = 1869 parse_client_message(r#"["REQ","sub-a",{"limit":1,"limit":2,"kinds":[1]}]"#) 1870 .expect_err("duplicate filter field"); 1871 let duplicate_event_field = parse_event_json( 1872 &RawEventJson::new(&format!( 1873 r#"{{"id":"{}","pubkey":"{}","created_at":1714124433,"kind":1,"tags":[],"content":"one","content":"two","sig":"{}"}}"#, 1874 "a".repeat(EventId::HEX_LENGTH), 1875 "1".repeat(PublicKeyHex::HEX_LENGTH), 1876 "b".repeat(SignatureHex::HEX_LENGTH) 1877 )) 1878 .expect("raw"), 1879 ) 1880 .expect_err("duplicate event field") 1881 .to_string(); 1882 1883 assert!( 1884 duplicate_filter_field.contains("duplicate object field `limit`"), 1885 "{duplicate_filter_field}" 1886 ); 1887 assert!( 1888 duplicate_event_field.contains("duplicate object field `content`"), 1889 "{duplicate_event_field}" 1890 ); 1891 } 1892 1893 #[test] 1894 fn nip01_client_and_relay_message_conformance_vectors_are_exact() { 1895 let event_payload = event_json("a", "b", 1, tags_json()); 1896 let event = 1897 parse_event_json(&RawEventJson::new(&event_payload).expect("raw")).expect("event"); 1898 let subscription_id = SubscriptionId::new("sub-vector").expect("sub"); 1899 let event_id = EventId::new(&"c".repeat(EventId::HEX_LENGTH)).expect("id"); 1900 let client_vectors = [ 1901 ( 1902 format!("[\"EVENT\",{event_payload}]"), 1903 ClientMessage::Event(event.clone()), 1904 ), 1905 ( 1906 format!("[\"AUTH\",{event_payload}]"), 1907 ClientMessage::Auth(event.clone()), 1908 ), 1909 ( 1910 "[\"REQ\",\"sub-vector\",{\"kinds\":[1]}]".to_owned(), 1911 ClientMessage::Req { 1912 subscription_id: subscription_id.clone(), 1913 filters: vec![ 1914 filter_from_value(&serde_json::json!({"kinds":[1]})).expect("filter"), 1915 ], 1916 }, 1917 ), 1918 ( 1919 "[\"COUNT\",\"sub-vector\",{\"kinds\":[1]}]".to_owned(), 1920 ClientMessage::Count { 1921 subscription_id: subscription_id.clone(), 1922 filters: vec![ 1923 filter_from_value(&serde_json::json!({"kinds":[1]})).expect("filter"), 1924 ], 1925 }, 1926 ), 1927 ( 1928 "[\"CLOSE\",\"sub-vector\"]".to_owned(), 1929 ClientMessage::Close(subscription_id.clone()), 1930 ), 1931 ]; 1932 for (raw, expected) in client_vectors { 1933 assert_eq!(parse_client_message(&raw), Ok(expected)); 1934 } 1935 let relay_vectors = [ 1936 ( 1937 RelayMessage::Event { 1938 subscription_id: subscription_id.clone(), 1939 event: event.clone(), 1940 }, 1941 serde_json::json!(["EVENT", "sub-vector", event_to_value(&event)]), 1942 ), 1943 ( 1944 RelayMessage::Ok { 1945 event_id: event_id.clone(), 1946 accepted: true, 1947 message: String::new(), 1948 }, 1949 serde_json::json!(["OK", event_id.as_str(), true, ""]), 1950 ), 1951 ( 1952 RelayMessage::Eose(subscription_id.clone()), 1953 serde_json::json!(["EOSE", "sub-vector"]), 1954 ), 1955 ( 1956 RelayMessage::Closed { 1957 subscription_id: subscription_id.clone(), 1958 message: "unsupported: search filters are not supported".to_owned(), 1959 }, 1960 serde_json::json!([ 1961 "CLOSED", 1962 "sub-vector", 1963 "unsupported: search filters are not supported" 1964 ]), 1965 ), 1966 ( 1967 RelayMessage::Count { 1968 subscription_id: subscription_id.clone(), 1969 count: 3, 1970 hll: None, 1971 }, 1972 serde_json::json!(["COUNT", "sub-vector", {"count": 3}]), 1973 ), 1974 ( 1975 RelayMessage::Notice("invalid: bad envelope".to_owned()), 1976 serde_json::json!(["NOTICE", "invalid: bad envelope"]), 1977 ), 1978 ( 1979 RelayMessage::Auth("challenge".to_owned()), 1980 serde_json::json!(["AUTH", "challenge"]), 1981 ), 1982 ]; 1983 for (message, expected) in relay_vectors { 1984 assert_eq!(relay_message_to_value(&message), expected); 1985 assert_eq!( 1986 serde_json::from_str::<serde_json::Value>(&message.encode()).expect("encoded"), 1987 expected 1988 ); 1989 } 1990 } 1991 1992 #[test] 1993 fn protocol_conformance_fixtures_cover_dense_envelopes_and_parser_stress() { 1994 let event_payload = event_json( 1995 "a", 1996 "b", 1997 30_023, 1998 r#"[["e","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","wss://relay.example","root"],["e","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"],["p","2222222222222222222222222222222222222222222222222222222222222222"],["h","Farm"],["emoji","🌱"]]"#, 1999 ); 2000 let parsed_event = 2001 parse_client_message(&format!("[\"EVENT\",{event_payload}]")).expect("event envelope"); 2002 let ClientMessage::Event(event) = parsed_event else { 2003 panic!("expected event") 2004 }; 2005 assert_eq!(event.unsigned().kind().as_u32(), 30_023); 2006 assert_eq!(event.unsigned().tags().len(), 5); 2007 2008 let req = serde_json::json!([ 2009 "REQ", 2010 "sub-dense", 2011 { 2012 "ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], 2013 "authors": ["1111111111111111111111111111111111111111111111111111111111111111"], 2014 "kinds": [1, 30023], 2015 "#e": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], 2016 "#p": ["2222222222222222222222222222222222222222222222222222222222222222"], 2017 "#h": ["Farm"], 2018 "since": 0, 2019 "until": 4102444800_u64, 2020 "limit": 500, 2021 "search": "radroots" 2022 }, 2023 { 2024 "#t": ["radroots", "market", "farm"], 2025 "limit": 1 2026 } 2027 ]) 2028 .to_string(); 2029 let ClientMessage::Req { 2030 subscription_id, 2031 filters, 2032 } = parse_client_message(&req).expect("req") 2033 else { 2034 panic!("expected req") 2035 }; 2036 assert_eq!(subscription_id.as_str(), "sub-dense"); 2037 assert_eq!(filters.len(), 2); 2038 assert_eq!(filters[0].kinds().len(), 2); 2039 assert_eq!(filters[0].tag_filters().len(), 3); 2040 assert_eq!(filters[1].limit(), Some(1)); 2041 2042 let count = serde_json::json!([ 2043 "COUNT", 2044 "sub-count", 2045 {"#h": ["Farm"], "kinds": [1], "limit": 1} 2046 ]) 2047 .to_string(); 2048 let ClientMessage::Count { 2049 subscription_id, 2050 filters, 2051 } = parse_client_message(&count).expect("count") 2052 else { 2053 panic!("expected count") 2054 }; 2055 assert_eq!(subscription_id.as_str(), "sub-count"); 2056 assert_eq!(filters.len(), 1); 2057 assert_eq!(filters[0].limit(), Some(1)); 2058 2059 for raw in [ 2060 "", 2061 "[", 2062 "[\"REQ\",\"sub\",{}", 2063 "[\"REQ\",\"sub\",{\"#h\":[{}]}]", 2064 "[\"REQ\",\"sub\",{\"ids\":[\"short\"]}]", 2065 "[\"COUNT\",\"sub\",{\"kinds\":[4294967296]}]", 2066 "[\"COUNT\",1,{}]", 2067 "[\"EVENT\",{\"tags\":[[1]]}]", 2068 "[\"AUTH\",{\"kind\":\"bad\"}]", 2069 "[\"CLOSE\",\"\"]", 2070 ] { 2071 std::panic::catch_unwind(|| { 2072 assert!(parse_client_message(raw).is_err()); 2073 }) 2074 .expect("parser must not panic"); 2075 } 2076 } 2077 2078 #[test] 2079 fn parser_preserves_addressable_events_and_weird_tag_shapes() { 2080 let p_tag = "2".repeat(PublicKeyHex::HEX_LENGTH); 2081 let event_payload = format!( 2082 r#"{{"id":"{}","pubkey":"{}","created_at":1714124433,"kind":30023,"tags":[["emoji","🌱","https://example.invalid/seed.png"],["d","market-stall"],["d","shadow"],["é","not-indexed"],["h","Farm","extra"],["p","{}","wss://relay.example","mention"]],"content":"addressable 🌱","sig":"{}"}}"#, 2083 "a".repeat(EventId::HEX_LENGTH), 2084 "1".repeat(PublicKeyHex::HEX_LENGTH), 2085 p_tag, 2086 "b".repeat(SignatureHex::HEX_LENGTH) 2087 ); 2088 let event = 2089 parse_event_json(&RawEventJson::new(&event_payload).expect("raw")).expect("event"); 2090 let coordinate = AddressCoordinate::from_event(&event) 2091 .expect("coordinate") 2092 .expect("addressable"); 2093 let filter = filter_from_value(&serde_json::json!({ 2094 "#d": ["market-stall"], 2095 "#h": ["Farm"], 2096 "#p": [p_tag], 2097 "limit": u64::MAX 2098 })) 2099 .expect("filter"); 2100 2101 assert_eq!(event.unsigned().tags().len(), 6); 2102 assert_eq!( 2103 event.unsigned().tags()[0].values(), 2104 &[ 2105 "emoji".to_owned(), 2106 "🌱".to_owned(), 2107 "https://example.invalid/seed.png".to_owned() 2108 ] 2109 ); 2110 assert_eq!(event.unsigned().tags()[3].indexed_pair(), None); 2111 assert_eq!(coordinate.d().as_str(), "market-stall"); 2112 assert_eq!(filter.limit(), Some(u64::MAX)); 2113 assert!(filter.matches(&event)); 2114 assert_eq!( 2115 filter_from_value(&serde_json::json!({"#é": ["value"]})) 2116 .expect_err("non ascii tag filter"), 2117 "filter field `#é` is invalid: tag name must be a single ASCII letter" 2118 ); 2119 } 2120 2121 #[test] 2122 fn parse_client_message_rejects_malformed_envelopes() { 2123 assert!( 2124 parse_client_message("{") 2125 .expect_err("json") 2126 .starts_with("client message JSON is invalid") 2127 ); 2128 assert_eq!( 2129 parse_client_message("{}").expect_err("object"), 2130 "client message must be an array" 2131 ); 2132 assert_eq!( 2133 parse_client_message("[]").expect_err("empty"), 2134 "client message command is missing" 2135 ); 2136 assert_eq!( 2137 parse_client_message("[1]").expect_err("command type"), 2138 "client message command must be a string" 2139 ); 2140 assert_eq!( 2141 parse_client_message("[\"BOGUS\"]").expect_err("unsupported"), 2142 "client message command `BOGUS` is unsupported" 2143 ); 2144 assert_eq!( 2145 parse_client_message("[\"EVENT\"]").expect_err("event length"), 2146 "EVENT client message must contain exactly 2 elements" 2147 ); 2148 assert_eq!( 2149 parse_client_message("[\"EVENT\",{}]").expect_err("event shape"), 2150 "event field `id` is missing" 2151 ); 2152 assert_eq!( 2153 parse_client_message("[\"AUTH\"]").expect_err("auth length"), 2154 "AUTH client message must contain exactly 2 elements" 2155 ); 2156 assert_eq!( 2157 parse_client_message("[\"AUTH\",{}]").expect_err("auth shape"), 2158 "event field `id` is missing" 2159 ); 2160 assert_eq!( 2161 parse_client_message("[\"REQ\",\"sub-a\"]").expect_err("req length"), 2162 "REQ client message must contain a subscription id and filters" 2163 ); 2164 assert_eq!( 2165 parse_client_message("[\"REQ\",1,{}]").expect_err("req sub type"), 2166 "REQ subscription id must be a string" 2167 ); 2168 assert_eq!( 2169 parse_client_message("[\"REQ\",\"\",{}]").expect_err("req sub empty"), 2170 "subscription id must not be empty" 2171 ); 2172 assert_eq!( 2173 parse_client_message("[\"REQ\",\"sub-a\",1]").expect_err("req filter"), 2174 "filter must be a JSON object" 2175 ); 2176 assert_eq!( 2177 parse_client_message("[\"COUNT\"]").expect_err("count length"), 2178 "COUNT client message must contain a subscription id and filters" 2179 ); 2180 assert_eq!( 2181 parse_client_message("[\"COUNT\",1,{}]").expect_err("count sub type"), 2182 "COUNT subscription id must be a string" 2183 ); 2184 assert_eq!( 2185 parse_client_message("[\"COUNT\",\"\",{}]").expect_err("count sub empty"), 2186 "subscription id must not be empty" 2187 ); 2188 assert_eq!( 2189 parse_client_message("[\"COUNT\",\"sub-a\",1]").expect_err("count filter"), 2190 "filter must be a JSON object" 2191 ); 2192 assert_eq!( 2193 parse_client_message("[\"CLOSE\"]").expect_err("close length"), 2194 "CLOSE client message must contain exactly 2 elements" 2195 ); 2196 assert_eq!( 2197 parse_client_message("[\"CLOSE\",1]").expect_err("close sub type"), 2198 "CLOSE subscription id must be a string" 2199 ); 2200 } 2201 2202 #[test] 2203 fn malformed_client_message_corpus_is_rejected_or_parsed_without_panic() { 2204 let valid_event = event_json("a", "b", 1, tags_json()); 2205 let long_sub = "x".repeat(SubscriptionId::MAX_LENGTH + 1); 2206 let oversized_kind = u64::from(u32::MAX) + 1; 2207 let corpus = [ 2208 String::new(), 2209 "[".to_owned(), 2210 "null".to_owned(), 2211 "[null]".to_owned(), 2212 "[\"EVENT\",null]".to_owned(), 2213 format!("[\"EVENT\",{valid_event},{}]", serde_json::json!({})), 2214 "[\"REQ\",{},{}]".to_owned(), 2215 "[\"REQ\",\"sub\",{\"ids\":[]}]".to_owned(), 2216 "[\"REQ\",\"sub\",{\"ids\":[1]}]".to_owned(), 2217 "[\"REQ\",\"sub\",{\"#aa\":[\"value\"]}]".to_owned(), 2218 format!("[\"REQ\",\"sub\",{{\"kinds\":[{oversized_kind}]}}]"), 2219 format!("[\"REQ\",\"{long_sub}\",{{}}]"), 2220 "[\"COUNT\",\"sub\",{\"authors\":[\"BAD\"]}]".to_owned(), 2221 "[\"COUNT\",\"sub\",{\"unknown\":true}]".to_owned(), 2222 "[\"CLOSE\",\"sub\",{}]".to_owned(), 2223 "[\"AUTH\",[]]".to_owned(), 2224 "[\"NOTICE\",\"not a client command\"]".to_owned(), 2225 ]; 2226 for raw in corpus { 2227 std::panic::catch_unwind(|| { 2228 let _ = parse_client_message(&raw); 2229 }) 2230 .expect("parser must not panic"); 2231 } 2232 } 2233 2234 #[test] 2235 fn parse_event_json_rejects_invalid_event_shapes() { 2236 assert_eq!( 2237 parse_event_json(&RawEventJson::new("{").expect("raw")) 2238 .expect_err("invalid json") 2239 .to_string(), 2240 "event field `event` is invalid: invalid JSON: EOF while parsing an object at line 1 column 1" 2241 ); 2242 assert_eq!( 2243 event_from_value(&serde_json::json!(1)) 2244 .expect_err("not object") 2245 .to_string(), 2246 "event field `event` is invalid: must be an object" 2247 ); 2248 assert_eq!( 2249 event_from_value(&serde_json::json!({})) 2250 .expect_err("missing id") 2251 .to_string(), 2252 "event field `id` is missing" 2253 ); 2254 assert_eq!( 2255 event_from_value(&event_value_without("created_at")) 2256 .expect_err("missing created_at") 2257 .to_string(), 2258 "event field `created_at` is missing" 2259 ); 2260 assert_eq!( 2261 event_from_value(&event_value_with("id", serde_json::json!(1))) 2262 .expect_err("id type") 2263 .to_string(), 2264 "event field `id` is invalid: must be a string" 2265 ); 2266 assert_eq!( 2267 event_from_value(&event_value_with("id", serde_json::json!("bad"))) 2268 .expect_err("id scalar") 2269 .to_string(), 2270 "event field `id` is invalid: event id must be 64 characters, got 3" 2271 ); 2272 assert_eq!( 2273 event_from_value(&event_value_with("pubkey", serde_json::json!("bad"))) 2274 .expect_err("pubkey scalar") 2275 .to_string(), 2276 "event field `pubkey` is invalid: public key must be 64 characters, got 3" 2277 ); 2278 assert_eq!( 2279 event_from_value(&event_value_with("created_at", serde_json::json!("now"))) 2280 .expect_err("created type") 2281 .to_string(), 2282 "event field `created_at` is invalid: must be an unsigned integer" 2283 ); 2284 assert_eq!( 2285 event_from_value(&event_value_with( 2286 "kind", 2287 serde_json::json!(u64::from(u32::MAX) + 1) 2288 )) 2289 .expect_err("kind range") 2290 .to_string(), 2291 format!( 2292 "event field `kind` is invalid: kind must fit in u32, got {}", 2293 u64::from(u32::MAX) + 1 2294 ) 2295 ); 2296 assert_eq!( 2297 event_from_value(&event_value_with("tags", serde_json::json!(1))) 2298 .expect_err("tags type") 2299 .to_string(), 2300 "event field `tags` is invalid: must be an array" 2301 ); 2302 assert_eq!( 2303 event_from_value(&event_value_with("tags", serde_json::json!([1]))) 2304 .expect_err("tag type") 2305 .to_string(), 2306 "event field `tags` is invalid: tag must be an array" 2307 ); 2308 assert_eq!( 2309 event_from_value(&event_value_with("tags", serde_json::json!([[1]]))) 2310 .expect_err("tag part type") 2311 .to_string(), 2312 "event field `tags` is invalid: tag elements must be strings" 2313 ); 2314 assert_eq!( 2315 event_from_value(&event_value_with("tags", serde_json::json!([[]]))) 2316 .expect_err("tag empty") 2317 .to_string(), 2318 "event field `tags` is invalid: tag must not be empty" 2319 ); 2320 assert_eq!( 2321 event_from_value(&event_value_with("content", serde_json::json!(1))) 2322 .expect_err("content type") 2323 .to_string(), 2324 "event field `content` is invalid: must be a string" 2325 ); 2326 assert_eq!( 2327 event_from_value(&event_value_with("sig", serde_json::json!("bad"))) 2328 .expect_err("sig scalar") 2329 .to_string(), 2330 "event field `sig` is invalid: signature must be 128 characters, got 3" 2331 ); 2332 } 2333 2334 #[test] 2335 fn filter_model_parses_core_fields_and_matches_events() { 2336 let event_tag = "e".repeat(EventId::HEX_LENGTH); 2337 let event = event_for_filter(&event_tag, 50, 1); 2338 let filter = filter_from_value(&serde_json::json!({ 2339 "ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], 2340 "authors": ["1111111111111111111111111111111111111111111111111111111111111111"], 2341 "kinds": [1], 2342 "#e": [event_tag], 2343 "#p": ["1111111111111111111111111111111111111111111111111111111111111111"], 2344 "#t": ["radroots"], 2345 "since": 40, 2346 "until": 60, 2347 "limit": 5, 2348 "search": "fresh carrots" 2349 })) 2350 .expect("filter"); 2351 2352 assert_eq!(filter.ids()[0].as_str(), "a".repeat(EventId::HEX_LENGTH)); 2353 assert_eq!( 2354 filter.authors()[0].as_str(), 2355 "1".repeat(PublicKeyHex::HEX_LENGTH) 2356 ); 2357 assert_eq!(filter.kinds()[0].as_u32(), 1); 2358 assert_eq!(filter.since().expect("since").as_u64(), 40); 2359 assert_eq!(filter.until().expect("until").as_u64(), 60); 2360 assert_eq!(filter.limit(), Some(5)); 2361 assert_eq!(filter.search(), Some("fresh carrots")); 2362 assert_eq!( 2363 filter 2364 .tag_filters() 2365 .get(&TagName::new("t").expect("tag")) 2366 .expect("tag")[0] 2367 .as_str(), 2368 "radroots" 2369 ); 2370 assert!(filter.matches(&event)); 2371 assert!(Filter::empty().matches(&event)); 2372 assert_eq!(Filter::empty().limit(), None); 2373 assert_eq!(Filter::empty().search(), None); 2374 let without_limit = filter.without_limit(); 2375 assert_eq!(without_limit.limit(), None); 2376 assert_eq!(without_limit.search(), filter.search()); 2377 assert!(without_limit.matches(&event)); 2378 let with_limit = without_limit.with_limit(2); 2379 assert_eq!(with_limit.limit(), Some(2)); 2380 assert_eq!(with_limit.search(), filter.search()); 2381 assert!(with_limit.matches(&event)); 2382 assert_eq!( 2383 filter_from_value(&filter_to_value(&filter)).expect("encoded"), 2384 filter 2385 ); 2386 } 2387 2388 #[test] 2389 fn filter_from_parts_builds_validated_filter_components() { 2390 let name = TagName::new("t").expect("tag name"); 2391 let value = TagValue::new("market"); 2392 let filter = Filter::from_parts( 2393 vec![EventId::new(&"a".repeat(EventId::HEX_LENGTH)).expect("id")], 2394 vec![PublicKeyHex::new(&"b".repeat(PublicKeyHex::HEX_LENGTH)).expect("author")], 2395 vec![Kind::new(1).expect("kind")], 2396 BTreeMap::from([(name.clone(), vec![value.clone()])]), 2397 Some(UnixTimestamp::new(10)), 2398 Some(UnixTimestamp::new(20)), 2399 Some(30), 2400 Some("carrots".to_owned()), 2401 ) 2402 .expect("filter"); 2403 2404 assert_eq!(filter.ids().len(), 1); 2405 assert_eq!(filter.authors().len(), 1); 2406 assert_eq!(filter.kinds(), &[Kind::new(1).expect("kind")]); 2407 assert_eq!(filter.tag_filters().get(&name), Some(&vec![value])); 2408 assert_eq!(filter.since(), Some(UnixTimestamp::new(10))); 2409 assert_eq!(filter.until(), Some(UnixTimestamp::new(20))); 2410 assert_eq!(filter.limit(), Some(30)); 2411 assert_eq!(filter.search(), Some("carrots")); 2412 assert_eq!( 2413 Filter::from_parts( 2414 Vec::new(), 2415 Vec::new(), 2416 Vec::new(), 2417 BTreeMap::from([(TagName::new("ab").expect("tag"), Vec::new())]), 2418 None, 2419 None, 2420 None, 2421 None 2422 ) 2423 .expect_err("invalid tag"), 2424 "filter field `#ab` is invalid: tag name must be a single ASCII letter" 2425 ); 2426 } 2427 2428 #[test] 2429 fn filter_complete_semantics_are_exact_id_only() { 2430 assert!( 2431 filter_from_value(&serde_json::json!({ 2432 "ids": ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"] 2433 })) 2434 .expect("id filter") 2435 .is_complete() 2436 ); 2437 assert!( 2438 !filter_from_value(&serde_json::json!({"kinds": [1]})) 2439 .expect("kind filter") 2440 .is_complete() 2441 ); 2442 assert!(!Filter::empty().is_complete()); 2443 } 2444 2445 #[test] 2446 fn filter_model_rejects_non_matching_events() { 2447 let event_tag = "e".repeat(EventId::HEX_LENGTH); 2448 let event = event_for_filter(&event_tag, 50, 1); 2449 2450 assert!( 2451 !filter_from_value(&serde_json::json!({ 2452 "ids": ["bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"] 2453 })) 2454 .expect("id filter") 2455 .matches(&event) 2456 ); 2457 assert!( 2458 !filter_from_value(&serde_json::json!({ 2459 "authors": ["2222222222222222222222222222222222222222222222222222222222222222"] 2460 })) 2461 .expect("author filter") 2462 .matches(&event) 2463 ); 2464 assert!( 2465 !filter_from_value(&serde_json::json!({"kinds": [2]})) 2466 .expect("kind filter") 2467 .matches(&event) 2468 ); 2469 assert!( 2470 !filter_from_value(&serde_json::json!({"since": 51})) 2471 .expect("since filter") 2472 .matches(&event) 2473 ); 2474 assert!( 2475 !filter_from_value(&serde_json::json!({"until": 49})) 2476 .expect("until filter") 2477 .matches(&event) 2478 ); 2479 assert!( 2480 !filter_from_value(&serde_json::json!({"#e": [ 2481 "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 2482 ]})) 2483 .expect("tag filter") 2484 .matches(&event) 2485 ); 2486 } 2487 2488 #[test] 2489 fn filter_model_rejects_invalid_filter_shapes() { 2490 assert_eq!( 2491 filter_from_value(&serde_json::json!(1)).expect_err("object"), 2492 "filter must be a JSON object" 2493 ); 2494 assert_eq!( 2495 filter_from_value(&serde_json::json!({"ids": 1})).expect_err("ids array"), 2496 "filter field `ids` must be a non-empty array" 2497 ); 2498 assert_eq!( 2499 filter_from_value(&serde_json::json!({"ids": []})).expect_err("ids empty"), 2500 "filter field `ids` must be a non-empty array" 2501 ); 2502 assert_eq!( 2503 filter_from_value(&serde_json::json!({"ids": [1]})).expect_err("ids string"), 2504 "filter field `ids` values must be strings" 2505 ); 2506 assert_eq!( 2507 filter_from_value(&serde_json::json!({"ids": ["bad"]})).expect_err("ids hex"), 2508 "filter field `ids` is invalid: event id must be 64 characters, got 3" 2509 ); 2510 assert_eq!( 2511 filter_from_value(&serde_json::json!({"authors": ["bad"]})).expect_err("author hex"), 2512 "filter field `authors` is invalid: public key must be 64 characters, got 3" 2513 ); 2514 assert_eq!( 2515 filter_from_value(&serde_json::json!({"authors": 1})).expect_err("authors array"), 2516 "filter field `authors` must be a non-empty array" 2517 ); 2518 assert_eq!( 2519 filter_from_value(&serde_json::json!({"authors": []})).expect_err("authors empty"), 2520 "filter field `authors` must be a non-empty array" 2521 ); 2522 assert_eq!( 2523 filter_from_value(&serde_json::json!({"authors": [1]})).expect_err("authors string"), 2524 "filter field `authors` values must be strings" 2525 ); 2526 assert_eq!( 2527 filter_from_value(&serde_json::json!({"kinds": 1})).expect_err("kinds array"), 2528 "filter field `kinds` must be a non-empty array" 2529 ); 2530 assert_eq!( 2531 filter_from_value(&serde_json::json!({"kinds": []})).expect_err("kinds empty"), 2532 "filter field `kinds` must be a non-empty array" 2533 ); 2534 assert_eq!( 2535 filter_from_value(&serde_json::json!({"kinds": ["one"]})).expect_err("kind integer"), 2536 "filter field `kinds` values must be unsigned integers" 2537 ); 2538 assert_eq!( 2539 filter_from_value(&serde_json::json!({"kinds": [u64::from(u32::MAX) + 1]})) 2540 .expect_err("kind range"), 2541 format!( 2542 "filter field `kinds` is invalid: kind must fit in u32, got {}", 2543 u64::from(u32::MAX) + 1 2544 ) 2545 ); 2546 assert_eq!( 2547 filter_from_value(&serde_json::json!({"since": "now"})).expect_err("since"), 2548 "filter field `since` must be an unsigned integer" 2549 ); 2550 assert_eq!( 2551 filter_from_value(&serde_json::json!({"until": "then"})).expect_err("until"), 2552 "filter field `until` must be an unsigned integer" 2553 ); 2554 assert_eq!( 2555 filter_from_value(&serde_json::json!({"limit": "ten"})).expect_err("limit"), 2556 "filter field `limit` must be an unsigned integer" 2557 ); 2558 assert_eq!( 2559 filter_from_value(&serde_json::json!({"search": 1})).expect_err("search"), 2560 "filter field `search` must be a string" 2561 ); 2562 assert_eq!( 2563 filter_from_value(&serde_json::json!({"#": ["value"]})).expect_err("tag empty"), 2564 "filter field `#` is invalid: tag name must not be empty" 2565 ); 2566 assert_eq!( 2567 filter_from_value(&serde_json::json!({"#aa": ["value"]})).expect_err("tag long"), 2568 "filter field `#aa` is invalid: tag name must be a single ASCII letter" 2569 ); 2570 assert_eq!( 2571 filter_from_value(&serde_json::json!({"#t": 1})).expect_err("tag array"), 2572 "filter field `#t` must be a non-empty array" 2573 ); 2574 assert_eq!( 2575 filter_from_value(&serde_json::json!({"#t": []})).expect_err("tag empty array"), 2576 "filter field `#t` must be a non-empty array" 2577 ); 2578 assert_eq!( 2579 filter_from_value(&serde_json::json!({"#t": [1]})).expect_err("tag string"), 2580 "filter field `#t` values must be strings" 2581 ); 2582 assert_eq!( 2583 filter_from_value(&serde_json::json!({"#e": ["bad"]})).expect_err("tag event id"), 2584 "filter field `#e` is invalid: event id must be 64 characters, got 3" 2585 ); 2586 assert_eq!( 2587 filter_from_value(&serde_json::json!({"#p": ["bad"]})).expect_err("tag pubkey"), 2588 "filter field `#p` is invalid: public key must be 64 characters, got 3" 2589 ); 2590 assert_eq!( 2591 filter_from_value(&serde_json::json!({"unknown": true})).expect_err("unknown"), 2592 "filter field `unknown` is unsupported" 2593 ); 2594 } 2595 2596 #[test] 2597 fn relay_message_encoder_emits_nip01_and_nip42_messages() { 2598 let event = parse_event_json( 2599 &RawEventJson::new(&event_json("a", "b", 1, tags_json())).expect("raw"), 2600 ) 2601 .expect("event"); 2602 let subscription_id = SubscriptionId::new("sub-a").expect("sub"); 2603 let accepted_id = EventId::new(&"c".repeat(EventId::HEX_LENGTH)).expect("id"); 2604 let rejected_id = EventId::new(&"d".repeat(EventId::HEX_LENGTH)).expect("id"); 2605 2606 assert_eq!( 2607 relay_message_to_value(&RelayMessage::Event { 2608 subscription_id: subscription_id.clone(), 2609 event: event.clone() 2610 }), 2611 serde_json::json!(["EVENT", "sub-a", event_to_value(&event)]) 2612 ); 2613 assert_eq!( 2614 serde_json::from_str::<serde_json::Value>(&encode_relay_message(&RelayMessage::Ok { 2615 event_id: accepted_id, 2616 accepted: true, 2617 message: String::new() 2618 })) 2619 .expect("ok"), 2620 serde_json::json!([ 2621 "OK", 2622 "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", 2623 true, 2624 "" 2625 ]) 2626 ); 2627 assert_eq!( 2628 serde_json::from_str::<serde_json::Value>( 2629 &RelayMessage::Ok { 2630 event_id: rejected_id, 2631 accepted: false, 2632 message: "invalid: event id mismatch".to_owned() 2633 } 2634 .encode() 2635 ) 2636 .expect("rejected"), 2637 serde_json::json!([ 2638 "OK", 2639 "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", 2640 false, 2641 "invalid: event id mismatch" 2642 ]) 2643 ); 2644 assert_eq!( 2645 relay_message_to_value(&RelayMessage::Eose(subscription_id.clone())), 2646 serde_json::json!(["EOSE", "sub-a"]) 2647 ); 2648 assert_eq!( 2649 relay_message_to_value(&RelayMessage::Closed { 2650 subscription_id: subscription_id.clone(), 2651 message: "unsupported: filter contains unknown elements".to_owned() 2652 }), 2653 serde_json::json!([ 2654 "CLOSED", 2655 "sub-a", 2656 "unsupported: filter contains unknown elements" 2657 ]) 2658 ); 2659 assert_eq!( 2660 relay_message_to_value(&RelayMessage::Count { 2661 subscription_id, 2662 count: 7, 2663 hll: None 2664 }), 2665 serde_json::json!(["COUNT", "sub-a", {"count": 7}]) 2666 ); 2667 assert_eq!( 2668 relay_message_to_value(&RelayMessage::Notice("maintenance window".to_owned())), 2669 serde_json::json!(["NOTICE", "maintenance window"]) 2670 ); 2671 assert_eq!( 2672 relay_message_to_value(&RelayMessage::Auth("challenge-a".to_owned())), 2673 serde_json::json!(["AUTH", "challenge-a"]) 2674 ); 2675 } 2676 2677 #[test] 2678 fn relay_message_encoder_emits_negentropy_messages() { 2679 let subscription_id = SubscriptionId::new("neg-sub").expect("sub"); 2680 2681 assert_eq!( 2682 relay_message_to_value(&RelayMessage::NegErr { 2683 subscription_id: subscription_id.clone(), 2684 message: "blocked: Negentropy sync is disabled".to_owned() 2685 }), 2686 serde_json::json!(["NEG-ERR", "neg-sub", "blocked: Negentropy sync is disabled"]) 2687 ); 2688 assert_eq!( 2689 relay_message_to_value(&RelayMessage::NegMsg { 2690 subscription_id, 2691 message: "00ff".to_owned() 2692 }), 2693 serde_json::json!(["NEG-MSG", "neg-sub", "00ff"]) 2694 ); 2695 } 2696 2697 #[test] 2698 fn relay_message_encoder_emits_count_hll_when_present() { 2699 let subscription_id = SubscriptionId::new("count-hll").expect("sub"); 2700 let hll = "0a".repeat(256); 2701 2702 assert_eq!( 2703 relay_message_to_value(&RelayMessage::Count { 2704 subscription_id, 2705 count: 42, 2706 hll: Some(hll.clone()) 2707 }), 2708 serde_json::json!(["COUNT", "count-hll", {"count": 42, "hll": hll}]) 2709 ); 2710 } 2711 2712 #[test] 2713 fn event_to_value_round_trips_with_event_parser() { 2714 let event = parse_event_json( 2715 &RawEventJson::new(&event_json("e", "f", 30402, tags_json())).expect("raw"), 2716 ) 2717 .expect("event"); 2718 2719 assert_eq!( 2720 event_from_value(&event_to_value(&event)).expect("parsed"), 2721 event 2722 ); 2723 } 2724 2725 #[test] 2726 fn canonical_event_json_serializes_empty_content_and_tags() { 2727 let event = unsigned_event(Vec::new(), ""); 2728 2729 assert_eq!( 2730 event.canonical_json(), 2731 include_str!("../tests/fixtures/canonical_empty_event.json").trim_end() 2732 ); 2733 assert_eq!(canonical_event_json(&event), event.canonical_json()); 2734 } 2735 2736 #[test] 2737 fn canonical_event_json_serializes_escaped_content() { 2738 let event = unsigned_event( 2739 vec![Tag::from_parts("alt", &["quote"]).expect("tag")], 2740 "quote \" slash \\ newline\n", 2741 ); 2742 2743 assert_eq!( 2744 event.canonical_json(), 2745 include_str!("../tests/fixtures/canonical_escaped_event.json").trim_end() 2746 ); 2747 } 2748 2749 #[test] 2750 fn canonical_event_json_serializes_unicode_content() { 2751 let event = unsigned_event( 2752 vec![Tag::from_parts("t", &["radroots"]).expect("tag")], 2753 "radroots 🌱 café", 2754 ); 2755 2756 assert_eq!( 2757 event.canonical_json(), 2758 include_str!("../tests/fixtures/canonical_unicode_event.json").trim_end() 2759 ); 2760 } 2761 2762 #[test] 2763 fn canonical_event_json_preserves_repeated_tags() { 2764 let event = unsigned_event( 2765 vec![ 2766 Tag::from_parts("e", &["one"]).expect("first e"), 2767 Tag::from_parts("e", &["two"]).expect("second e"), 2768 Tag::from_parts("p", &["peer", "wss://relay.example"]).expect("p"), 2769 ], 2770 "with repeated tags", 2771 ); 2772 2773 assert_eq!( 2774 event.canonical_json(), 2775 include_str!("../tests/fixtures/canonical_repeated_tags_event.json").trim_end() 2776 ); 2777 } 2778 2779 #[test] 2780 fn canonical_event_json_preserves_large_tags_without_shape_drift() { 2781 let tags = (0..64) 2782 .map(|index| Tag::from_parts("t", &[&format!("topic-{index}")]).expect("tag")) 2783 .collect::<Vec<_>>(); 2784 let event = unsigned_event(tags, "large tag set"); 2785 let value = 2786 serde_json::from_str::<serde_json::Value>(&event.canonical_json()).expect("json"); 2787 2788 assert_eq!(value[0], 0); 2789 assert_eq!(value[4].as_array().expect("tags").len(), 64); 2790 assert_eq!(value[5], "large tag set"); 2791 } 2792 2793 #[test] 2794 fn parser_fuzz_style_scalar_corpus_is_total() { 2795 let ids = ["0", "a", "f", "g", "A", "00", "ff"]; 2796 for id_seed in ids { 2797 let id = id_seed.repeat(EventId::HEX_LENGTH); 2798 let raw = serde_json::json!([ 2799 "EVENT", 2800 { 2801 "id": id, 2802 "pubkey": "1".repeat(PublicKeyHex::HEX_LENGTH), 2803 "created_at": 1_714_124_433_u64, 2804 "kind": 1, 2805 "tags": [["e", "a".repeat(EventId::HEX_LENGTH)]], 2806 "content": "fuzz", 2807 "sig": "2".repeat(SignatureHex::HEX_LENGTH) 2808 } 2809 ]) 2810 .to_string(); 2811 std::panic::catch_unwind(|| { 2812 let _ = parse_client_message(&raw); 2813 }) 2814 .expect("event parser must not panic"); 2815 } 2816 for size in [1_usize, 2, 4, 8, 16, 32, 64, 128] { 2817 let values = (0..size) 2818 .map(|index| serde_json::Value::String(format!("topic-{index}"))) 2819 .collect::<Vec<_>>(); 2820 let raw = serde_json::json!(["REQ", "fuzz", {"#t": values}]).to_string(); 2821 assert!(parse_client_message(&raw).is_ok()); 2822 } 2823 } 2824 2825 fn unsigned_event(tags: Vec<Tag>, content: &str) -> UnsignedEvent { 2826 UnsignedEvent::new( 2827 PublicKeyHex::new(&"1".repeat(PublicKeyHex::HEX_LENGTH)).expect("pubkey"), 2828 UnixTimestamp::new(1_714_124_433), 2829 Kind::new(1).expect("kind"), 2830 tags, 2831 content, 2832 ) 2833 } 2834 2835 fn event_for_filter(event_tag: &str, created_at: u64, kind: u64) -> Event { 2836 Event::new( 2837 EventId::new(&"a".repeat(EventId::HEX_LENGTH)).expect("id"), 2838 UnsignedEvent::new( 2839 PublicKeyHex::new(&"1".repeat(PublicKeyHex::HEX_LENGTH)).expect("pubkey"), 2840 UnixTimestamp::new(created_at), 2841 Kind::new(kind).expect("kind"), 2842 vec![ 2843 Tag::from_parts("e", &[event_tag]).expect("event tag"), 2844 Tag::from_parts( 2845 "p", 2846 &["1111111111111111111111111111111111111111111111111111111111111111"], 2847 ) 2848 .expect("pubkey tag"), 2849 Tag::from_parts("t", &["radroots"]).expect("topic tag"), 2850 ], 2851 "hello", 2852 ), 2853 SignatureHex::new(&"b".repeat(SignatureHex::HEX_LENGTH)).expect("sig"), 2854 ) 2855 } 2856 2857 fn addressable_event(d: &str, kind: u64) -> Event { 2858 Event::new( 2859 EventId::new(&"a".repeat(EventId::HEX_LENGTH)).expect("id"), 2860 UnsignedEvent::new( 2861 PublicKeyHex::new(&"1".repeat(PublicKeyHex::HEX_LENGTH)).expect("pubkey"), 2862 UnixTimestamp::new(50), 2863 Kind::new(kind).expect("kind"), 2864 vec![ 2865 Tag::from_parts( 2866 "p", 2867 &["1111111111111111111111111111111111111111111111111111111111111111"], 2868 ) 2869 .expect("pubkey tag"), 2870 Tag::from_parts("d", &[d]).expect("d tag"), 2871 ], 2872 "hello", 2873 ), 2874 SignatureHex::new(&"b".repeat(SignatureHex::HEX_LENGTH)).expect("sig"), 2875 ) 2876 } 2877 2878 fn addressable_event_without_d() -> Event { 2879 Event::new( 2880 EventId::new(&"a".repeat(EventId::HEX_LENGTH)).expect("id"), 2881 UnsignedEvent::new( 2882 PublicKeyHex::new(&"1".repeat(PublicKeyHex::HEX_LENGTH)).expect("pubkey"), 2883 UnixTimestamp::new(50), 2884 Kind::new(30_402).expect("kind"), 2885 vec![ 2886 Tag::from_parts( 2887 "p", 2888 &["1111111111111111111111111111111111111111111111111111111111111111"], 2889 ) 2890 .expect("pubkey tag"), 2891 ], 2892 "hello", 2893 ), 2894 SignatureHex::new(&"b".repeat(SignatureHex::HEX_LENGTH)).expect("sig"), 2895 ) 2896 } 2897 2898 fn tags_json() -> &'static str { 2899 "[[\"e\",\"root\"],[\"p\",\"peer\",\"wss://relay.example\"]]" 2900 } 2901 2902 fn event_json(id_char: &str, sig_char: &str, kind: u64, tags: &str) -> String { 2903 format!( 2904 "{{\"id\":\"{}\",\"pubkey\":\"{}\",\"created_at\":1714124433,\"kind\":{},\"tags\":{},\"content\":\"hello\",\"sig\":\"{}\"}}", 2905 id_char.repeat(EventId::HEX_LENGTH), 2906 "1".repeat(PublicKeyHex::HEX_LENGTH), 2907 kind, 2908 tags, 2909 sig_char.repeat(SignatureHex::HEX_LENGTH) 2910 ) 2911 } 2912 2913 fn event_value_with(field: &'static str, value: serde_json::Value) -> serde_json::Value { 2914 let mut event = 2915 serde_json::from_str::<serde_json::Value>(&event_json("a", "b", 1, tags_json())) 2916 .expect("event value"); 2917 event 2918 .as_object_mut() 2919 .expect("event object") 2920 .insert(field.to_owned(), value); 2921 event 2922 } 2923 2924 fn event_value_without(field: &'static str) -> serde_json::Value { 2925 let mut event = 2926 serde_json::from_str::<serde_json::Value>(&event_json("a", "b", 1, tags_json())) 2927 .expect("event value"); 2928 event.as_object_mut().expect("event object").remove(field); 2929 event 2930 } 2931 }