ruma_state_res/state_res.rs
1use std::{
2 borrow::Borrow,
3 cmp::{Ordering, Reverse},
4 collections::{BinaryHeap, HashMap, HashSet},
5 hash::Hash,
6 num::NonZero,
7 sync::OnceLock,
8};
9
10use ruma_common::{
11 EventId, MilliSecondsSinceUnixEpoch, OwnedUserId,
12 room_version_rules::{AuthorizationRules, StateResolutionV2Rules},
13};
14use ruma_events::{
15 StateEventType, TimelineEventType,
16 room::{member::MembershipState, power_levels::UserPowerLevel},
17};
18use tracing::{debug, info, instrument, trace, warn};
19
20#[cfg(test)]
21mod tests;
22
23use crate::{
24 Error, Event, Result, auth_types_for_event, check_state_dependent_auth_rules,
25 events::{
26 RoomCreateEvent, RoomMemberEvent, RoomPowerLevelsEvent, RoomPowerLevelsIntField,
27 power_levels::RoomPowerLevelsEventOptionExt,
28 },
29 utils::{RoomIdExt, event_id_map::EventIdMap, event_id_set::EventIdSet},
30};
31
32/// A mapping of event type and state_key to some value `T`, usually an `EventId`.
33///
34/// This is the representation of what the Matrix specification calls a "room state" or a "state
35/// map" during [state resolution].
36///
37/// [state resolution]: https://spec.matrix.org/v1.18/rooms/v2/#state-resolution
38pub type StateMap<T> = HashMap<(StateEventType, String), T>;
39
40/// Apply the [state resolution] algorithm introduced in room version 2 to resolve the state of a
41/// room.
42///
43/// ## Arguments
44///
45/// * `auth_rules` - The authorization rules to apply for the version of the current room.
46///
47/// * `state_res_rules` - The state resolution rules to apply for the version of the current room.
48///
49/// * `state_maps` - The incoming states to resolve. Each `StateMap` represents a possible fork in
50/// the state of a room.
51///
52/// * `auth_chains` - The list of full recursive sets of `auth_events` for each event in the
53/// `state_maps`.
54///
55/// * `fetch_event` - Function to fetch an event in the room given its event ID.
56///
57/// * `fetch_conflicted_state_subgraph` - Function to fetch the conflicted state subgraph for the
58/// given conflicted state set, for state resolution rules that use it. If it is called and
59/// returns `None`, this function will return an error.
60///
61/// ## Invariants
62///
63/// The caller of `resolve` must ensure that all the events are from the same room.
64///
65/// ## Returns
66///
67/// The resolved room state.
68///
69/// [state resolution]: https://spec.matrix.org/v1.18/rooms/v2/#state-resolution
70#[instrument(skip_all)]
71pub fn resolve<'a, E, MapsIter>(
72 auth_rules: &AuthorizationRules,
73 state_res_rules: &StateResolutionV2Rules,
74 state_maps: impl IntoIterator<IntoIter = MapsIter>,
75 auth_chains: Vec<EventIdSet<E::Id>>,
76 fetch_event: impl Fn(&EventId) -> Option<E>,
77 fetch_conflicted_state_subgraph: impl Fn(&StateMap<Vec<E::Id>>) -> Option<EventIdSet<E::Id>>,
78) -> Result<StateMap<E::Id>>
79where
80 E: Event + Clone,
81 E::Id: 'a,
82 MapsIter: Iterator<Item = &'a StateMap<E::Id>> + Clone,
83{
84 info!("state resolution starting");
85
86 // Split the unconflicted state map and the conflicted state set.
87 let (unconflicted_state_map, conflicted_state_set) =
88 split_conflicted_state_set(state_maps.into_iter());
89
90 info!(count = unconflicted_state_map.len(), "unconflicted events");
91 trace!(map = ?unconflicted_state_map, "unconflicted events");
92
93 if conflicted_state_set.is_empty() {
94 info!("no conflicted state found");
95 return Ok(unconflicted_state_map);
96 }
97
98 info!(count = conflicted_state_set.len(), "conflicted events");
99 trace!(map = ?conflicted_state_set, "conflicted events");
100
101 // Since v12, fetch the conflicted state subgraph.
102 let conflicted_state_subgraph = if state_res_rules.consider_conflicted_state_subgraph {
103 let conflicted_state_subgraph = fetch_conflicted_state_subgraph(&conflicted_state_set)
104 .ok_or(Error::FetchConflictedStateSubgraphFailed)?;
105
106 info!(count = conflicted_state_subgraph.len(), "events in conflicted state subgraph");
107 trace!(set = ?conflicted_state_subgraph, "conflicted state subgraph");
108
109 conflicted_state_subgraph
110 } else {
111 EventIdSet::new()
112 };
113
114 // The full conflicted set is the union of the conflicted state set and the auth difference,
115 // and since v12, the conflicted state subgraph.
116 let full_conflicted_set: EventIdSet<_> = auth_difference(auth_chains)
117 .chain(conflicted_state_set.into_values().flatten())
118 .chain(conflicted_state_subgraph)
119 // Don't honor events we cannot "verify"
120 .filter(|id| fetch_event(id.borrow()).is_some())
121 .collect();
122
123 info!(count = full_conflicted_set.len(), "full conflicted set");
124 trace!(set = ?full_conflicted_set, "full conflicted set");
125
126 // 1. Select the set X of all power events that appear in the full conflicted set. For each such
127 // power event P, enlarge X by adding the events in the auth chain of P which also belong to
128 // the full conflicted set. Sort X into a list using the reverse topological power ordering.
129 let conflicted_power_events = full_conflicted_set
130 .iter()
131 .filter(|&id| is_power_event_id(id.borrow(), &fetch_event))
132 .cloned()
133 .collect::<Vec<_>>();
134
135 let sorted_power_events =
136 sort_power_events(conflicted_power_events, &full_conflicted_set, auth_rules, &fetch_event)?;
137
138 debug!(count = sorted_power_events.len(), "power events");
139 trace!(list = ?sorted_power_events, "sorted power events");
140
141 // 2. Apply the iterative auth checks algorithm, starting from the unconflicted state map, to
142 // the list of events from the previous step to get a partially resolved state.
143
144 // Since v12, begin the first phase of iterative auth checks with an empty state map.
145 let initial_state_map = if state_res_rules.begin_iterative_auth_checks_with_empty_state_map {
146 HashMap::new()
147 } else {
148 unconflicted_state_map.clone()
149 };
150
151 let partially_resolved_state =
152 iterative_auth_checks(auth_rules, &sorted_power_events, initial_state_map, &fetch_event)?;
153
154 debug!(count = partially_resolved_state.len(), "resolved power events");
155 trace!(map = ?partially_resolved_state, "resolved power events");
156
157 // 3. Take all remaining events that weren’t picked in step 1 and order them by the mainline
158 // ordering based on the power level in the partially resolved state obtained in step 2.
159 let sorted_power_events_set = sorted_power_events.into_iter().collect::<EventIdSet<_>>();
160 let remaining_events = full_conflicted_set
161 .iter()
162 .filter(|&id| !sorted_power_events_set.contains(id.borrow()))
163 .cloned()
164 .collect::<Vec<_>>();
165
166 debug!(count = remaining_events.len(), "events left to resolve");
167 trace!(list = ?remaining_events, "events left to resolve");
168
169 // This "epochs" power level event
170 let power_event = partially_resolved_state.get(&(StateEventType::RoomPowerLevels, "".into()));
171
172 debug!(event_id = ?power_event, "power event");
173
174 let sorted_remaining_events =
175 mainline_sort(&remaining_events, power_event.cloned(), &fetch_event)?;
176
177 trace!(list = ?sorted_remaining_events, "events left, sorted");
178
179 // 4. Apply the iterative auth checks algorithm on the partial resolved state and the list of
180 // events from the previous step.
181 let mut resolved_state = iterative_auth_checks(
182 auth_rules,
183 &sorted_remaining_events,
184 partially_resolved_state,
185 &fetch_event,
186 )?;
187
188 // 5. Update the result by replacing any event with the event with the same key from the
189 // unconflicted state map, if such an event exists, to get the final resolved state.
190 resolved_state.extend(unconflicted_state_map);
191
192 info!("state resolution finished");
193
194 Ok(resolved_state)
195}
196
197/// Split the unconflicted state map and the conflicted state set.
198///
199/// Definition in the specification:
200///
201/// > If a given key _K_ is present in every _Si_ with the same value _V_ in each state map, then
202/// > the pair (_K_, _V_) belongs to the unconflicted state map. Otherwise, _V_ belongs to the
203/// > conflicted state set.
204///
205/// It means that, for a given (event type, state key) tuple, if all state maps have the same event
206/// ID, it lands in the unconflicted state map, otherwise the event IDs land in the conflicted state
207/// set.
208///
209/// ## Arguments
210///
211/// * `state_maps` - The incoming states to resolve. Each `StateMap` represents a possible fork in
212/// the state of a room.
213///
214/// ## Returns
215///
216/// Returns an `(unconflicted_state_map, conflicted_state_set)` tuple.
217fn split_conflicted_state_set<'a, Id>(
218 state_maps: impl Iterator<Item = &'a StateMap<Id>>,
219) -> (StateMap<Id>, StateMap<Vec<Id>>)
220where
221 Id: Clone + Eq + Hash + 'a,
222{
223 let mut state_set_count = 0_usize;
224 let mut occurrences = HashMap::<_, HashMap<_, _>>::new();
225
226 let state_maps = state_maps.inspect(|_| state_set_count += 1);
227 for (k, v) in state_maps.flatten() {
228 occurrences.entry(k).or_default().entry(v).and_modify(|x| *x += 1).or_insert(1);
229 }
230
231 let mut unconflicted_state_map = StateMap::new();
232 let mut conflicted_state_set = StateMap::new();
233
234 for (k, v) in occurrences {
235 for (id, occurrence_count) in v {
236 if occurrence_count == state_set_count {
237 unconflicted_state_map.insert((k.0.clone(), k.1.clone()), id.clone());
238 } else {
239 conflicted_state_set
240 .entry((k.0.clone(), k.1.clone()))
241 .and_modify(|x: &mut Vec<_>| x.push(id.clone()))
242 .or_insert(vec![id.clone()]);
243 }
244 }
245 }
246
247 (unconflicted_state_map, conflicted_state_set)
248}
249
250/// Get the auth difference for the given auth chains.
251///
252/// Definition in the specification:
253///
254/// > The auth difference is calculated by first calculating the full auth chain for each state
255/// > _Si_, that is the union of the auth chains for each event in _Si_, and then taking every event
256/// > that doesn’t appear in every auth chain. If _Ci_ is the full auth chain of _Si_, then the auth
257/// > difference is ∪_Ci_ − ∩_Ci_.
258///
259/// ## Arguments
260///
261/// * `auth_chains` - The list of full recursive sets of `auth_events`.
262///
263/// ## Returns
264///
265/// Returns an iterator over all the event IDs that are not present in all the auth chains.
266fn auth_difference<Id>(auth_chains: Vec<EventIdSet<Id>>) -> impl Iterator<Item = Id>
267where
268 Id: Eq + Hash + Borrow<EventId>,
269{
270 let num_sets = auth_chains.len();
271
272 let mut id_counts: EventIdMap<Id, usize> = EventIdMap::new();
273 for id in auth_chains.into_iter().flatten() {
274 *id_counts.entry(id).or_default() += 1;
275 }
276
277 id_counts.into_iter().filter_map(move |(id, count)| (count < num_sets).then_some(id))
278}
279
280/// Enlarge the given list of conflicted power events by adding the events in their auth chain that
281/// are in the full conflicted set, and sort it using reverse topological power ordering.
282///
283/// ## Arguments
284///
285/// * `conflicted_power_events` - The list of power events in the full conflicted set.
286///
287/// * `full_conflicted_set` - The full conflicted set.
288///
289/// * `rules` - The authorization rules for the current room version.
290///
291/// * `fetch_event` - Function to fetch an event in the room given its event ID.
292///
293/// ## Returns
294///
295/// Returns the ordered list of event IDs from earliest to latest.
296#[instrument(skip_all)]
297fn sort_power_events<E: Event>(
298 conflicted_power_events: Vec<E::Id>,
299 full_conflicted_set: &EventIdSet<E::Id>,
300 rules: &AuthorizationRules,
301 fetch_event: impl Fn(&EventId) -> Option<E>,
302) -> Result<Vec<E::Id>> {
303 debug!("reverse topological sort of power events");
304
305 // A representation of the DAG, a map of event ID to its list of auth events that are in the
306 // full conflicted set.
307 let mut graph = EventIdMap::new();
308
309 // Fill the graph.
310 for event_id in conflicted_power_events {
311 add_event_and_auth_chain_to_graph(&mut graph, event_id, full_conflicted_set, &fetch_event);
312
313 // TODO: if these functions are ever made async here
314 // is a good place to yield every once in a while so other
315 // tasks can make progress
316 }
317
318 // The map of event ID to the power level of the sender of the event.
319 let mut event_to_power_level = EventIdMap::new();
320 // We need to know the creator in case of missing power levels. Given that it's the same for all
321 // the events in the room, we will just load it for the first event and reuse it.
322 let creators_lock = OnceLock::new();
323
324 // Get the power level of the sender of each event in the graph.
325 for event_id in graph.keys() {
326 let sender_power_level =
327 power_level_for_sender(event_id.borrow(), rules, &creators_lock, &fetch_event)
328 .map_err(Error::AuthEvent)?;
329 debug!(
330 event_id = event_id.borrow().as_str(),
331 power_level = ?sender_power_level,
332 "found the power level of an event's sender",
333 );
334
335 event_to_power_level.insert(event_id.clone(), sender_power_level);
336
337 // TODO: if these functions are ever made async here
338 // is a good place to yield every once in a while so other
339 // tasks can make progress
340 }
341
342 reverse_topological_power_sort(&graph, |event_id| {
343 let event = fetch_event(event_id).ok_or_else(|| Error::NotFound(event_id.to_owned()))?;
344 let power_level = *event_to_power_level
345 .get(event_id)
346 .ok_or_else(|| Error::NotFound(event_id.to_owned()))?;
347 Ok((power_level, event.origin_server_ts()))
348 })
349}
350
351/// Sorts the given event graph using reverse topological power ordering.
352///
353/// Definition in the specification:
354///
355/// > The reverse topological power ordering of a set of events is the lexicographically smallest
356/// > topological ordering based on the DAG formed by auth events. The reverse topological power
357/// > ordering is ordered from earliest event to latest. For comparing two topological orderings to
358/// > determine which is the lexicographically smallest, the following comparison relation on events
359/// > is used: for events x and y, x < y if
360/// >
361/// > 1. x’s sender has greater power level than y’s sender, when looking at their respective
362/// > auth_events; or
363/// > 2. the senders have the same power level, but x’s origin_server_ts is less than y’s
364/// > origin_server_ts; or
365/// > 3. the senders have the same power level and the events have the same origin_server_ts, but
366/// > x’s event_id is less than y’s event_id.
367/// >
368/// > The reverse topological power ordering can be found by sorting the events using Kahn’s
369/// > algorithm for topological sorting, and at each step selecting, among all the candidate
370/// > vertices, the smallest vertex using the above comparison relation.
371///
372/// ## Arguments
373///
374/// * `graph` - The graph to sort. A map of event ID to its auth events that are in the full
375/// conflicted set.
376///
377/// * `event_details_fn` - Function to obtain a (power level, origin_server_ts) of an event for
378/// breaking ties.
379///
380/// ## Returns
381///
382/// Returns the ordered list of event IDs from earliest to latest.
383#[instrument(skip_all)]
384pub fn reverse_topological_power_sort<Id, F>(
385 graph: &EventIdMap<Id, EventIdSet<Id>>,
386 event_details_fn: F,
387) -> Result<Vec<Id>>
388where
389 F: Fn(&EventId) -> Result<(UserPowerLevel, MilliSecondsSinceUnixEpoch)>,
390 Id: Clone + Eq + Ord + Hash + Borrow<EventId>,
391{
392 #[derive(PartialEq, Eq)]
393 struct TieBreaker<Id> {
394 power_level: UserPowerLevel,
395 origin_server_ts: MilliSecondsSinceUnixEpoch,
396 event_id: Id,
397 }
398
399 impl<Id> Ord for TieBreaker<Id>
400 where
401 Id: Ord,
402 {
403 fn cmp(&self, other: &Self) -> Ordering {
404 // NOTE: the power level comparison is "backwards" intentionally.
405 other
406 .power_level
407 .cmp(&self.power_level)
408 .then(self.origin_server_ts.cmp(&other.origin_server_ts))
409 .then(self.event_id.cmp(&other.event_id))
410 }
411 }
412
413 impl<Id> PartialOrd for TieBreaker<Id>
414 where
415 Id: Ord,
416 {
417 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
418 Some(self.cmp(other))
419 }
420 }
421
422 // We consider that the DAG is directed from most recent events to oldest events, so an event is
423 // an incoming edge to its auth events.
424
425 // Map of event to the list of events in its auth events.
426 let mut outgoing_edges_map: EventIdMap<_, EventIdSet<_>> = EventIdMap::new();
427
428 // Map of event to the list of events that reference it in its auth events.
429 let mut incoming_edges_map: EventIdMap<_, EventIdSet<_>> = EventIdMap::new();
430
431 // Vec of events that have an outdegree of zero (no outgoing edges), i.e. the oldest events.
432 // Use a BinaryHeap to keep the events sorted.
433 let mut heap = BinaryHeap::new();
434
435 // Populate the list of events with an outdegree of zero, and the maps of incoming and outgoing
436 // edges with the graph.
437 for (event_id, outgoing_edges) in graph {
438 if outgoing_edges.is_empty() {
439 let (power_level, origin_server_ts) = event_details_fn(event_id.borrow())?;
440
441 // `Reverse` because `BinaryHeap` sorts largest -> smallest and we need
442 // smallest -> largest.
443 heap.push(Reverse(TieBreaker {
444 power_level,
445 origin_server_ts,
446 event_id: event_id.clone(),
447 }));
448 } else {
449 for auth_event_id in outgoing_edges {
450 incoming_edges_map
451 .entry(auth_event_id.borrow())
452 .or_default()
453 .insert(event_id.borrow());
454 }
455
456 outgoing_edges_map
457 .insert(event_id.clone(), outgoing_edges.iter().map(Borrow::borrow).collect());
458 }
459 }
460
461 let mut sorted = vec![];
462
463 // Apply Kahn's algorithm.
464 // https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
465 while let Some(Reverse(TieBreaker { event_id, .. })) = heap.pop() {
466 for &parent_id in incoming_edges_map.get(event_id.borrow()).into_iter().flatten() {
467 let parent_has_zero_outdegrees = {
468 let outgoing_edges = outgoing_edges_map.get_mut(parent_id).expect(
469 "outgoing edges map should have a key for all event IDs with outgoing edges",
470 );
471
472 outgoing_edges.remove(event_id.borrow());
473 outgoing_edges.is_empty()
474 };
475
476 // Push on the heap once all the outgoing edges have been removed.
477 if parent_has_zero_outdegrees {
478 let (power_level, origin_server_ts) = event_details_fn(parent_id)?;
479 // Because the parent has no more outgoing edges, we can remove its entry from the
480 // outgoing edges map to get the owned event ID used for the key.
481 let (parent_id, _) = outgoing_edges_map
482 .remove_entry(parent_id)
483 .expect("outgoing edges map should have a key for all event IDs");
484
485 heap.push(Reverse(TieBreaker {
486 power_level,
487 origin_server_ts,
488 event_id: parent_id,
489 }));
490 }
491 }
492
493 sorted.push(event_id);
494 }
495
496 Ok(sorted)
497}
498
499/// Find the power level for the sender of the event of the given event ID or return a default value
500/// of zero.
501///
502/// We find the most recent `m.room.power_levels` by walking backwards in the auth chain of the
503/// event.
504///
505/// Do NOT use this anywhere but topological sort.
506///
507/// ## Arguments
508///
509/// * `event_id` - The event ID of the event to get the power level of the sender of.
510///
511/// * `rules` - The authorization rules for the current room version.
512///
513/// * `creator_lock` - A lock used to cache the user ID of the creator of the room. If it is empty
514/// the creator will be fetched in the auth chain and used to populate the lock.
515///
516/// * `fetch_event` - Function to fetch an event in the room given its event ID.
517///
518/// ## Returns
519///
520/// Returns the power level of the sender of the event or an `Err(_)` if one of the auth events if
521/// malformed.
522fn power_level_for_sender<E: Event>(
523 event_id: &EventId,
524 rules: &AuthorizationRules,
525 creators_lock: &OnceLock<HashSet<OwnedUserId>>,
526 fetch_event: impl Fn(&EventId) -> Option<E>,
527) -> std::result::Result<UserPowerLevel, String> {
528 let event = fetch_event(event_id);
529 let mut room_create_event = None;
530 let mut room_power_levels_event = None;
531
532 if let Some(event) = &event
533 && rules.room_create_event_id_as_room_id
534 && creators_lock.get().is_none()
535 {
536 // The m.room.create event is not in the auth events, we can get its ID via the room ID.
537 room_create_event = event
538 .room_id()
539 .and_then(|room_id| room_id.room_create_event_id().ok())
540 .and_then(|room_create_event_id| fetch_event(&room_create_event_id));
541 }
542
543 for auth_event_id in event.as_ref().map(|pdu| pdu.auth_events()).into_iter().flatten() {
544 if let Some(auth_event) = fetch_event(auth_event_id.borrow()) {
545 if is_type_and_key(&auth_event, &TimelineEventType::RoomPowerLevels, "") {
546 room_power_levels_event = Some(RoomPowerLevelsEvent::new(auth_event));
547 } else if !rules.room_create_event_id_as_room_id
548 && creators_lock.get().is_none()
549 && is_type_and_key(&auth_event, &TimelineEventType::RoomCreate, "")
550 {
551 room_create_event = Some(auth_event);
552 }
553
554 if room_power_levels_event.is_some()
555 && (rules.room_create_event_id_as_room_id
556 || creators_lock.get().is_some()
557 || room_create_event.is_some())
558 {
559 break;
560 }
561 }
562 }
563
564 // TODO: Use OnceLock::try_or_get_init when it is stabilized.
565 let creators = if let Some(creators) = creators_lock.get() {
566 Some(creators)
567 } else if let Some(room_create_event) = room_create_event {
568 let room_create_event = RoomCreateEvent::new(room_create_event);
569 let creators = room_create_event.creators(rules)?;
570 Some(creators_lock.get_or_init(|| creators))
571 } else {
572 None
573 };
574
575 if let Some((event, creators)) = event.zip(creators) {
576 room_power_levels_event.user_power_level(event.sender(), creators, rules)
577 } else {
578 room_power_levels_event
579 .get_as_int_or_default(RoomPowerLevelsIntField::UsersDefault, rules)
580 .map(Into::into)
581 }
582}
583
584/// Perform the iterative auth checks to the given list of events.
585///
586/// Definition in the specification:
587///
588/// > The iterative auth checks algorithm takes as input an initial room state and a sorted list of
589/// > state events, and constructs a new room state by iterating through the event list and applying
590/// > the state event to the room state if the state event is allowed by the authorization rules. If
591/// > the state event is not allowed by the authorization rules, then the event is ignored. If a
592/// > (event_type, state_key) key that is required for checking the authorization rules is not
593/// > present in the state, then the appropriate state event from the event’s auth_events is used if
594/// > the auth event is not rejected.
595///
596/// ## Arguments
597///
598/// * `rules` - The authorization rules for the current room version.
599///
600/// * `events` - The sorted state events to apply to the `partial_state`.
601///
602/// * `state` - The current state that was partially resolved for the room.
603///
604/// * `fetch_event` - Function to fetch an event in the room given its event ID.
605///
606/// ## Returns
607///
608/// Returns the partially resolved state, or an `Err(_)` if one of the state events in the room has
609/// an unexpected format.
610fn iterative_auth_checks<E: Event + Clone>(
611 rules: &AuthorizationRules,
612 events: &[E::Id],
613 mut state: StateMap<E::Id>,
614 fetch_event: impl Fn(&EventId) -> Option<E>,
615) -> Result<StateMap<E::Id>> {
616 debug!("starting iterative auth checks");
617
618 trace!(list = ?events, "events to check");
619
620 for event_id in events {
621 let event = fetch_event(event_id.borrow())
622 .ok_or_else(|| Error::NotFound(event_id.borrow().to_owned()))?;
623 let state_key = event.state_key().ok_or(Error::MissingStateKey)?;
624
625 let mut auth_events = StateMap::new();
626 for auth_event_id in event.auth_events() {
627 if let Some(auth_event) = fetch_event(auth_event_id.borrow()) {
628 if !auth_event.rejected() {
629 auth_events.insert(
630 auth_event
631 .event_type()
632 .with_state_key(auth_event.state_key().ok_or(Error::MissingStateKey)?),
633 auth_event,
634 );
635 }
636 } else {
637 warn!(event_id = %auth_event_id.borrow(), "missing auth event");
638 }
639 }
640
641 // If the `m.room.create` event is not in the auth events, we need to add it, because it's
642 // always part of the state and required in the auth rules.
643 if rules.room_create_event_id_as_room_id
644 && *event.event_type() != TimelineEventType::RoomCreate
645 {
646 if let Some(room_create_event) = event
647 .room_id()
648 .and_then(|room_id| room_id.room_create_event_id().ok())
649 .and_then(|room_create_event_id| fetch_event(&room_create_event_id))
650 {
651 auth_events.insert((StateEventType::RoomCreate, String::new()), room_create_event);
652 } else {
653 warn!("missing m.room.create event");
654 }
655 }
656
657 let auth_types = match auth_types_for_event(
658 event.event_type(),
659 event.sender(),
660 Some(state_key),
661 event.content(),
662 rules,
663 ) {
664 Ok(auth_types) => auth_types,
665 Err(error) => {
666 warn!("failed to get list of required auth events for malformed event: {error}");
667 continue;
668 }
669 };
670
671 for key in auth_types {
672 if let Some(auth_event_id) = state.get(&key) {
673 if let Some(auth_event) = fetch_event(auth_event_id.borrow()) {
674 if !auth_event.rejected() {
675 auth_events.insert(key.to_owned(), auth_event);
676 }
677 } else {
678 warn!(event_id = %auth_event_id.borrow(), "missing auth event");
679 }
680 }
681 }
682
683 match check_state_dependent_auth_rules(rules, &event, |ty, key| {
684 auth_events.get(&ty.with_state_key(key))
685 }) {
686 Ok(()) => {
687 // Add event to the partially resolved state.
688 state.insert(event.event_type().with_state_key(state_key), event_id.clone());
689 }
690 Err(error) => {
691 // Don't add this event to the state.
692 warn!(event_id = ?event.event_id(), "event failed the authentication check: {error}");
693 }
694 }
695
696 // TODO: if these functions are ever made async here
697 // is a good place to yield every once in a while so other
698 // tasks can make progress
699 }
700
701 Ok(state)
702}
703
704/// Perform mainline ordering of the given events.
705///
706/// Definition in the spec:
707///
708/// > Given mainline positions calculated from P, the mainline ordering based on P of a set of
709/// > events is the ordering, from smallest to largest, using the following comparison relation on
710/// > events: for events x and y, x < y if
711/// >
712/// > 1. the mainline position of x is greater than the mainline position of y (i.e. the auth chain
713/// > of x is based on an earlier event in the mainline than y); or
714/// > 2. the mainline positions of the events are the same, but x’s origin_server_ts is less than
715/// > y’s origin_server_ts; or
716/// > 3. the mainline positions of the events are the same and the events have the same
717/// > origin_server_ts, but x’s event_id is less than y’s event_id.
718///
719/// ## Arguments
720///
721/// * `events` - The list of event IDs to sort.
722///
723/// * `power_level` - The power level event in the current state.
724///
725/// * `fetch_event` - Function to fetch an event in the room given its event ID.
726///
727/// ## Returns
728///
729/// Returns the sorted list of event IDs, or an `Err(_)` if one the event in the room has an
730/// unexpected format.
731fn mainline_sort<E: Event>(
732 events: &[E::Id],
733 mut power_level: Option<E::Id>,
734 fetch_event: impl Fn(&EventId) -> Option<E>,
735) -> Result<Vec<E::Id>> {
736 debug!("mainline sort of events");
737
738 // There are no events to sort, bail.
739 if events.is_empty() {
740 return Ok(vec![]);
741 }
742
743 // Populate the mainline of the power level.
744 let mut mainline = vec![];
745
746 while let Some(power_level_event_id) = power_level {
747 mainline.push(power_level_event_id.clone());
748
749 let power_level_event = fetch_event(power_level_event_id.borrow())
750 .ok_or_else(|| Error::NotFound(power_level_event_id.borrow().to_owned()))?;
751
752 power_level = None;
753
754 for auth_event_id in power_level_event.auth_events() {
755 let auth_event = fetch_event(auth_event_id.borrow())
756 .ok_or_else(|| Error::NotFound(power_level_event_id.borrow().to_owned()))?;
757 if is_type_and_key(&auth_event, &TimelineEventType::RoomPowerLevels, "") {
758 power_level = Some(auth_event_id.to_owned());
759 break;
760 }
761 }
762
763 // TODO: if these functions are ever made async here
764 // is a good place to yield every once in a while so other
765 // tasks can make progress
766 }
767
768 // The reverse iterator inverts the spec convention: position 1 is the
769 // deepest mainline event and N is the current power-levels event, where
770 // the spec uses 0 for the most recent and ∞ for no ancestor. With
771 // `Option<NonZero<usize>>`, ascending sort by `(position,
772 // origin_server_ts, event_id)` matches the spec mainline ordering, with
773 // no-ancestor events (`None`) first.
774 let mainline_map = mainline
775 .iter()
776 .rev()
777 .enumerate()
778 .map(|(idx, event_id)| {
779 let position = NonZero::new(idx + 1).expect("idx + 1 is non-zero");
780 ((*event_id).clone(), position)
781 })
782 .collect::<EventIdMap<_, _>>();
783
784 let mut order_map = HashMap::new();
785 for event_id in events.iter() {
786 if let Some(event) = fetch_event(event_id.borrow())
787 && let Ok(position) = mainline_position(event, &mainline_map, &fetch_event)
788 {
789 order_map.insert(
790 event_id,
791 (
792 position,
793 fetch_event(event_id.borrow()).map(|event| event.origin_server_ts()),
794 event_id,
795 ),
796 );
797 }
798
799 // TODO: if these functions are ever made async here
800 // is a good place to yield every once in a while so other
801 // tasks can make progress
802 }
803
804 let mut sorted_event_ids = order_map.keys().map(|&k| k.clone()).collect::<Vec<_>>();
805 sorted_event_ids.sort_by_key(|event_id| order_map.get(event_id).unwrap());
806
807 Ok(sorted_event_ids)
808}
809
810/// Get the mainline position of the given event from the given mainline map.
811///
812/// Definition in the spec:
813///
814/// > Let P = P0 be an m.room.power_levels event. Starting with i = 0, repeatedly fetch Pi+1, the
815/// > m.room.power_levels event in the auth_events of Pi. Increment i and repeat until Pi has no
816/// > m.room.power_levels event in its auth_events. The mainline of P0 is the list of events [P0 ,
817/// > P1, … , Pn], fetched in this way.
818/// >
819/// > Let e = e0 be another event (possibly another m.room.power_levels event). We can compute a
820/// > similar list of events [e1, …, em], where ej+1 is the m.room.power_levels event in the
821/// > auth_events of ej and where em has no m.room.power_levels event in its auth_events. (Note that
822/// > the event we started with, e0, is not included in this list. Also note that it may be empty,
823/// > because e may not cite an m.room.power_levels event in its auth_events at all.)
824/// >
825/// > Now compare these two lists as follows.
826/// >
827/// > * Find the smallest index j ≥ 1 for which ej belongs to the mainline of P.
828/// > * If such a j exists, then ej = Pi for some unique index i ≥ 0. Otherwise set i = ∞, where ∞
829/// > is a sentinel value greater than any integer.
830/// > * In both cases, the mainline position of e is i.
831///
832/// ## Arguments
833///
834/// * `event` - The event to compute the mainline position of.
835///
836/// * `mainline_map` - The mainline map of the m.room.power_levels event.
837///
838/// * `fetch_event` - Function to fetch an event in the room given its event ID.
839///
840/// ## Returns
841///
842/// Returns the mainline position of the event, or an `Err(_)` if one of the events in the auth
843/// chain of the event was not found.
844///
845/// `None` is returned for the spec's `i = ∞` case (no power-levels ancestor in the auth chain).
846/// The position is the reverse of the spec encoding: `1` is the deepest mainline event and `N`
847/// the current power-levels event. Ascending sort by `(position, origin_server_ts, event_id)`
848/// then matches the spec mainline ordering.
849fn mainline_position<E: Event>(
850 event: E,
851 mainline_map: &EventIdMap<E::Id, NonZero<usize>>,
852 fetch_event: impl Fn(&EventId) -> Option<E>,
853) -> Result<Option<NonZero<usize>>> {
854 let mut current_event = Some(event);
855
856 while let Some(event) = current_event {
857 let event_id = event.event_id();
858 debug!(event_id = event_id.borrow().as_str(), "mainline");
859
860 // If the current event is in the mainline map, return its position.
861 if let Some(position) = mainline_map.get(event_id.borrow()) {
862 return Ok(Some(*position));
863 }
864
865 current_event = None;
866
867 // Look for the power levels event in the auth events.
868 for auth_event_id in event.auth_events() {
869 let auth_event = fetch_event(auth_event_id.borrow())
870 .ok_or_else(|| Error::NotFound(auth_event_id.borrow().to_owned()))?;
871
872 if is_type_and_key(&auth_event, &TimelineEventType::RoomPowerLevels, "") {
873 current_event = Some(auth_event);
874 break;
875 }
876 }
877 }
878
879 // No power-levels ancestor in the auth chain.
880 Ok(None)
881}
882
883/// Add the event with the given event ID and all the events in its auth chain that are in the full
884/// conflicted set to the graph.
885fn add_event_and_auth_chain_to_graph<E: Event>(
886 graph: &mut EventIdMap<E::Id, EventIdSet<E::Id>>,
887 event_id: E::Id,
888 full_conflicted_set: &EventIdSet<E::Id>,
889 fetch_event: impl Fn(&EventId) -> Option<E>,
890) {
891 let mut state = vec![event_id];
892
893 // Iterate through the auth chain of the event.
894 while let Some(event_id) = state.pop() {
895 // Add the current event to the graph.
896 graph.entry(event_id.clone()).or_default();
897
898 // Iterate through the auth events of this event.
899 for auth_event_id in fetch_event(event_id.borrow())
900 .as_ref()
901 .map(|event| event.auth_events())
902 .into_iter()
903 .flatten()
904 {
905 // If the auth event ID is in the full conflicted set…
906 if full_conflicted_set.contains(auth_event_id.borrow()) {
907 // If the auth event ID is not in the graph, we need to check its auth events later.
908 if !graph.contains_event_id(auth_event_id.borrow()) {
909 state.push(auth_event_id.to_owned());
910 }
911
912 // Add the auth event ID to the list of incoming edges.
913 graph.get_mut(event_id.borrow()).unwrap().insert(auth_event_id.to_owned());
914 }
915 }
916 }
917}
918
919/// Whether the given event ID belongs to a power event.
920///
921/// See the docs of `is_power_event()` for the definition of a power event.
922fn is_power_event_id<E: Event>(event_id: &EventId, fetch: impl Fn(&EventId) -> Option<E>) -> bool {
923 match fetch(event_id).as_ref() {
924 Some(state) => is_power_event(state),
925 _ => false,
926 }
927}
928
929fn is_type_and_key(event: impl Event, event_type: &TimelineEventType, state_key: &str) -> bool {
930 event.event_type() == event_type && event.state_key() == Some(state_key)
931}
932
933/// Whether the given event is a power event.
934///
935/// Definition in the spec:
936///
937/// > A power event is a state event with type `m.room.power_levels` or `m.room.join_rules`, or a
938/// > state event with type `m.room.member` where the `membership` is `leave` or `ban` and the
939/// > `sender` does not match the `state_key`. The idea behind this is that power events are events
940/// > that might remove someone’s ability to do something in the room.
941fn is_power_event(event: impl Event) -> bool {
942 match event.event_type() {
943 TimelineEventType::RoomPowerLevels
944 | TimelineEventType::RoomJoinRules
945 | TimelineEventType::RoomCreate => event.state_key() == Some(""),
946 TimelineEventType::RoomMember => {
947 let room_member_event = RoomMemberEvent::new(event);
948 if room_member_event.membership().is_ok_and(|membership| {
949 matches!(membership, MembershipState::Leave | MembershipState::Ban)
950 }) {
951 return Some(room_member_event.sender().as_str()) != room_member_event.state_key();
952 }
953
954 false
955 }
956 _ => false,
957 }
958}
959
960/// Convenience trait for adding event type plus state key to state maps.
961pub(crate) trait EventTypeExt {
962 fn with_state_key(self, state_key: impl Into<String>) -> (StateEventType, String);
963}
964
965impl EventTypeExt for StateEventType {
966 fn with_state_key(self, state_key: impl Into<String>) -> (StateEventType, String) {
967 (self, state_key.into())
968 }
969}
970
971impl EventTypeExt for TimelineEventType {
972 fn with_state_key(self, state_key: impl Into<String>) -> (StateEventType, String) {
973 (self.to_string().into(), state_key.into())
974 }
975}
976
977impl<T> EventTypeExt for &T
978where
979 T: EventTypeExt + Clone,
980{
981 fn with_state_key(self, state_key: impl Into<String>) -> (StateEventType, String) {
982 self.to_owned().with_state_key(state_key)
983 }
984}