xmtp_api_d14n/protocol/traits/
envelopes.rs

1//! Traits representing un-processed (extracted) and processed (extracted) protobuf types
2use chrono::Utc;
3use xmtp_common::{MaybeSend, MaybeSync};
4use xmtp_proto::types::{Cursor, OrphanedEnvelope};
5
6use crate::protocol::{
7    BytesExtractor, CursorExtractor, DependsOnExtractor, MlsDataExtractor, OrphanExtractor,
8    TimestampExtractor,
9};
10
11use super::*;
12/// An low-level envelope from the network gRPC interface
13/*
14* WARN: ProtocolEnvelope implementation for a Vec<T>
15* should be avoided, since it may cause Envelope
16* to implicitly act on a collection when a single envelope is expected.
17* Theres a way to seal this trait implementation to
18* avoid external implementations which should be done.
19*/
20pub trait ProtocolEnvelope<'env>: std::fmt::Debug + MaybeSend + MaybeSync {
21    type Nested<'a>
22    where
23        Self: 'a;
24    fn accept<V: EnvelopeVisitor<'env>>(&self, visitor: &mut V) -> Result<(), EnvelopeError>
25    where
26        EnvelopeError: From<<V as EnvelopeVisitor<'env>>::Error>;
27    fn get_nested(&self) -> Result<Self::Nested<'_>, ConversionError>;
28}
29
30//TODO: https://github.com/xmtp/libxmtp/issues/2691
31// will improve usage of timestamp/sorting/resolution, so that earlier
32// networking layers do not deserialize more than necessary.
33/// Represents a Single High-Level Envelope
34/// An [`Envelope`] is a [`ProtocolEnvelope`] with some [`Extractor`](super::Extractor)
35/// applied to it.
36/// Envelopes received from the network generally must adhere
37/// to the form of envelopes expected in d14n [Node2Node Protocol](https://github.com/xmtp/XIPs/blob/main/XIPs/xip-49-decentralized-backend.md#321-originator-node).
38/// In the network, Node operators are responseible for maintaining
39/// a [`Cursor`](xmtp_proto::types::Cursor) per envelope.
40/// Likewise, Clients form the [`ClientEnvelope`] according to the [Client Node2Node Protocol](https://github.com/xmtp/XIPs/blob/main/XIPs/xip-49-decentralized-backend.md#332-envelopes)
41/// Client envelopes maintain a payload/topic with MLS and Client-specific duties.
42pub trait Envelope<'env>: std::fmt::Debug + MaybeSend + MaybeSync {
43    /// get the oriignal envelope bytes
44    fn bytes(&self) -> Result<Vec<u8>, EnvelopeError>;
45    /// get the orphaned version of this envelope
46    fn orphan(&self) -> Result<OrphanedEnvelope, EnvelopeError>;
47    /// Extract the topic for this envelope
48    fn topic(&self) -> Result<Topic, EnvelopeError>;
49    /// Extract the cursor for this envelope
50    fn cursor(&self) -> Result<Cursor, EnvelopeError>;
51    /// get the envelope this depends on.
52    fn depends_on(&self) -> Result<Option<GlobalCursor>, EnvelopeError>;
53    /// Extract the payload for this envelope
54    fn payload(&self) -> Result<Payload, EnvelopeError>;
55    /// the Mls Data bytes as a sha256 hash
56    fn sha256_hash(&self) -> Result<Vec<u8>, EnvelopeError>;
57    /// Get the timestamp of this envelope
58    fn timestamp(&self) -> Option<chrono::DateTime<Utc>>;
59    /// Extract the client envelope (envelope containing message payload & AAD, if any) for this
60    /// envelope.
61    fn client_envelope(&self) -> Result<ClientEnvelope, EnvelopeError>;
62    /// Try to get a group message from this Envelope
63    fn group_message(&self) -> Result<Option<GroupMessage>, EnvelopeError>;
64    /// Try to get a welcome message
65    fn welcome_message(&self) -> Result<Option<WelcomeMessage>, EnvelopeError>;
66}
67
68// Allows us to call these methods straight on the protobuf types without any
69// parsing/matching first.
70impl<'env, T> Envelope<'env> for T
71where
72    T: ProtocolEnvelope<'env>,
73{
74    fn bytes(&self) -> Result<Vec<u8>, EnvelopeError> {
75        let mut extractor = BytesExtractor::new();
76        self.accept(&mut extractor)?;
77        Ok(extractor.get())
78    }
79
80    fn orphan(&self) -> Result<OrphanedEnvelope, EnvelopeError> {
81        let mut extractor = OrphanExtractor::default();
82        self.accept(&mut extractor)?;
83        Ok(extractor.get()?)
84    }
85
86    fn topic(&self) -> Result<Topic, EnvelopeError> {
87        let mut extractor = TopicExtractor::new();
88        self.accept(&mut extractor)?;
89        Ok(extractor.get()?)
90    }
91
92    fn cursor(&self) -> Result<Cursor, EnvelopeError> {
93        let mut extractor = CursorExtractor::new();
94        self.accept(&mut extractor)?;
95        Ok(extractor.get()?)
96    }
97
98    fn depends_on(&self) -> Result<Option<GlobalCursor>, EnvelopeError> {
99        let mut extractor = DependsOnExtractor::default();
100        self.accept(&mut extractor)?;
101        Ok(extractor.get())
102    }
103
104    fn payload(&self) -> Result<Payload, EnvelopeError> {
105        let mut extractor = PayloadExtractor::new();
106        self.accept(&mut extractor)?;
107        Ok(extractor.get()?)
108    }
109
110    fn sha256_hash(&self) -> Result<Vec<u8>, EnvelopeError> {
111        let mut extractor = MlsDataExtractor::new();
112        self.accept(&mut extractor)?;
113        Ok(extractor.get_sha256()?)
114    }
115
116    // TODO: Currently the only "unexpected" way for this to fail
117    // would be a deserialization error, or if timestamp is
118    // > 2262 A.D.
119    // Deserializing/failing earlier: https://github.com/xmtp/libxmtp/issues/2691
120    // would encode more invariants into these extractor types
121    fn timestamp(&self) -> Option<chrono::DateTime<Utc>> {
122        let mut extractor = TimestampExtractor::default();
123        self.accept(&mut extractor).ok()?;
124        extractor.maybe_get()
125    }
126
127    fn client_envelope(&self) -> Result<ClientEnvelope, EnvelopeError> {
128        // ensures we only recurse the proto data structure once.
129        let mut extractor = (
130            TopicExtractor::new(),
131            PayloadExtractor::new(),
132            DependsOnExtractor::default(),
133        );
134        self.accept(&mut extractor)?;
135        let topic = extractor.0.get().map_err(ExtractionError::from)?;
136        let payload = extractor.1.get().map_err(ExtractionError::from)?;
137        let depends_on = extractor.2.get();
138        Ok(ClientEnvelope {
139            aad: Some(AuthenticatedData {
140                target_topic: topic.into(),
141                depends_on: depends_on.map(Into::into),
142            }),
143            payload: Some(payload),
144        })
145    }
146
147    fn group_message(&self) -> Result<Option<GroupMessage>, EnvelopeError> {
148        let mut extractor = (
149            V3GroupMessageExtractor::default(),
150            GroupMessageExtractor::default(),
151        );
152        self.accept(&mut extractor)?;
153        if let Ok(Some(v3)) = extractor.0.get() {
154            return Ok(Some(v3));
155        }
156
157        match extractor.1.get() {
158            Ok(v) => return Ok(Some(v)),
159            Err(ExtractionError::Conversion(ConversionError::Missing { .. })) => (),
160            Err(e) => return Err(e.into()),
161        }
162
163        Ok(None)
164    }
165
166    fn welcome_message(&self) -> Result<Option<WelcomeMessage>, EnvelopeError> {
167        let mut extractor = (
168            V3WelcomeMessageExtractor::default(),
169            WelcomeMessageExtractor::default(),
170        );
171        self.accept(&mut extractor)?;
172        match extractor.0.get() {
173            Ok(v) => return Ok(Some(v)),
174            Err(ConversionError::Builder(_)) | Err(ConversionError::Missing { .. }) => (),
175            Err(e) => return Err(e.into()),
176        }
177
178        match extractor.1.get() {
179            Ok(v) => return Ok(Some(v)),
180            Err(ExtractionError::Conversion(ConversionError::Builder(_)))
181            | Err(ExtractionError::Conversion(ConversionError::Missing { .. })) => (),
182            Err(e) => return Err(e.into()),
183        }
184
185        Ok(None)
186    }
187}
188
189impl<'env, T> ProtocolEnvelope<'env> for &T
190where
191    T: ProtocolEnvelope<'env>,
192{
193    type Nested<'a>
194        = <T as ProtocolEnvelope<'env>>::Nested<'a>
195    where
196        Self: 'a;
197
198    fn accept<V: EnvelopeVisitor<'env>>(&self, visitor: &mut V) -> Result<(), EnvelopeError>
199    where
200        EnvelopeError: From<<V as EnvelopeVisitor<'env>>::Error>,
201    {
202        <T as ProtocolEnvelope<'env>>::accept(self, visitor)
203    }
204
205    fn get_nested(&self) -> Result<Self::Nested<'_>, ConversionError> {
206        <T as ProtocolEnvelope<'env>>::get_nested(self)
207    }
208}