tuwunel_service/rooms/state_res/
resolve.rs1#[cfg(test)]
2mod tests;
3
4mod auth_difference;
5mod conflicted_subgraph;
6mod iterative_auth_check;
7mod mainline_sort;
8mod power_sort;
9mod split_conflicted;
10
11use std::{
12 collections::{BTreeMap, HashSet},
13 ops::Deref,
14 vec::IntoIter,
15};
16
17use futures::{FutureExt, Stream, StreamExt, TryFutureExt};
18use ruma::{OwnedEventId, events::StateEventType, room_version_rules::RoomVersionRules};
19use tuwunel_core::{
20 Result, debug,
21 itertools::Itertools,
22 matrix::{Event, TypeStateKey, event_id::RandomState},
23 smallvec::SmallVec,
24 trace,
25 utils::{
26 BoolExt,
27 stream::{BroadbandExt, IterStream},
28 },
29};
30
31use self::{
32 auth_difference::auth_difference, conflicted_subgraph::conflicted_subgraph_dfs,
33 iterative_auth_check::iterative_auth_check, mainline_sort::mainline_sort,
34 power_sort::power_sort, split_conflicted::split_conflicted_state,
35};
36#[cfg(test)]
37use super::test_utils;
38
39pub type StateMap<Id> = BTreeMap<TypeStateKey, Id>;
42
43#[derive(Clone)]
48pub struct AuthSet<Id>(Vec<Id>);
49
50pub type ConflictMap<Id> = StateMap<ConflictVec<Id>>;
52
53type ConflictVec<Id> = SmallVec<[Id; 2]>;
57
58type ConflictedSet = HashSet<OwnedEventId, RandomState>;
60
61impl<Id> AuthSet<Id> {
62 #[inline]
67 #[must_use]
68 pub(crate) fn from_distinct(ids: Vec<Id>) -> Self { Self(ids) }
69}
70
71impl<Id> Default for AuthSet<Id> {
72 fn default() -> Self { Self(Vec::new()) }
73}
74
75impl<Id: Ord> FromIterator<Id> for AuthSet<Id> {
76 fn from_iter<I: IntoIterator<Item = Id>>(iter: I) -> Self {
77 Self::from_distinct(
78 iter.into_iter()
79 .sorted_unstable()
80 .dedup()
81 .collect(),
82 )
83 }
84}
85
86impl<Id> IntoIterator for AuthSet<Id> {
87 type IntoIter = IntoIter<Id>;
88 type Item = Id;
89
90 fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
91}
92
93#[tracing::instrument(level = "debug", skip_all)]
119pub async fn resolve<States, AuthSets, FetchExists, ExistsFut, FetchEvent, EventFut, Pdu>(
120 rules: &RoomVersionRules,
121 state_maps: States,
122 auth_sets: AuthSets,
123 fetch: &FetchEvent,
124 exists: &FetchExists,
125 hydra_backports: bool,
126) -> Result<StateMap<OwnedEventId>>
127where
128 States: Stream<Item = StateMap<OwnedEventId>> + Send,
129 AuthSets: Stream<Item = AuthSet<OwnedEventId>> + Send,
130 FetchExists: Fn(OwnedEventId) -> ExistsFut + Sync,
131 ExistsFut: Future<Output = bool> + Send,
132 FetchEvent: Fn(OwnedEventId) -> EventFut + Sync,
133 EventFut: Future<Output = Result<Pdu>> + Send,
134 Pdu: Event + Clone,
135{
136 let (unconflicted_state, conflicted_states) = split_conflicted_state(state_maps).await;
138
139 debug!(
140 unconflicted = unconflicted_state.len(),
141 conflicted_states = conflicted_states.len(),
142 conflicted_events = conflicted_states
143 .values()
144 .fold(0_usize, |a, s| a.saturating_add(s.len())),
145 "unresolved states"
146 );
147
148 trace!(
149 ?unconflicted_state,
150 ?conflicted_states,
151 unconflicted = unconflicted_state.len(),
152 conflicted_states = conflicted_states.len(),
153 "unresolved states"
154 );
155
156 if conflicted_states.is_empty() {
157 return Ok(unconflicted_state.into_iter().collect());
158 }
159
160 let full_conflicted_set =
163 full_conflicted_set(rules, conflicted_states, auth_sets, fetch, exists, hydra_backports)
164 .await;
165
166 let sorted_power_set: Vec<_> = power_sort(rules, &full_conflicted_set, fetch)
171 .inspect_ok(|list| debug!(count = list.len(), "sorted power events"))
172 .inspect_ok(|list| trace!(?list, "sorted power events"))
173 .boxed()
174 .await?;
175
176 let power_set_event_ids: Vec<_> = sorted_power_set
177 .iter()
178 .sorted_unstable()
179 .collect();
180
181 let sorted_power_set = sorted_power_set
182 .iter()
183 .stream()
184 .map(AsRef::as_ref);
185
186 let begin_with_empty_state_map = rules
187 .state_res
188 .v2_rules()
189 .is_some_and(|r| r.begin_iterative_auth_checks_with_empty_state_map)
190 || hydra_backports;
191
192 let initial_state = begin_with_empty_state_map
193 .is_false()
194 .then(|| unconflicted_state.clone())
195 .unwrap_or_default();
196
197 let partially_resolved_state =
201 iterative_auth_check(rules, sorted_power_set, initial_state, fetch)
202 .inspect_ok(|map| debug!(count = map.len(), "partially resolved power state"))
203 .inspect_ok(|map| trace!(?map, "partially resolved power state"))
204 .boxed()
205 .await?;
206
207 let power_ty_sk = (StateEventType::RoomPowerLevels, "".into());
209 let power_event = partially_resolved_state.get(&power_ty_sk);
210 debug!(event_id = ?power_event, "epoch power event");
211
212 let remaining_events: Vec<_> = full_conflicted_set
213 .into_iter()
214 .filter(|id| power_set_event_ids.binary_search(&id).is_err())
215 .collect();
216
217 debug!(count = remaining_events.len(), "remaining events");
218 trace!(list = ?remaining_events, "remaining events");
219
220 let have_remaining_events = !remaining_events.is_empty();
221 let remaining_events = remaining_events
222 .iter()
223 .stream()
224 .map(AsRef::as_ref);
225
226 let sorted_remaining_events = have_remaining_events
230 .then_async(move || mainline_sort(power_event.cloned(), remaining_events, fetch))
231 .boxed();
232
233 let sorted_remaining_events = sorted_remaining_events
234 .await
235 .unwrap_or(Ok(Vec::new()))?;
236
237 debug!(count = sorted_remaining_events.len(), "sorted remaining events");
238 trace!(list = ?sorted_remaining_events, "sorted remaining events");
239
240 let sorted_remaining_events = sorted_remaining_events
241 .iter()
242 .stream()
243 .map(AsRef::as_ref);
244
245 let mut resolved_state =
248 iterative_auth_check(rules, sorted_remaining_events, partially_resolved_state, fetch)
249 .boxed()
250 .await?;
251
252 resolved_state.extend(unconflicted_state);
256
257 debug!(resolved_state = resolved_state.len(), "resolved state");
258 trace!(?resolved_state, "resolved state");
259
260 Ok(resolved_state)
261}
262
263#[tracing::instrument(
264 name = "conflicted",
265 level = "debug",
266 skip_all,
267 fields(
268 states = conflicted_states.len(),
269 events = conflicted_states.values().flatten().count()
270 ),
271)]
272async fn full_conflicted_set<AuthSets, FetchExists, ExistsFut, FetchEvent, EventFut, Pdu>(
273 rules: &RoomVersionRules,
274 conflicted_states: ConflictMap<OwnedEventId>,
275 auth_sets: AuthSets,
276 fetch: &FetchEvent,
277 exists: &FetchExists,
278 hydra_backports: bool,
279) -> ConflictedSet
280where
281 AuthSets: Stream<Item = AuthSet<OwnedEventId>> + Send,
282 FetchExists: Fn(OwnedEventId) -> ExistsFut + Sync,
283 ExistsFut: Future<Output = bool> + Send,
284 FetchEvent: Fn(OwnedEventId) -> EventFut + Sync,
285 EventFut: Future<Output = Result<Pdu>> + Send,
286 Pdu: Event,
287{
288 let consider_conflicted_subgraph = rules
289 .state_res
290 .v2_rules()
291 .is_some_and(|rules| rules.consider_conflicted_state_subgraph)
292 || hydra_backports;
293
294 let conflicted_state_set: Vec<_> = conflicted_states
295 .values()
296 .flatten()
297 .sorted_unstable()
298 .dedup()
299 .collect();
300
301 let conflicted_subgraph = consider_conflicted_subgraph
303 .then_async(async || conflicted_subgraph_dfs(&conflicted_state_set, fetch))
304 .map(Option::into_iter)
305 .map(IterStream::stream)
306 .flatten_stream()
307 .flatten()
308 .boxed();
309
310 let conflicted_state_ids = conflicted_state_set
311 .iter()
312 .map(Deref::deref)
313 .cloned()
314 .stream();
315
316 auth_difference(auth_sets)
317 .chain(conflicted_state_ids)
318 .broad_filter_map(async |id| exists(id.clone()).await.then_some(id))
319 .chain(conflicted_subgraph)
320 .collect::<ConflictedSet>()
321 .inspect(|set| debug!(count = set.len(), "full conflicted set"))
322 .inspect(|set| trace!(?set, "full conflicted set"))
323 .await
324}