envelope.rs (29355B)
1 #![forbid(unsafe_code)] 2 3 #[path = "event_head.rs"] 4 pub mod event_head; 5 #[path = "kinds.rs"] 6 pub mod kind; 7 8 #[cfg(all(not(feature = "std"), not(test)))] 9 use alloc::{string::String, vec::Vec}; 10 11 #[cfg(any(feature = "std", test))] 12 use std::{string::String, vec::Vec}; 13 14 use crate::id::{EventId, EventSignature, ParseError, parse_public_key}; 15 use crate::wire::v1::{ 16 DEFAULT_CONTENT_MAX_BYTES, DEFAULT_TAG_ELEMENT_MAX_BYTES, DEFAULT_TAG_MAX_COUNT, 17 DEFAULT_TAG_TOTAL_ELEMENT_MAX_COUNT, DEFAULT_TAG_TOTAL_MAX_BYTES, Nip01EventWire, 18 }; 19 use core::fmt; 20 use radroots_identity::PublicKey; 21 22 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 23 pub struct EventTimestamp(u64); 24 25 impl EventTimestamp { 26 pub const fn new(value: u64) -> Self { 27 Self(value) 28 } 29 30 #[inline] 31 pub const fn as_u64(self) -> u64 { 32 self.0 33 } 34 } 35 36 impl From<u64> for EventTimestamp { 37 #[inline] 38 fn from(value: u64) -> Self { 39 Self::new(value) 40 } 41 } 42 43 #[cfg(any(feature = "serde", test))] 44 impl serde::Serialize for EventTimestamp { 45 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 46 where 47 S: serde::Serializer, 48 { 49 serializer.serialize_u64(self.0) 50 } 51 } 52 53 #[cfg(any(feature = "serde", test))] 54 impl<'de> serde::Deserialize<'de> for EventTimestamp { 55 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 56 where 57 D: serde::Deserializer<'de>, 58 { 59 let value = u64::deserialize(deserializer)?; 60 Ok(Self::new(value)) 61 } 62 } 63 64 #[cfg(all(test, feature = "std"))] 65 impl dto_bindgen::Dto for EventTimestamp { 66 fn describe(ctx: &mut dto_bindgen::__private::DescribeCtx) -> dto_bindgen::__private::TypeRef { 67 <u64 as dto_bindgen::Dto>::describe(ctx) 68 } 69 } 70 71 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 72 pub struct EventKind(u32); 73 74 impl EventKind { 75 pub const fn new(value: u32) -> Self { 76 Self(value) 77 } 78 79 #[inline] 80 pub const fn as_u32(self) -> u32 { 81 self.0 82 } 83 84 pub const fn class(self) -> EventKindClass { 85 match self.0 { 86 0 | 3 => EventKindClass::Replaceable, 87 10_000..=19_999 => EventKindClass::Replaceable, 88 20_000..=29_999 => EventKindClass::Ephemeral, 89 30_000..=39_999 => EventKindClass::Addressable, 90 _ => EventKindClass::Regular, 91 } 92 } 93 } 94 95 impl From<u32> for EventKind { 96 #[inline] 97 fn from(value: u32) -> Self { 98 Self::new(value) 99 } 100 } 101 102 #[cfg(any(feature = "serde", test))] 103 impl serde::Serialize for EventKind { 104 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 105 where 106 S: serde::Serializer, 107 { 108 serializer.serialize_u32(self.0) 109 } 110 } 111 112 #[cfg(any(feature = "serde", test))] 113 impl<'de> serde::Deserialize<'de> for EventKind { 114 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 115 where 116 D: serde::Deserializer<'de>, 117 { 118 let value = u32::deserialize(deserializer)?; 119 Ok(Self::new(value)) 120 } 121 } 122 123 #[cfg(all(test, feature = "std"))] 124 impl dto_bindgen::Dto for EventKind { 125 fn describe(ctx: &mut dto_bindgen::__private::DescribeCtx) -> dto_bindgen::__private::TypeRef { 126 <u32 as dto_bindgen::Dto>::describe(ctx) 127 } 128 } 129 130 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 131 pub enum EventKindClass { 132 Regular, 133 Replaceable, 134 Ephemeral, 135 Addressable, 136 } 137 138 #[derive(Clone, Debug, PartialEq, Eq)] 139 pub enum EventEnvelopeError { 140 InvalidId(ParseError), 141 InvalidAuthor(ParseError), 142 InvalidSignature(ParseError), 143 NonCanonicalId, 144 NonCanonicalAuthor, 145 NonCanonicalSignature, 146 EmptyTag { 147 index: usize, 148 }, 149 EmptyTagKey { 150 index: usize, 151 }, 152 ControlCharacterTagKey { 153 index: usize, 154 }, 155 ContentTooLarge { 156 max: usize, 157 actual: usize, 158 }, 159 TooManyTags { 160 max: usize, 161 actual: usize, 162 }, 163 TooManyTagElements { 164 max: usize, 165 actual: usize, 166 }, 167 TagElementTooLarge { 168 tag_index: usize, 169 element_index: usize, 170 max: usize, 171 actual: usize, 172 }, 173 TagsTooLarge { 174 max: usize, 175 actual: usize, 176 }, 177 } 178 179 impl fmt::Display for EventEnvelopeError { 180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 181 match self { 182 Self::InvalidId(error) => write!(f, "event envelope id is invalid: {error}"), 183 Self::InvalidAuthor(error) => write!(f, "event envelope author is invalid: {error}"), 184 Self::InvalidSignature(error) => { 185 write!(f, "event envelope signature is invalid: {error}") 186 } 187 Self::NonCanonicalId => write!(f, "event envelope id must be canonical lowercase hex"), 188 Self::NonCanonicalAuthor => { 189 write!(f, "event envelope author must be canonical lowercase hex") 190 } 191 Self::NonCanonicalSignature => { 192 write!( 193 f, 194 "event envelope signature must be canonical lowercase hex" 195 ) 196 } 197 Self::EmptyTag { index } => write!(f, "event envelope tag {index} is empty"), 198 Self::EmptyTagKey { index } => write!(f, "event envelope tag {index} key is empty"), 199 Self::ControlCharacterTagKey { index } => { 200 write!( 201 f, 202 "event envelope tag {index} key contains a control character" 203 ) 204 } 205 Self::ContentTooLarge { max, actual } => { 206 write!( 207 f, 208 "event envelope content size {actual} exceeds {max} bytes" 209 ) 210 } 211 Self::TooManyTags { max, actual } => { 212 write!(f, "event envelope tag count {actual} exceeds {max}") 213 } 214 Self::TooManyTagElements { max, actual } => { 215 write!(f, "event envelope tag element count {actual} exceeds {max}") 216 } 217 Self::TagElementTooLarge { 218 tag_index, 219 element_index, 220 max, 221 actual, 222 } => write!( 223 f, 224 "event envelope tag {tag_index} element {element_index} size {actual} exceeds {max} bytes" 225 ), 226 Self::TagsTooLarge { max, actual } => { 227 write!(f, "event envelope tag bytes {actual} exceed {max}") 228 } 229 } 230 } 231 } 232 233 #[cfg(feature = "std")] 234 impl std::error::Error for EventEnvelopeError {} 235 236 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 237 pub struct EventEnvelopeLimits { 238 pub max_content_bytes: usize, 239 pub max_tag_count: usize, 240 pub max_total_tag_elements: usize, 241 pub max_tag_element_bytes: usize, 242 pub max_total_tag_bytes: usize, 243 } 244 245 impl Default for EventEnvelopeLimits { 246 fn default() -> Self { 247 Self { 248 max_content_bytes: DEFAULT_CONTENT_MAX_BYTES, 249 max_tag_count: DEFAULT_TAG_MAX_COUNT, 250 max_total_tag_elements: DEFAULT_TAG_TOTAL_ELEMENT_MAX_COUNT, 251 max_tag_element_bytes: DEFAULT_TAG_ELEMENT_MAX_BYTES, 252 max_total_tag_bytes: DEFAULT_TAG_TOTAL_MAX_BYTES, 253 } 254 } 255 } 256 257 #[derive(Clone, Debug, PartialEq, Eq)] 258 pub struct EventTag(Vec<String>); 259 260 impl EventTag { 261 pub fn new(index: usize, values: Vec<String>) -> Result<Self, EventEnvelopeError> { 262 Self::new_with_limits(index, values, EventEnvelopeLimits::default()) 263 } 264 265 pub fn new_with_limits( 266 index: usize, 267 values: Vec<String>, 268 limits: EventEnvelopeLimits, 269 ) -> Result<Self, EventEnvelopeError> { 270 validate_tag(index, &values)?; 271 let total_tag_elements = values.len(); 272 if total_tag_elements > limits.max_total_tag_elements { 273 return Err(EventEnvelopeError::TooManyTagElements { 274 max: limits.max_total_tag_elements, 275 actual: total_tag_elements, 276 }); 277 } 278 let total_bytes = validate_tag_elements(index, &values, limits)?; 279 if total_bytes > limits.max_total_tag_bytes { 280 return Err(EventEnvelopeError::TagsTooLarge { 281 max: limits.max_total_tag_bytes, 282 actual: total_bytes, 283 }); 284 } 285 Ok(Self(values)) 286 } 287 288 #[inline] 289 pub fn as_slice(&self) -> &[String] { 290 self.0.as_slice() 291 } 292 293 #[inline] 294 pub fn into_vec(self) -> Vec<String> { 295 self.0 296 } 297 } 298 299 #[cfg(any(feature = "serde", test))] 300 impl serde::Serialize for EventTag { 301 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 302 where 303 S: serde::Serializer, 304 { 305 self.0.serialize(serializer) 306 } 307 } 308 309 #[cfg(any(feature = "serde", test))] 310 impl<'de> serde::Deserialize<'de> for EventTag { 311 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 312 where 313 D: serde::Deserializer<'de>, 314 { 315 let values = Vec::<String>::deserialize(deserializer)?; 316 Self::new(0, values).map_err(serde::de::Error::custom) 317 } 318 } 319 320 #[derive(Clone, Debug, PartialEq, Eq)] 321 pub struct EventTags(Vec<EventTag>); 322 323 impl EventTags { 324 pub fn new(values: Vec<Vec<String>>) -> Result<Self, EventEnvelopeError> { 325 Self::new_with_limits(values, EventEnvelopeLimits::default()) 326 } 327 328 pub fn new_with_limits( 329 values: Vec<Vec<String>>, 330 limits: EventEnvelopeLimits, 331 ) -> Result<Self, EventEnvelopeError> { 332 let tag_count = values.len(); 333 if tag_count > limits.max_tag_count { 334 return Err(EventEnvelopeError::TooManyTags { 335 max: limits.max_tag_count, 336 actual: tag_count, 337 }); 338 } 339 let total_tag_elements = values 340 .iter() 341 .fold(0usize, |total, tag| total.saturating_add(tag.len())); 342 if total_tag_elements > limits.max_total_tag_elements { 343 return Err(EventEnvelopeError::TooManyTagElements { 344 max: limits.max_total_tag_elements, 345 actual: total_tag_elements, 346 }); 347 } 348 let mut tags = Vec::with_capacity(values.len()); 349 let mut total_tag_bytes = 0usize; 350 for (index, tag) in values.into_iter().enumerate() { 351 validate_tag(index, &tag)?; 352 total_tag_bytes = 353 total_tag_bytes.saturating_add(validate_tag_elements(index, &tag, limits)?); 354 if total_tag_bytes > limits.max_total_tag_bytes { 355 return Err(EventEnvelopeError::TagsTooLarge { 356 max: limits.max_total_tag_bytes, 357 actual: total_tag_bytes, 358 }); 359 } 360 tags.push(EventTag(tag)); 361 } 362 Ok(Self(tags)) 363 } 364 365 #[inline] 366 pub fn as_slice(&self) -> &[EventTag] { 367 self.0.as_slice() 368 } 369 370 pub fn to_vec(&self) -> Vec<Vec<String>> { 371 self.0.iter().map(|tag| tag.as_slice().to_vec()).collect() 372 } 373 374 #[inline] 375 pub fn into_vec(self) -> Vec<Vec<String>> { 376 self.0.into_iter().map(EventTag::into_vec).collect() 377 } 378 379 #[inline] 380 pub fn len(&self) -> usize { 381 self.0.len() 382 } 383 384 #[inline] 385 pub fn is_empty(&self) -> bool { 386 self.0.is_empty() 387 } 388 } 389 390 #[cfg(any(feature = "serde", test))] 391 impl serde::Serialize for EventTags { 392 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 393 where 394 S: serde::Serializer, 395 { 396 self.to_vec().serialize(serializer) 397 } 398 } 399 400 #[cfg(any(feature = "serde", test))] 401 impl<'de> serde::Deserialize<'de> for EventTags { 402 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 403 where 404 D: serde::Deserializer<'de>, 405 { 406 let values = Vec::<Vec<String>>::deserialize(deserializer)?; 407 Self::new(values).map_err(serde::de::Error::custom) 408 } 409 } 410 411 #[cfg(all(test, feature = "std"))] 412 impl dto_bindgen::Dto for EventTags { 413 fn describe(ctx: &mut dto_bindgen::__private::DescribeCtx) -> dto_bindgen::__private::TypeRef { 414 <Vec<Vec<String>> as dto_bindgen::Dto>::describe(ctx) 415 } 416 } 417 418 #[derive(Clone, Debug, PartialEq, Eq)] 419 pub struct EventEnvelopeParts { 420 pub id: String, 421 pub author: String, 422 pub created_at: u64, 423 pub kind: u32, 424 pub tags: Vec<Vec<String>>, 425 pub content: String, 426 pub sig: String, 427 } 428 429 #[cfg_attr( 430 any(feature = "serde", test), 431 derive(serde::Serialize, serde::Deserialize) 432 )] 433 #[cfg_attr(all(test, feature = "std"), derive(dto_bindgen::Dto))] 434 #[cfg_attr(all(test, feature = "std"), dto(export))] 435 #[cfg_attr(all(test, feature = "std"), dto(ts(name = "RadrootsEventEnvelopeDto")))] 436 #[derive(Clone, Debug, PartialEq, Eq)] 437 pub struct EventEnvelope { 438 id: EventId, 439 #[cfg_attr(all(test, feature = "std"), dto(as = "string"))] 440 author: PublicKey, 441 #[cfg_attr(all(test, feature = "std"), dto(int = "json_number"))] 442 created_at: EventTimestamp, 443 kind: EventKind, 444 tags: EventTags, 445 content: String, 446 sig: EventSignature, 447 } 448 449 impl EventEnvelope { 450 pub fn new(parts: EventEnvelopeParts) -> Result<Self, EventEnvelopeError> { 451 Self::new_with_limits(parts, EventEnvelopeLimits::default()) 452 } 453 454 pub fn new_with_limits( 455 parts: EventEnvelopeParts, 456 limits: EventEnvelopeLimits, 457 ) -> Result<Self, EventEnvelopeError> { 458 let id = EventId::parse(parts.id.as_str()).map_err(EventEnvelopeError::InvalidId)?; 459 if id.to_hex() != parts.id { 460 return Err(EventEnvelopeError::NonCanonicalId); 461 } 462 let author = 463 parse_public_key(parts.author.as_str()).map_err(EventEnvelopeError::InvalidAuthor)?; 464 if author.to_hex() != parts.author { 465 return Err(EventEnvelopeError::NonCanonicalAuthor); 466 } 467 let sig = EventSignature::parse(parts.sig.as_str()) 468 .map_err(EventEnvelopeError::InvalidSignature)?; 469 if sig.to_hex() != parts.sig { 470 return Err(EventEnvelopeError::NonCanonicalSignature); 471 } 472 let content_len = parts.content.len(); 473 if content_len > limits.max_content_bytes { 474 return Err(EventEnvelopeError::ContentTooLarge { 475 max: limits.max_content_bytes, 476 actual: content_len, 477 }); 478 } 479 let tags = EventTags::new_with_limits(parts.tags, limits)?; 480 Ok(Self { 481 id, 482 author, 483 created_at: EventTimestamp::new(parts.created_at), 484 kind: EventKind::new(parts.kind), 485 tags, 486 content: parts.content, 487 sig, 488 }) 489 } 490 491 #[inline] 492 pub fn id(&self) -> &EventId { 493 &self.id 494 } 495 496 #[inline] 497 pub fn id_hex(&self) -> String { 498 self.id.to_hex() 499 } 500 501 #[inline] 502 pub fn author(&self) -> &PublicKey { 503 &self.author 504 } 505 506 #[inline] 507 pub fn created_at(&self) -> EventTimestamp { 508 self.created_at 509 } 510 511 #[inline] 512 pub fn created_at_u64(&self) -> u64 { 513 self.created_at.as_u64() 514 } 515 516 #[inline] 517 pub fn kind(&self) -> EventKind { 518 self.kind 519 } 520 521 #[inline] 522 pub fn kind_u32(&self) -> u32 { 523 self.kind.as_u32() 524 } 525 526 #[inline] 527 pub fn kind_class(&self) -> EventKindClass { 528 self.kind.class() 529 } 530 531 #[inline] 532 pub fn tags(&self) -> &EventTags { 533 &self.tags 534 } 535 536 #[inline] 537 pub fn tag_slices(&self) -> &[EventTag] { 538 self.tags.as_slice() 539 } 540 541 pub fn tags_as_vec(&self) -> Vec<Vec<String>> { 542 self.tags.to_vec() 543 } 544 545 #[inline] 546 pub fn content(&self) -> &str { 547 self.content.as_str() 548 } 549 550 #[inline] 551 pub fn sig(&self) -> &EventSignature { 552 &self.sig 553 } 554 555 #[inline] 556 pub fn signature_hex(&self) -> String { 557 self.sig.to_hex() 558 } 559 560 pub fn to_nip01_wire(&self) -> Nip01EventWire { 561 Nip01EventWire { 562 id: self.id.to_hex(), 563 pubkey: self.author.to_hex(), 564 created_at: self.created_at.as_u64(), 565 kind: self.kind.as_u32(), 566 tags: self.tags.to_vec(), 567 content: self.content.clone(), 568 sig: self.sig.to_hex(), 569 extra: Default::default(), 570 } 571 } 572 } 573 574 fn validate_tag(index: usize, values: &[String]) -> Result<(), EventEnvelopeError> { 575 let Some(key) = values.first() else { 576 return Err(EventEnvelopeError::EmptyTag { index }); 577 }; 578 if key.is_empty() { 579 return Err(EventEnvelopeError::EmptyTagKey { index }); 580 } 581 if key.chars().any(char::is_control) { 582 return Err(EventEnvelopeError::ControlCharacterTagKey { index }); 583 } 584 Ok(()) 585 } 586 587 fn validate_tag_elements( 588 tag_index: usize, 589 values: &[String], 590 limits: EventEnvelopeLimits, 591 ) -> Result<usize, EventEnvelopeError> { 592 let mut total_tag_bytes = 0usize; 593 for (element_index, value) in values.iter().enumerate() { 594 let value_len = value.len(); 595 if value_len > limits.max_tag_element_bytes { 596 return Err(EventEnvelopeError::TagElementTooLarge { 597 tag_index, 598 element_index, 599 max: limits.max_tag_element_bytes, 600 actual: value_len, 601 }); 602 } 603 total_tag_bytes = total_tag_bytes.saturating_add(value_len); 604 } 605 Ok(total_tag_bytes) 606 } 607 608 #[cfg(test)] 609 #[cfg_attr(coverage_nightly, coverage(off))] 610 mod tests { 611 use super::*; 612 613 fn hex_64(character: char) -> String { 614 crate::test_valid_hex_64(character) 615 } 616 617 fn hex_128(character: char) -> String { 618 core::iter::repeat_n(character, 128).collect() 619 } 620 621 fn event_parts() -> EventEnvelopeParts { 622 EventEnvelopeParts { 623 id: hex_64('1'), 624 author: hex_64('a'), 625 created_at: u64::from(u32::MAX) + 1, 626 kind: 30_023, 627 tags: vec![vec!["d".to_owned(), "article".to_owned()]], 628 content: "hello".to_owned(), 629 sig: hex_128('b'), 630 } 631 } 632 633 #[test] 634 fn envelope_uses_private_typed_state_and_getters() { 635 let envelope = EventEnvelope::new(event_parts()).expect("envelope"); 636 637 assert_eq!(envelope.id_hex(), hex_64('1')); 638 assert_eq!(envelope.author().to_hex(), hex_64('a')); 639 assert_eq!(envelope.created_at_u64(), u64::from(u32::MAX) + 1); 640 assert_eq!(envelope.kind_u32(), 30_023); 641 assert_eq!(envelope.kind_class(), EventKindClass::Addressable); 642 assert_eq!(envelope.content(), "hello"); 643 assert_eq!(envelope.signature_hex(), hex_128('b')); 644 assert_eq!( 645 envelope.tags_as_vec(), 646 vec![vec!["d".to_owned(), "article".to_owned()]] 647 ); 648 } 649 650 #[test] 651 fn envelope_rejects_invalid_parts() { 652 let mut parts = event_parts(); 653 parts.id = "bad".to_owned(); 654 assert!(matches!( 655 EventEnvelope::new(parts), 656 Err(EventEnvelopeError::InvalidId(_)) 657 )); 658 659 let mut parts = event_parts(); 660 parts.tags = vec![Vec::new()]; 661 assert_eq!( 662 EventEnvelope::new(parts), 663 Err(EventEnvelopeError::EmptyTag { index: 0 }) 664 ); 665 } 666 667 #[test] 668 fn kind_classifies_nip01_ranges() { 669 assert_eq!(EventKind::new(0).class(), EventKindClass::Replaceable); 670 assert_eq!(EventKind::new(1).class(), EventKindClass::Regular); 671 assert_eq!(EventKind::new(3).class(), EventKindClass::Replaceable); 672 assert_eq!(EventKind::new(9_999).class(), EventKindClass::Regular); 673 assert_eq!(EventKind::new(10_000).class(), EventKindClass::Replaceable); 674 assert_eq!(EventKind::new(19_999).class(), EventKindClass::Replaceable); 675 assert_eq!(EventKind::new(20_000).class(), EventKindClass::Ephemeral); 676 assert_eq!(EventKind::new(29_999).class(), EventKindClass::Ephemeral); 677 assert_eq!(EventKind::new(30_000).class(), EventKindClass::Addressable); 678 assert_eq!(EventKind::new(39_999).class(), EventKindClass::Addressable); 679 assert_eq!(EventKind::new(40_000).class(), EventKindClass::Regular); 680 } 681 682 #[test] 683 fn envelope_rejects_domain_budget_violations() { 684 let mut parts = event_parts(); 685 assert_eq!( 686 EventEnvelope::new_with_limits( 687 parts.clone(), 688 EventEnvelopeLimits { 689 max_content_bytes: 4, 690 ..EventEnvelopeLimits::default() 691 } 692 ), 693 Err(EventEnvelopeError::ContentTooLarge { max: 4, actual: 5 }) 694 ); 695 696 parts.tags = vec![vec!["d".to_owned()]]; 697 assert_eq!( 698 EventEnvelope::new_with_limits( 699 parts.clone(), 700 EventEnvelopeLimits { 701 max_tag_count: 0, 702 ..EventEnvelopeLimits::default() 703 } 704 ), 705 Err(EventEnvelopeError::TooManyTags { max: 0, actual: 1 }) 706 ); 707 708 parts.tags = vec![vec!["d".to_owned(), "abcd".to_owned()]]; 709 assert_eq!( 710 EventEnvelope::new_with_limits( 711 parts.clone(), 712 EventEnvelopeLimits { 713 max_total_tag_elements: 1, 714 ..EventEnvelopeLimits::default() 715 } 716 ), 717 Err(EventEnvelopeError::TooManyTagElements { max: 1, actual: 2 }) 718 ); 719 720 assert_eq!( 721 EventEnvelope::new_with_limits( 722 parts.clone(), 723 EventEnvelopeLimits { 724 max_tag_element_bytes: 3, 725 ..EventEnvelopeLimits::default() 726 } 727 ), 728 Err(EventEnvelopeError::TagElementTooLarge { 729 tag_index: 0, 730 element_index: 1, 731 max: 3, 732 actual: 4 733 }) 734 ); 735 736 parts.tags = vec![vec!["d".to_owned(), "soil".to_owned()]]; 737 assert_eq!( 738 EventEnvelope::new_with_limits( 739 parts, 740 EventEnvelopeLimits { 741 max_total_tag_bytes: 4, 742 ..EventEnvelopeLimits::default() 743 } 744 ), 745 Err(EventEnvelopeError::TagsTooLarge { max: 4, actual: 5 }) 746 ); 747 } 748 749 #[test] 750 fn envelope_accepts_exact_domain_budget_boundaries() { 751 let mut parts = event_parts(); 752 parts.content = "hello".to_owned(); 753 parts.tags = vec![vec!["d".to_owned(), "soil".to_owned()]]; 754 755 let envelope = EventEnvelope::new_with_limits( 756 parts, 757 EventEnvelopeLimits { 758 max_content_bytes: 5, 759 max_tag_count: 1, 760 max_total_tag_elements: 2, 761 max_tag_element_bytes: 4, 762 max_total_tag_bytes: 5, 763 }, 764 ) 765 .expect("envelope"); 766 767 assert_eq!(envelope.content(), "hello"); 768 assert_eq!( 769 envelope.tags_as_vec(), 770 vec![vec!["d".to_owned(), "soil".to_owned()]] 771 ); 772 } 773 774 #[test] 775 fn envelope_serializes_to_domain_shape() { 776 let envelope = EventEnvelope::new(event_parts()).expect("envelope"); 777 let encoded = serde_json::to_value(&envelope).expect("json"); 778 779 assert_eq!( 780 encoded.get("id").and_then(serde_json::Value::as_str), 781 Some(hex_64('1').as_str()) 782 ); 783 assert_eq!( 784 encoded.get("author").and_then(serde_json::Value::as_str), 785 Some(hex_64('a').as_str()) 786 ); 787 assert_eq!( 788 encoded 789 .get("created_at") 790 .and_then(serde_json::Value::as_u64), 791 Some(u64::from(u32::MAX) + 1) 792 ); 793 } 794 795 #[test] 796 #[cfg_attr(coverage_nightly, coverage(off))] 797 fn typed_envelope_api_and_error_contracts_are_complete() { 798 #[allow(dead_code)] 799 #[derive(Debug, serde::Deserialize)] 800 struct MissingTimestamp { 801 value: EventTimestamp, 802 } 803 #[allow(dead_code)] 804 #[derive(Debug, serde::Deserialize)] 805 struct MissingKind { 806 value: EventKind, 807 } 808 #[allow(dead_code)] 809 #[derive(Debug, serde::Deserialize)] 810 struct MissingTags { 811 value: EventTags, 812 } 813 814 for message in [ 815 serde_json::from_str::<MissingTimestamp>("{}") 816 .expect_err("missing timestamp") 817 .to_string(), 818 serde_json::from_str::<MissingKind>("{}") 819 .expect_err("missing kind") 820 .to_string(), 821 serde_json::from_str::<MissingTags>("{}") 822 .expect_err("missing tags") 823 .to_string(), 824 ] { 825 assert!(message.contains("missing field `value`")); 826 } 827 828 let timestamp = EventTimestamp::from(42); 829 assert_eq!(timestamp.as_u64(), 42); 830 assert_eq!( 831 serde_json::from_str::<EventTimestamp>( 832 &serde_json::to_string(×tamp).expect("timestamp json"), 833 ) 834 .expect("timestamp"), 835 timestamp 836 ); 837 let kind = EventKind::from(30_023); 838 assert_eq!(kind.as_u32(), 30_023); 839 assert_eq!( 840 serde_json::from_str::<EventKind>(&serde_json::to_string(&kind).expect("kind json"),) 841 .expect("kind"), 842 kind 843 ); 844 845 let parse_error = EventId::parse("bad").expect_err("invalid id"); 846 for error in [ 847 EventEnvelopeError::InvalidId(parse_error.clone()), 848 EventEnvelopeError::InvalidAuthor(parse_error.clone()), 849 EventEnvelopeError::InvalidSignature(parse_error), 850 EventEnvelopeError::NonCanonicalId, 851 EventEnvelopeError::NonCanonicalAuthor, 852 EventEnvelopeError::NonCanonicalSignature, 853 EventEnvelopeError::EmptyTag { index: 1 }, 854 EventEnvelopeError::EmptyTagKey { index: 1 }, 855 EventEnvelopeError::ControlCharacterTagKey { index: 1 }, 856 EventEnvelopeError::ContentTooLarge { max: 1, actual: 2 }, 857 EventEnvelopeError::TooManyTags { max: 1, actual: 2 }, 858 EventEnvelopeError::TooManyTagElements { max: 1, actual: 2 }, 859 EventEnvelopeError::TagElementTooLarge { 860 tag_index: 1, 861 element_index: 2, 862 max: 3, 863 actual: 4, 864 }, 865 EventEnvelopeError::TagsTooLarge { max: 1, actual: 2 }, 866 ] { 867 assert!(!error.to_string().is_empty()); 868 } 869 870 let tag = EventTag::new(0, vec!["t".to_owned(), "soil".to_owned()]).expect("tag"); 871 assert_eq!(tag.clone().into_vec(), vec!["t", "soil"]); 872 let tag_json = serde_json::to_string(&tag).expect("tag json"); 873 assert_eq!( 874 serde_json::from_str::<EventTag>(&tag_json).expect("tag"), 875 tag 876 ); 877 assert!(serde_json::from_str::<EventTag>("[]").is_err()); 878 assert!( 879 EventTag::new_with_limits( 880 0, 881 vec!["tag".to_owned()], 882 EventEnvelopeLimits { 883 max_total_tag_bytes: 2, 884 ..EventEnvelopeLimits::default() 885 }, 886 ) 887 .is_err() 888 ); 889 890 let empty_tags = EventTags::new(Vec::new()).expect("empty tags"); 891 assert_eq!(empty_tags.len(), 0); 892 assert!(empty_tags.is_empty()); 893 assert!(empty_tags.clone().into_vec().is_empty()); 894 let tags = EventTags::new(vec![vec!["t".to_owned(), "soil".to_owned()]]).expect("tags"); 895 let tags_json = serde_json::to_string(&tags).expect("tags json"); 896 assert_eq!( 897 serde_json::from_str::<EventTags>(&tags_json).expect("tags"), 898 tags 899 ); 900 assert!(serde_json::from_str::<EventTags>("[[]]").is_err()); 901 902 let envelope = EventEnvelope::new(event_parts()).expect("envelope"); 903 assert_eq!(envelope.id().to_hex(), envelope.id_hex()); 904 assert_eq!(envelope.author().to_hex(), hex_64('a')); 905 assert_eq!(envelope.created_at().as_u64(), envelope.created_at_u64()); 906 assert_eq!(envelope.kind().as_u32(), envelope.kind_u32()); 907 assert_eq!(envelope.tags().to_vec(), envelope.tags_as_vec()); 908 assert_eq!(envelope.tag_slices(), envelope.tags().as_slice()); 909 assert_eq!(envelope.sig().to_hex(), envelope.signature_hex()); 910 let wire = envelope.to_nip01_wire(); 911 assert_eq!(wire.id, envelope.id_hex()); 912 let encoded = serde_json::to_string(&envelope).expect("envelope json"); 913 assert_eq!( 914 serde_json::from_str::<EventEnvelope>(&encoded).expect("envelope"), 915 envelope 916 ); 917 918 for (field, value) in [ 919 ("id", hex_64('A')), 920 ("author", hex_64('A')), 921 ("sig", hex_128('B')), 922 ] { 923 let mut parts = event_parts(); 924 match field { 925 "id" => parts.id = value, 926 "author" => parts.author = value, 927 "sig" => parts.sig = value, 928 _ => unreachable!("fixture field"), 929 } 930 assert!(EventEnvelope::new(parts).is_err()); 931 } 932 for tags in [ 933 vec![vec![String::new()]], 934 vec![vec!["line\nbreak".to_owned()]], 935 ] { 936 let mut parts = event_parts(); 937 parts.tags = tags; 938 assert!(EventEnvelope::new(parts).is_err()); 939 } 940 } 941 }