lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

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(&timestamp).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 }