Skip to main content

tuwunel_core/matrix/
pdu.rs

1//! Persistent data unit storage and federation-format utilities.
2//!
3//! The module contains the stored event representation, sequence identifiers,
4//! builders, and validation helpers. Its wire-format adapters account for
5//! room-version rules.
6
7mod builder;
8mod count;
9mod format;
10mod hashes;
11mod id;
12mod raw_id;
13#[cfg(test)]
14mod tests;
15mod unsigned;
16
17use std::cmp::Ordering;
18
19use ruma::{
20	CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId,
21	OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, UInt, UserId,
22	canonical_json::redact_in_place,
23	events::TimelineEventType,
24	room_version_rules::{RedactionRules, RoomVersionRules},
25	serde::Raw,
26};
27use serde::{Deserialize, Serialize};
28use serde_json::value::RawValue as RawJsonValue;
29use smallvec::SmallVec;
30
31pub use self::{
32	Count as PduCount, Id as PduId, Pdu as PduEvent, RawId as RawPduId,
33	builder::{Builder, Builder as PduBuilder},
34	count::Count,
35	format::{
36		check::{check_room_id, check_rules},
37		from_incoming_federation, into_outgoing_federation,
38	},
39	hashes::EventHashes as EventHash,
40	id::Id,
41	raw_id::*,
42};
43use super::{Event, ShortRoomId, StateKey};
44use crate::{Result, err};
45
46/// Stores a Matrix persistent data unit in typed form.
47///
48/// The representation retains canonical event fields used by state resolution,
49/// storage, and client serialization. Federation adapters normalize
50/// version-specific wire shapes.
51#[derive(Clone, Deserialize, Serialize, Debug)]
52pub struct Pdu {
53	/// Matrix event type.
54	///
55	/// The value is serialized under the top-level `type` field.
56	#[serde(rename = "type")]
57	pub kind: TimelineEventType,
58
59	/// Raw canonical event content.
60	///
61	/// Content remains encoded until a caller requests a typed or JSON value.
62	pub content: Content,
63
64	/// Matrix identifier assigned to this event.
65	///
66	/// Stored PDUs always carry an owned event ID, including room versions that
67	/// derive it outside the federation wire object.
68	pub event_id: OwnedEventId,
69
70	/// Matrix room containing this event.
71	///
72	/// Stored PDUs carry the room ID even when a room version omits it from a
73	/// creation event's federation representation.
74	pub room_id: OwnedRoomId,
75
76	/// Matrix user who sent the event.
77	///
78	/// The sender participates in authorization and client-visible event
79	/// output.
80	pub sender: OwnedUserId,
81
82	/// State key when this is a state event.
83	///
84	/// Message-like events store `None` and omit the field during
85	/// serialization.
86	#[serde(skip_serializing_if = "Option::is_none")]
87	pub state_key: Option<StateKey>,
88
89	/// Event targeted by a redaction when carried at the top level.
90	///
91	/// Newer room versions can place this value in event content instead. An
92	/// absent target is omitted during serialization.
93	#[serde(skip_serializing_if = "Option::is_none")]
94	pub redacts: Option<OwnedEventId>,
95
96	/// Events declared as direct predecessors of this event.
97	///
98	/// The sequence is stored inline for the common single-predecessor case.
99	pub prev_events: PrevEvents,
100
101	/// Events used to authorize this event.
102	///
103	/// These identifiers form the event's explicit authorization dependency
104	/// set.
105	pub auth_events: AuthEvents,
106
107	/// Millisecond timestamp supplied by the originating server.
108	///
109	/// The value is preserved for ordering metadata and client serialization.
110	pub origin_server_ts: UInt,
111
112	/// Event depth in the room directed acyclic graph.
113	///
114	/// Depth is originating-server-provided graph metadata and is distinct from
115	/// the local timeline sequence.
116	pub depth: UInt,
117
118	/// Content hash declared by the event.
119	///
120	/// Federation validation compares the declaration with the computed content
121	/// hash to detect content changes.
122	pub hashes: EventHash,
123
124	/// Server that originated an event carrying a top-level `origin` field.
125	///
126	/// Legacy event formats retain this value for local and remote events. The
127	/// field is absent when it was not carried by the event format.
128	#[serde(skip_serializing_if = "Option::is_none")]
129	pub origin: Option<OwnedServerName>,
130
131	/// Unsigned metadata excluded from event hashing and signing.
132	///
133	/// Stored values are local annotations such as transaction IDs, age, prior
134	/// state, and bundled relations. The field is omitted when absent.
135	#[serde(default, skip_serializing_if = "Option::is_none")]
136	pub unsigned: Option<Unsigned>,
137
138	//TODO: https://spec.matrix.org/v1.14/rooms/v11/#rejected-events
139	/// Whether state resolution rejected this event in test fixtures.
140	///
141	/// Production builds derive rejection state outside the serialized PDU.
142	#[cfg(test)]
143	#[serde(default, skip_serializing)]
144	pub rejected: bool,
145}
146
147/// Inline storage for the common single-entry `prev_events` case.
148///
149/// Events with additional predecessors spill to the heap, avoiding larger
150/// inline storage on every event.
151pub type PrevEvents = SmallVec<[OwnedEventId; 1]>;
152
153/// Inline storage for the typical three-entry `auth_events` case.
154///
155/// Restricted rooms can require many more entries, so this remains a spilling
156/// `SmallVec` rather than a fixed-capacity `ArrayVec`.
157pub type AuthEvents = SmallVec<[OwnedEventId; 3]>;
158
159/// Raw event-content storage with 112 bytes of inline capacity.
160///
161/// The capacity follows an allocator-profile mode in the 96 to 112 byte range
162/// and targets a 128 byte total size with `SmallVec` metadata.
163pub type Content = Raw<CanonicalJsonObject, 112>;
164
165/// Raw `unsigned` storage with 112 bytes of inline capacity.
166///
167/// The enclosing field is usually `None` or contains a small local annotation,
168/// such as `transaction_id`, `age`, or `membership`. Those values remain inline
169/// at the `Content` size class, while larger state-event `prev_content` and
170/// bundled `m.relations` values spill to the heap.
171pub type Unsigned = Raw<CanonicalJsonObject, 112>;
172
173/// The [maximum size allowed] for a PDU.
174/// [maximum size allowed]: <https://spec.matrix.org/latest/client-server-api/#size-limits>
175pub const MAX_PDU_BYTES: usize = 65_535;
176
177/// The [maximum length allowed] for the `prev_events` array of a PDU.
178/// [maximum length allowed]: <https://spec.matrix.org/latest/rooms/v1/#event-format>
179pub const MAX_PREV_EVENTS: usize = 20;
180
181/// The [maximum length allowed] for the `auth_events` array of a PDU.
182/// [maximum length allowed]: <https://spec.matrix.org/latest/rooms/v1/#event-format>
183pub const MAX_AUTH_EVENTS: usize = 10;
184
185impl Pdu {
186	/// Inserts room and event IDs before deserializing a canonical PDU object.
187	///
188	/// Existing values under those keys are replaced. The resulting typed PDU
189	/// owns both supplied identifiers.
190	pub fn from_object_and_roomid_and_eventid(
191		room_id: &RoomId,
192		event_id: &EventId,
193		mut json: CanonicalJsonObject,
194	) -> Result<Self> {
195		let room_id = CanonicalJsonValue::String(room_id.into());
196		json.insert("room_id".into(), room_id);
197		Self::from_object_and_eventid(event_id, json)
198	}
199
200	/// Inserts an event ID before deserializing a canonical PDU object.
201	///
202	/// Any existing `event_id` value is replaced. Other object fields pass
203	/// through to normal PDU deserialization.
204	pub fn from_object_and_eventid(
205		event_id: &EventId,
206		mut json: CanonicalJsonObject,
207	) -> Result<Self> {
208		let event_id = CanonicalJsonValue::String(event_id.into());
209		json.insert("event_id".into(), event_id);
210		Self::from_object(json)
211	}
212
213	/// Normalizes federation wire fields and validates PDU format and room ID.
214	///
215	/// Version-specific wire fields are converted to the stored representation
216	/// before these checks. Signature, content-hash, and authorization
217	/// validation remain the caller's responsibility.
218	///
219	/// # Panics
220	///
221	/// Panics if the object lacks `type` while the selected rules do not
222	/// require a create-event room ID.
223	pub fn from_object_federation(
224		room_id: &RoomId,
225		event_id: &EventId,
226		json: CanonicalJsonObject,
227		rules: &RoomVersionRules,
228	) -> Result<(Self, CanonicalJsonObject)> {
229		let json = from_incoming_federation(room_id, event_id, json, rules);
230		let pdu = Self::from_object_checked(json.clone(), rules)?;
231		check_room_id(&pdu, room_id)?;
232		Ok((pdu, json))
233	}
234
235	/// Validates a canonical PDU object before deserializing it.
236	///
237	/// Checks use the supplied room-version event-format rules. Successful
238	/// validation returns the typed stored representation.
239	pub fn from_object_checked(
240		json: CanonicalJsonObject,
241		rules: &RoomVersionRules,
242	) -> Result<Self> {
243		check_rules(&json, &rules.event_format)?;
244		Self::from_object(json)
245	}
246
247	/// Deserializes a canonical JSON object into a stored PDU.
248	///
249	/// The object is wrapped as a canonical JSON value before typed
250	/// deserialization. No room-version format checks are performed.
251	pub fn from_object(json: CanonicalJsonObject) -> Result<Self> {
252		let json = CanonicalJsonValue::Object(json);
253		Self::from_value(json)
254	}
255
256	/// Deserializes raw JSON through a canonical JSON value.
257	///
258	/// Canonical conversion normalizes integer and object representation before
259	/// the PDU fields are decoded. No room-version format checks are
260	/// performed.
261	///
262	/// # Panics
263	///
264	/// Panics if the raw JSON contains a value outside canonical JSON, such as
265	/// a floating-point value or an integer outside the canonical range.
266	pub fn from_raw_value(json: &RawJsonValue) -> Result<Self> {
267		let json: CanonicalJsonValue = json.into();
268		Self::from_value(json)
269	}
270
271	/// Deserializes a canonical JSON value into a stored PDU.
272	///
273	/// The input must contain the fields required by `Pdu`. No room-version
274	/// format checks are performed before deserialization.
275	pub fn from_value(json: CanonicalJsonValue) -> Result<Self> {
276		serde_json::from_value(json.into()).map_err(Into::into)
277	}
278
279	/// Deserializes raw JSON directly into a stored PDU.
280	///
281	/// This path uses `Pdu`'s Serde representation without first canonicalizing
282	/// the input. Callers that require canonical validation should use a
283	/// checked constructor.
284	pub fn from_raw_json(json: &RawJsonValue) -> Result<Self> {
285		Self::deserialize(json).map_err(Into::into)
286	}
287
288	/// MSC4025: a pruned clone per the redaction rules, carrying no
289	/// `redacted_because`; no redaction event exists for a serve-time
290	/// erasure.
291	pub fn redacted(&self, rules: &RedactionRules) -> Result<Self> {
292		let mut object = self.to_canonical_object();
293
294		redact_in_place(&mut object, rules, None)
295			.map_err(|e| err!("Failed to redact event: {e}"))?;
296
297		Self::from_object(object)
298	}
299}
300
301impl Event for Pdu
302where
303	Self: Send + Sync + 'static,
304{
305	#[inline]
306	fn auth_events(&self) -> impl DoubleEndedIterator<Item = &EventId> + Clone + Send + '_ {
307		self.auth_events.iter().map(AsRef::as_ref)
308	}
309
310	#[inline]
311	fn auth_events_into(
312		self,
313	) -> impl IntoIterator<IntoIter = impl Iterator<Item = OwnedEventId>> + Send {
314		self.auth_events.into_iter()
315	}
316
317	#[inline]
318	fn content(&self) -> &RawJsonValue { self.content.json() }
319
320	#[inline]
321	fn event_id(&self) -> &EventId { &self.event_id }
322
323	#[inline]
324	fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch {
325		MilliSecondsSinceUnixEpoch(self.origin_server_ts)
326	}
327
328	#[inline]
329	fn prev_events(&self) -> impl DoubleEndedIterator<Item = &EventId> + Clone + Send + '_ {
330		self.prev_events.iter().map(AsRef::as_ref)
331	}
332
333	#[inline]
334	fn redacts(&self) -> Option<&EventId> { self.redacts.as_deref() }
335
336	#[cfg(test)]
337	#[inline]
338	fn rejected(&self) -> bool { self.rejected }
339
340	#[cfg(not(test))]
341	#[inline]
342	fn rejected(&self) -> bool { false }
343
344	#[inline]
345	fn room_id(&self) -> &RoomId { &self.room_id }
346
347	#[inline]
348	fn sender(&self) -> &UserId { &self.sender }
349
350	#[inline]
351	fn state_key(&self) -> Option<&str> { self.state_key.as_deref() }
352
353	#[inline]
354	fn kind(&self) -> &TimelineEventType { &self.kind }
355
356	#[inline]
357	fn unsigned(&self) -> Option<&RawJsonValue> { self.unsigned.as_ref().map(Unsigned::json) }
358
359	#[inline]
360	fn as_mut_pdu(&mut self) -> &mut Pdu { self }
361
362	#[inline]
363	fn as_pdu(&self) -> &Pdu { self }
364
365	#[inline]
366	fn into_pdu(self) -> Pdu { self }
367
368	#[inline]
369	fn is_owned(&self) -> bool { true }
370}
371
372impl Event for &Pdu
373where
374	Self: Send,
375{
376	#[inline]
377	fn auth_events(&self) -> impl DoubleEndedIterator<Item = &EventId> + Clone + Send + '_ {
378		self.auth_events.iter().map(AsRef::as_ref)
379	}
380
381	#[inline]
382	fn auth_events_into(
383		self,
384	) -> impl IntoIterator<IntoIter = impl Iterator<Item = OwnedEventId>> + Send {
385		self.auth_events.iter().map(ToOwned::to_owned)
386	}
387
388	#[inline]
389	fn content(&self) -> &RawJsonValue { self.content.json() }
390
391	#[inline]
392	fn event_id(&self) -> &EventId { &self.event_id }
393
394	#[inline]
395	fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch {
396		MilliSecondsSinceUnixEpoch(self.origin_server_ts)
397	}
398
399	#[inline]
400	fn prev_events(&self) -> impl DoubleEndedIterator<Item = &EventId> + Clone + Send + '_ {
401		self.prev_events.iter().map(AsRef::as_ref)
402	}
403
404	#[inline]
405	fn redacts(&self) -> Option<&EventId> { self.redacts.as_deref() }
406
407	#[cfg(test)]
408	#[inline]
409	fn rejected(&self) -> bool { self.rejected }
410
411	#[cfg(not(test))]
412	#[inline]
413	fn rejected(&self) -> bool { false }
414
415	#[inline]
416	fn room_id(&self) -> &RoomId { &self.room_id }
417
418	#[inline]
419	fn sender(&self) -> &UserId { &self.sender }
420
421	#[inline]
422	fn state_key(&self) -> Option<&str> { self.state_key.as_deref() }
423
424	#[inline]
425	fn kind(&self) -> &TimelineEventType { &self.kind }
426
427	#[inline]
428	fn unsigned(&self) -> Option<&RawJsonValue> { self.unsigned.as_ref().map(Unsigned::json) }
429
430	#[inline]
431	fn as_pdu(&self) -> &Pdu { self }
432
433	#[inline]
434	fn into_pdu(self) -> Pdu { self.clone() }
435
436	#[inline]
437	fn is_owned(&self) -> bool { false }
438}
439
440/// Prevent derived equality which wouldn't limit itself to event_id
441impl Eq for Pdu {}
442
443/// Equality determined by the Pdu's ID, not the memory representations.
444impl PartialEq for Pdu {
445	fn eq(&self, other: &Self) -> bool { self.event_id == other.event_id }
446}
447
448/// Ordering determined by the Pdu's ID, not the memory representations.
449impl Ord for Pdu {
450	fn cmp(&self, other: &Self) -> Ordering { self.event_id.cmp(&other.event_id) }
451}
452
453/// Ordering determined by the Pdu's ID, not the memory representations.
454impl PartialOrd for Pdu {
455	fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
456}