Skip to main content

matrix_sdk/event_cache/
redecryptor.rs

1// Copyright 2025 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The Redecryptor (affectionately known as R2D2) is a layer and long-running
16//! background task which handles redecryption of events in case we couldn't
17//! decrypt them immediately.
18//!
19//! There are various reasons why a room key might not be available immediately
20//! when the event becomes available:
21//!     - The to-device message containing the room key just arrives late, i.e.
22//!       after the room event.
23//!     - The event is a historic event and we need to first download the room
24//!       key from the backup.
25//!     - The event is a historic event in a previously unjoined room, we need
26//!       to receive historic room keys as defined in [MSC3061].
27//!
28//! R2D2 listens to the [`OlmMachine`] for received room keys and new
29//! m.room_key.withheld events.
30//!
31//! If a new room key has been received, it attempts to find any UTDs in the
32//! [`EventCache`]. If R2D2 decrypts any UTDs from the event cache, it will
33//! replace the events in the cache and send out new [`RoomEventCacheUpdate`]s
34//! to any of its listeners.
35//!
36//! If a new withheld info has been received, it attempts to find any relevant
37//! events and updates the [`EncryptionInfo`] of an event.
38//!
39//! There's an additional gotcha: the [`OlmMachine`] might get recreated by
40//! calls to [`BaseClient::regenerate_olm()`]. When this happens, we will
41//! receive a `None` on the room keys stream and we need to re-listen to it.
42//!
43//! Another gotcha is that room keys might be received on another process if the
44//! [`Client`] is operating on a Apple iOS device. A separate process is used
45//! in this case to receive push notifications. In this case, the room key will
46//! be received and R2D2 won't get notified about it. To work around this,
47//! decryption requests can be explicitly sent to R2D2.
48//!
49//! The final gotcha is that a room key might be received just in between the
50//! time the event was initially tried to be decrypted and the time it took to
51//! persist it in the event cache. To handle this race condition, R2D2 listens
52//! to the event cache and attempts to decrypt any UTDs the event cache
53//! persists.
54//!
55//! In the graph below, the Timeline block is meant to be the `Timeline` from
56//! the `matrix-sdk-ui` crate, but it could be any other listener that
57//! subscribes to [`RedecryptorReport`] stream.
58//!
59//! ```markdown
60//! 
61//!      .----------------------.
62//!     |                        |
63//!     |      Beeb, boop!       |
64//!     |                        .
65//!      ----------------------._ \
66//!                               -;  _____
67//!                                 .`/L|__`.
68//!                                / =[_]O|` \
69//!                                |"+_____":|
70//!                              __:='|____`-:__
71//!                             ||[] ||====|| []||
72//!                             ||[] ||====|| []||
73//!                             |:== ||====|| ==:|
74//!                             ||[] ||====|| []||
75//!                             ||[] ||====|| []||
76//!                            _||_  ||====||  _||_
77//!                           (====) |:====:| (====)
78//!                            }--{  | |  | |  }--{
79//!                           (____) |_|  |_| (____)
80//!
81//!                              ┌─────────────┐
82//!                              │             │
83//!                  ┌───────────┤   Timeline  │◄────────────┐
84//!                  │           │             │             │
85//!                  │           └──────▲──────┘             │
86//!                  │                  │                    │
87//!                  │                  │                    │
88//!                  │                  │                    │
89//!              Decryption             │                Redecryptor
90//!                request              │                  report
91//!                  │        RoomEventCacheUpdates          │
92//!                  │                  │                    │
93//!                  │                  │                    │
94//!                  │      ┌───────────┴───────────┐        │
95//!                  │      │                       │        │
96//!                  └──────►         R2D2          │────────┘
97//!                         │                       │
98//!                         └──▲─────────────────▲──┘
99//!                            │                 │
100//!                            │                 │
101//!                            │                 │
102//!                         Received        Received room
103//!                          events          keys stream
104//!                            │                 │
105//!                            │                 │
106//!                            │                 │
107//!                    ┌───────┴──────┐  ┌───────┴──────┐
108//!                    │              │  │              │
109//!                    │  Event Cache │  │  OlmMachine  │
110//!                    │              │  │              │
111//!                    └──────────────┘  └──────────────┘
112//! ```
113//!
114//! [MSC3061]: https://github.com/matrix-org/matrix-spec/pull/1655#issuecomment-2213152255
115
116use std::{
117    borrow::Cow,
118    collections::{BTreeMap, BTreeSet},
119    pin::Pin,
120    sync::Weak,
121};
122
123use as_variant::as_variant;
124use futures_core::Stream;
125use futures_util::{StreamExt, future::try_join_all, pin_mut};
126#[cfg(doc)]
127use matrix_sdk_base::{BaseClient, crypto::OlmMachine};
128use matrix_sdk_base::{
129    crypto::{
130        store::types::{RoomKeyInfo, RoomKeyWithheldInfo},
131        types::events::room::encrypted::EncryptedEvent,
132    },
133    deserialized_responses::{DecryptedRoomEvent, TimelineEvent, TimelineEventKind},
134    locks::Mutex,
135    task_monitor::BackgroundTaskHandle,
136    timer,
137};
138#[cfg(doc)]
139use matrix_sdk_common::deserialized_responses::EncryptionInfo;
140use ruma::{
141    OwnedEventId, OwnedRoomId, RoomId,
142    events::{AnySyncTimelineEvent, room::encrypted::OriginalSyncRoomEncryptedEvent},
143    push::Action,
144    serde::Raw,
145};
146use tokio::sync::{
147    broadcast::{self, Sender},
148    mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel},
149};
150use tokio_stream::wrappers::{
151    BroadcastStream, UnboundedReceiverStream, errors::BroadcastStreamRecvError,
152};
153use tracing::{info, instrument, trace, warn};
154
155#[cfg(doc)]
156use super::RoomEventCache;
157use super::{
158    EventCache, EventCacheError, EventCacheInner, EventsOrigin, RoomEventCacheGenericUpdate,
159    RoomEventCacheUpdate, TimelineVectorDiffs,
160    caches::{
161        EventLocation, event_linked_chunk::EventLinkedChunk, room::RoomEventCacheLinkedChunkUpdate,
162    },
163};
164use crate::{Client, Result, Room, encryption::backups::BackupState, room::PushContext};
165
166type SessionId<'a> = &'a str;
167type OwnedSessionId = String;
168
169type EventIdAndUtd = (OwnedEventId, Raw<AnySyncTimelineEvent>);
170type EventIdAndEvent = (OwnedEventId, DecryptedRoomEvent);
171
172#[derive(Clone)]
173pub(super) struct ResolvedUtd {
174    pub event_id: OwnedEventId,
175    decrypted_event: DecryptedRoomEvent,
176    actions: Option<Vec<Action>>,
177}
178
179#[derive(Clone)]
180pub(super) enum MaybeResolvedEvent {
181    NotYet(ResolvedUtd),
182    Resolved(TimelineEvent),
183}
184
185impl MaybeResolvedEvent {
186    pub fn try_resolve_event(self, mut unresolved_event: TimelineEvent) -> Self {
187        match self {
188            Self::NotYet(resolved_utd) => {
189                // There is a race between the multiple sources of updates. It's possible
190                // that two sources trigger a decryption for the same event (for example,
191                // the room key stream and the event cache updates). It is then likely that
192                // the event has been already resolved. This race is fine, but we should
193                // avoid to replace an event that has already been resolved as it is a
194                // non-negligible operation.
195                //
196                // Note that a simple check like “event's kind is `UnableToDecrypt`” is not
197                // enough. The event can already be decrypted but its encryption info can
198                // change. So we must ensure they are also different.
199                if matches!(unresolved_event.kind, TimelineEventKind::UnableToDecrypt { .. })
200                    || unresolved_event.encryption_info()
201                        != Some(&resolved_utd.decrypted_event.encryption_info)
202                {
203                    unresolved_event.kind =
204                        TimelineEventKind::Decrypted(resolved_utd.decrypted_event);
205
206                    if let Some(actions) = resolved_utd.actions {
207                        unresolved_event.set_push_actions(actions);
208                    }
209
210                    // The unresolved event becomes resolved :-].
211                    Self::Resolved(unresolved_event)
212                } else {
213                    Self::NotYet(resolved_utd)
214                }
215            }
216
217            Self::Resolved(event) => Self::Resolved(event),
218        }
219    }
220
221    pub fn as_resolved(&self) -> Option<&TimelineEvent> {
222        if let Self::Resolved(event) = self { Some(event) } else { None }
223    }
224}
225
226/// Internal trait to resolve events on a `&[MaybeResolvedEvent]` with the help
227/// of `Cow` to avoid copying if no event is newly resolved.
228pub(super) trait TryResolveEvents {
229    fn try_resolve_events(
230        &self,
231        event_linked_chunk: &EventLinkedChunk,
232    ) -> Cow<'_, [MaybeResolvedEvent]>;
233}
234
235impl TryResolveEvents for [MaybeResolvedEvent] {
236    fn try_resolve_events(
237        &self,
238        event_linked_chunk: &EventLinkedChunk,
239    ) -> Cow<'_, [MaybeResolvedEvent]> {
240        let mut new_resolved_events = Cow::Borrowed(self);
241
242        for (nth, resolved_event) in self.iter().enumerate() {
243            match resolved_event {
244                MaybeResolvedEvent::NotYet(resolved_utd) => {
245                    // Event has not been resolved. Let's try to locate the corresponding event with
246                    // the provided `EventLinkedChunk` and try to resolve it.
247
248                    if let Some((_location, event)) =
249                        event_linked_chunk.find_event(&resolved_utd.event_id)
250                    {
251                        let new_resolved_event = MaybeResolvedEvent::NotYet(resolved_utd.clone())
252                            .try_resolve_event(event);
253
254                        if matches!(new_resolved_event, MaybeResolvedEvent::Resolved(_)) {
255                            // Use `slice::get_unchecked_mut` to avoid a bounds check.
256                            //
257                            // SAFETY: `self` and `new_resolved_events` have the same size and
258                            // represent the same data. Thus, the index `nth` exists in
259                            // `new_resolved_events`.
260                            unsafe {
261                                *new_resolved_events.to_mut().get_unchecked_mut(nth) =
262                                    new_resolved_event;
263                            }
264                        }
265                    }
266                }
267
268                MaybeResolvedEvent::Resolved(_event) => {
269                    // Event has already been resolved. Nothing to do.
270                }
271            }
272        }
273
274        new_resolved_events
275    }
276}
277
278/// The information sent across the channel to the long-running task requesting
279/// that the supplied set of sessions be retried.
280#[derive(Debug, Clone)]
281pub struct DecryptionRetryRequest {
282    /// The room ID of the room the events belong to.
283    pub room_id: OwnedRoomId,
284    /// Events that are not decrypted.
285    pub utd_session_ids: BTreeSet<OwnedSessionId>,
286    /// Events that are decrypted but might need to have their
287    /// [`EncryptionInfo`] refreshed.
288    pub refresh_info_session_ids: BTreeSet<OwnedSessionId>,
289}
290
291/// A report coming from the redecryptor.
292#[derive(Debug, Clone)]
293pub enum RedecryptorReport {
294    /// Events which we were able to decrypt.
295    ResolvedUtds {
296        /// The room ID of the room the events belong to.
297        room_id: OwnedRoomId,
298        /// The list of event IDs of the decrypted events.
299        events: BTreeSet<OwnedEventId>,
300    },
301    /// The redecryptor might have missed some room keys so it might not have
302    /// re-decrypted events that are now decryptable.
303    Lagging,
304    /// A room key backup has become available.
305    ///
306    /// This means that components might want to tell R2D2 about events they
307    /// care about to attempt a decryption.
308    BackupAvailable,
309}
310
311pub(super) struct RedecryptorChannels {
312    utd_reporter: Sender<RedecryptorReport>,
313    pub(super) decryption_request_sender: UnboundedSender<DecryptionRetryRequest>,
314    pub(super) decryption_request_receiver:
315        Mutex<Option<UnboundedReceiver<DecryptionRetryRequest>>>,
316}
317
318impl RedecryptorChannels {
319    pub(super) fn new() -> Self {
320        let (utd_reporter, _) = broadcast::channel(100);
321        let (decryption_request_sender, decryption_request_receiver) = unbounded_channel();
322
323        Self {
324            utd_reporter,
325            decryption_request_sender,
326            decryption_request_receiver: Mutex::new(Some(decryption_request_receiver)),
327        }
328    }
329}
330
331/// A function which can be used to filter and map [`TimelineEvent`]s into a
332/// tuple of event ID and raw [`AnySyncTimelineEvent`].
333///
334/// The tuple can be used to attempt to redecrypt events.
335fn filter_timeline_event_to_utd(
336    event: TimelineEvent,
337) -> Option<(OwnedEventId, Raw<AnySyncTimelineEvent>)> {
338    let event_id = event.event_id().map(ToOwned::to_owned);
339
340    // Only pick out events that are UTDs, get just the Raw event as this is what
341    // the OlmMachine needs.
342    let event = as_variant!(event.kind, TimelineEventKind::UnableToDecrypt { event, .. } => event);
343    // Zip the event ID and event together so we don't have to pick out the event ID
344    // again. We need the event ID to replace the event in the cache.
345    event_id.zip(event)
346}
347
348/// A function which can be used to filter an map [`TimelineEvent`]s into a
349/// tuple of event ID and [`DecryptedRoomEvent`].
350///
351/// The tuple can be used to attempt to update the encryption info of the
352/// decrypted event.
353fn filter_timeline_event_to_decrypted(
354    event: TimelineEvent,
355) -> Option<(OwnedEventId, DecryptedRoomEvent)> {
356    let event_id = event.event_id().map(ToOwned::to_owned);
357
358    let event = as_variant!(event.kind, TimelineEventKind::Decrypted(event) => event);
359    // Zip the event ID and event together so we don't have to pick out the event ID
360    // again. We need the event ID to replace the event in the cache.
361    event_id.zip(event)
362}
363
364impl EventCache {
365    /// Retrieve a set of events that we weren't able to decrypt.
366    ///
367    /// # Arguments
368    ///
369    /// * `room_id` - The ID of the room where the events were sent to.
370    /// * `session_id` - The unique ID of the room key that was used to encrypt
371    ///   the event.
372    async fn all_encrypted_events(
373        &self,
374        room_id: &RoomId,
375        session_id: SessionId<'_>,
376    ) -> Result<Vec<EventIdAndUtd>, EventCacheError> {
377        let caches = self.inner.all_caches_for_room(room_id).await?;
378
379        Ok(caches
380            .all_events_of_type(Some("m.room.encrypted"), Some(session_id))
381            .await?
382            .filter_map(filter_timeline_event_to_utd)
383            .collect())
384    }
385
386    /// Retrieve a set of events that we weren't able to decrypt from the memory
387    /// of the event cache.
388    async fn all_in_memory_encrypted_events(&self) -> BTreeMap<OwnedRoomId, Vec<EventIdAndUtd>> {
389        let mut utds = BTreeMap::new();
390
391        for (room_id, caches) in self.inner.by_room.read().await.iter() {
392            let room_utds: Vec<_> = caches
393                .all_in_memory_events()
394                .await
395                .into_iter()
396                .flatten()
397                .filter_map(filter_timeline_event_to_utd)
398                .collect();
399
400            utds.insert(room_id.to_owned(), room_utds);
401        }
402
403        utds
404    }
405
406    async fn all_decrypted_events(
407        &self,
408        room_id: &RoomId,
409        session_id: SessionId<'_>,
410    ) -> Result<Vec<EventIdAndEvent>, EventCacheError> {
411        let caches = self.inner.all_caches_for_room(room_id).await?;
412
413        Ok(caches
414            .all_events_of_type(None, Some(session_id))
415            .await?
416            .filter_map(filter_timeline_event_to_decrypted)
417            .collect())
418    }
419
420    async fn all_in_memory_decrypted_events(&self) -> BTreeMap<OwnedRoomId, Vec<EventIdAndEvent>> {
421        let mut decrypted_events = BTreeMap::new();
422
423        for (room_id, caches) in self.inner.by_room.read().await.iter() {
424            let room_utds: Vec<_> = caches
425                .all_in_memory_events()
426                .await
427                .into_iter()
428                .flatten()
429                .filter_map(filter_timeline_event_to_decrypted)
430                .collect();
431
432            decrypted_events.insert(room_id.to_owned(), room_utds);
433        }
434
435        decrypted_events
436    }
437
438    /// Handle a chunk of events that we were previously unable to decrypt but
439    /// have now successfully decrypted.
440    ///
441    /// This function will replace the existing UTD events in memory and the
442    /// store and send out a [`RoomEventCacheUpdate`] for the newly
443    /// decrypted events.
444    ///
445    /// # Arguments
446    ///
447    /// * `room_id` - The ID of the room where the events were sent to.
448    /// * `events` - A chunk of events that were successfully decrypted.
449    #[instrument(skip_all, fields(room_id))]
450    async fn on_resolved_utds(
451        &self,
452        room_id: &RoomId,
453        resolved_utds: Vec<ResolvedUtd>,
454    ) -> Result<(), EventCacheError> {
455        if resolved_utds.is_empty() {
456            trace!("No events were redecrypted or updated, nothing to replace");
457            return Ok(());
458        }
459
460        timer!("Resolving UTDs");
461
462        let event_ids: BTreeSet<_> =
463            resolved_utds.iter().map(|resolved_utd| resolved_utd.event_id.clone()).collect();
464
465        let all_caches = self.inner.all_caches_for_room(room_id).await?;
466        let mut maybe_resolved_events = Vec::with_capacity(resolved_utds.len());
467
468        // # Room cache, thread caches, and pinned-event cache
469        //
470        // For each resolved UTD, find the corresponding event (either in-store or
471        // in-memory of the room cache), build the resolved event, and replace it in the
472        // store. We use the room cache for that because it contains all events (there
473        // is an exception with the event-focused cache, see below).
474        //
475        // # Event-focused cache
476        //
477        // Events received by the sync or pagination are not forwarded to the
478        // event-focused cache: it handles its own set of events. All of them live
479        // in-memory, there are not put in the store by any cache. So this cache will
480        // miss all UTD resolutions. To address that, the event-focused cache
481        // resolves UTD on its own, without the general logic described above.
482        {
483            let room_cache = &all_caches.room;
484            let mut state = room_cache.state().write().await?;
485
486            let mut maybe_resolved_in_memory_events = Vec::new();
487
488            for resolved_utd in resolved_utds {
489                // Try to locate the event (either in-store or in-memory).
490                if let Some((location, event)) = state.find_event(&resolved_utd.event_id).await? {
491                    let maybe_resolved_event =
492                        MaybeResolvedEvent::NotYet(resolved_utd).try_resolve_event(event);
493
494                    // It is an in-memory event, let's keep it apart to replace in-memory UTDs.
495                    if matches!(location, EventLocation::Memory(_)) {
496                        maybe_resolved_in_memory_events.push(maybe_resolved_event.clone());
497                    }
498
499                    // Even is known, let's keep it for later, even if unresolved.
500                    maybe_resolved_events.push(maybe_resolved_event);
501                } else {
502                    // Event is unknown by the room cache, the thread caches, nor the pinned-events
503                    // cache. However, it might be known by an event-focused cache! So let's keep it
504                    // for later.
505                    maybe_resolved_events.push(MaybeResolvedEvent::NotYet(resolved_utd));
506                }
507            }
508
509            // Replace all resolved events in the store.
510            state
511                .save_events(
512                    maybe_resolved_events
513                        .iter()
514                        .filter_map(|resolved_event| resolved_event.as_resolved())
515                        .cloned(),
516                )
517                .await?;
518
519            // Now, replace the in-memory events.
520            let timeline_event_diffs = state
521                .replace_in_memory_utds(&maybe_resolved_in_memory_events)
522                .await?
523                .unwrap_or_default();
524
525            if !timeline_event_diffs.is_empty() {
526                state.update_sender.send(
527                    RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs {
528                        diffs: timeline_event_diffs,
529                        origin: EventsOrigin::Cache,
530                    }),
531                    Some(RoomEventCacheGenericUpdate { room_id: room_id.to_owned() }),
532                );
533            }
534        }
535
536        // Resolve in-memory UTDs on the thread caches.
537        {
538            // TODO: This ain't great for performance; there shouldn't be
539            // that many thread caches alive at the same time, but they could
540            // accumulate over time. Consider keeping track of which linked
541            // chunk contains which event ID, to avoid doing the linear searches
542            // here.
543
544            // Replaces UTDs in each thread, and maybe update the thread summary.
545            for (thread_id, thread_cache) in try_join_all(
546                all_caches.threads.read().await.iter().map(|(thread_id, thread_cache)| async {
547                    Result::<_, EventCacheError>::Ok(
548                        // If at least one event has been replaced, return the `thread_id` and the
549                        // `thread_cache` to update the thread summary later.
550                        thread_cache
551                            .replace_in_memory_utds(&maybe_resolved_events)
552                            .await?
553                            .then(|| (thread_id.clone(), thread_cache.clone())),
554                    )
555                }),
556            )
557            .await?
558            .into_iter()
559            // Filter out results that are `None`, i.e. a thread where no UTD has been replaced.
560            .flatten()
561            {
562                let new_thread_summary =
563                    thread_cache.state().read().await?.compute_thread_summary().await?;
564
565                all_caches.room.update_thread_summary(&thread_id, new_thread_summary).await?;
566            }
567        }
568
569        // Resolve in-memory UTDs on the pinned-events cache.
570        if let Some(pinned_events_cache) = all_caches.pinned_events.get() {
571            pinned_events_cache.replace_in_memory_utds(&maybe_resolved_events).await?;
572        }
573
574        // Resolve in-memory UTDs on the event-focused caches.
575        {
576            // TODO: This ain't great for performance; there shouldn't be that many
577            // event-focused caches alive at the same time, but they could
578            // accumulate over time. Consider keeping track of which linked chunk
579            // contains which event ID, to avoid doing the linear searches here.
580            try_join_all(all_caches.event_focused.read().await.values().map(
581                |event_focused_cache| {
582                    event_focused_cache.replace_in_memory_utds(&maybe_resolved_events)
583                },
584            ))
585            .await?;
586        }
587
588        let report =
589            RedecryptorReport::ResolvedUtds { room_id: room_id.to_owned(), events: event_ids };
590        let _ = self.inner.redecryption_channels.utd_reporter.send(report);
591
592        Ok(())
593    }
594
595    /// Attempt to decrypt a single event.
596    async fn decrypt_event(
597        &self,
598        room_id: &RoomId,
599        room: Option<&Room>,
600        push_context: Option<&PushContext>,
601        event: &Raw<EncryptedEvent>,
602    ) -> Option<(DecryptedRoomEvent, Option<Vec<Action>>)> {
603        if let Some(room) = room {
604            match room
605                .decrypt_event(
606                    event.cast_ref_unchecked::<OriginalSyncRoomEncryptedEvent>(),
607                    push_context,
608                )
609                .await
610            {
611                Ok(maybe_decrypted) => {
612                    let actions = maybe_decrypted.push_actions().map(|a| a.to_vec());
613
614                    if let TimelineEventKind::Decrypted(decrypted) = maybe_decrypted.kind {
615                        Some((decrypted, actions))
616                    } else {
617                        warn!(
618                            "Failed to redecrypt an event despite receiving a room key or request to redecrypt"
619                        );
620                        None
621                    }
622                }
623                Err(e) => {
624                    warn!(
625                        "Failed to redecrypt an event despite receiving a room key or request to redecrypt {e:?}"
626                    );
627                    None
628                }
629            }
630        } else {
631            let client = self.inner.client().ok()?;
632            let machine = client.olm_machine().await;
633            let machine = machine.as_ref()?;
634
635            match machine.decrypt_room_event(event, room_id, client.decryption_settings()).await {
636                Ok(decrypted) => Some((decrypted, None)),
637                Err(e) => {
638                    warn!(
639                        "Failed to redecrypt an event despite receiving a room key or a request to redecrypt {e:?}"
640                    );
641                    None
642                }
643            }
644        }
645    }
646
647    /// Attempt to redecrypt events after a room key with the given session ID
648    /// has been received.
649    #[instrument(skip_all, fields(room_id, session_id))]
650    async fn retry_decryption(
651        &self,
652        room_id: &RoomId,
653        session_id: SessionId<'_>,
654    ) -> Result<(), EventCacheError> {
655        // Get all the relevant UTDs.
656        let events = self.all_encrypted_events(room_id, session_id).await?;
657        self.retry_decryption_for_events(room_id, events).await
658    }
659
660    /// Attempt to redecrypt events that were persisted in the event cache.
661    #[instrument(skip_all, fields(updates.linked_chunk_id))]
662    async fn retry_decryption_for_event_cache_updates(
663        &self,
664        updates: RoomEventCacheLinkedChunkUpdate,
665    ) -> Result<(), EventCacheError> {
666        let room_id = updates.linked_chunk_id.room_id();
667        let events: Vec<_> = updates
668            .updates
669            .into_iter()
670            .flat_map(|updates| updates.into_items())
671            .filter_map(filter_timeline_event_to_utd)
672            .collect();
673
674        self.retry_decryption_for_events(room_id, events).await
675    }
676
677    async fn retry_decryption_for_in_memory_events(&self) {
678        let utds = self.all_in_memory_encrypted_events().await;
679
680        for (room_id, utds) in utds.into_iter() {
681            if let Err(e) = self.retry_decryption_for_events(&room_id, utds).await {
682                warn!(%room_id, "Failed to redecrypt in-memory events {e:?}");
683            }
684        }
685    }
686
687    /// Attempt to redecrypt a chunk of UTDs.
688    #[instrument(skip_all, fields(room_id, session_id))]
689    async fn retry_decryption_for_events(
690        &self,
691        room_id: &RoomId,
692        events: Vec<EventIdAndUtd>,
693    ) -> Result<(), EventCacheError> {
694        trace!("Retrying to decrypt");
695
696        if events.is_empty() {
697            trace!("No relevant events found.");
698            return Ok(());
699        }
700
701        let room = self.inner.client().ok().and_then(|client| client.get_room(room_id));
702        let push_context =
703            if let Some(room) = &room { room.push_context().await.ok().flatten() } else { None };
704
705        // Let's attempt to decrypt them them.
706        let mut decrypted_events = Vec::with_capacity(events.len());
707
708        for (event_id, event) in events {
709            // If we managed to decrypt the event, and we should have to since we received
710            // the room key for this specific event, then replace the event.
711            if let Some((decrypted_event, actions)) = self
712                .decrypt_event(
713                    room_id,
714                    room.as_ref(),
715                    push_context.as_ref(),
716                    event.cast_ref_unchecked(),
717                )
718                .await
719            {
720                decrypted_events.push(ResolvedUtd { event_id, decrypted_event, actions });
721            }
722        }
723
724        if !decrypted_events.is_empty() && tracing::level_enabled!(tracing::Level::TRACE) {
725            let event_ids: BTreeSet<_> =
726                decrypted_events.iter().map(|resolved_utd| &resolved_utd.event_id).collect();
727
728            trace!(?event_ids, "Successfully redecrypted events");
729        }
730
731        // Replace the events and notify listeners that UTDs have been replaced with
732        // decrypted events.
733        self.on_resolved_utds(room_id, decrypted_events).await?;
734
735        Ok(())
736    }
737
738    /// Attempt to update the encryption info for the given list of events.
739    async fn update_encryption_info_for_events(
740        &self,
741        room: &Room,
742        events: Vec<EventIdAndEvent>,
743    ) -> Result<(), EventCacheError> {
744        // Let's attempt to update their encryption info.
745        let mut updated_events = Vec::with_capacity(events.len());
746
747        for (event_id, mut event) in events {
748            if let Some(session_id) = event.encryption_info.session_id() {
749                let new_encryption_info =
750                    room.get_encryption_info(session_id, &event.encryption_info.sender).await;
751
752                // Only create a replacement if the encryption info actually changed.
753                if let Some(new_encryption_info) = new_encryption_info
754                    && event.encryption_info != new_encryption_info
755                {
756                    event.encryption_info = new_encryption_info;
757                    updated_events.push(ResolvedUtd {
758                        event_id,
759                        decrypted_event: event,
760                        actions: None,
761                    });
762                }
763            }
764        }
765
766        if !updated_events.is_empty() && tracing::level_enabled!(tracing::Level::TRACE) {
767            let event_ids: BTreeSet<_> =
768                updated_events.iter().map(|resolved_utd| &resolved_utd.event_id).collect();
769
770            trace!(?event_ids, "Replacing the encryption info of some events");
771        }
772
773        self.on_resolved_utds(room.room_id(), updated_events).await
774    }
775
776    #[instrument(skip_all, fields(room_id, session_id))]
777    async fn update_encryption_info(
778        &self,
779        room_id: &RoomId,
780        session_id: SessionId<'_>,
781    ) -> Result<(), EventCacheError> {
782        trace!("Updating encryption info");
783
784        let Ok(client) = self.inner.client() else {
785            return Ok(());
786        };
787
788        let Some(room) = client.get_room(room_id) else {
789            return Ok(());
790        };
791
792        // Get all the relevant events.
793        let events = self.all_decrypted_events(room_id, session_id).await?;
794
795        if events.is_empty() {
796            trace!("No relevant events found.");
797            return Ok(());
798        }
799
800        // Let's attempt to update their encryption info.
801        self.update_encryption_info_for_events(&room, events).await
802    }
803
804    async fn retry_update_encryption_info_for_in_memory_events(&self) {
805        let decrypted_events = self.all_in_memory_decrypted_events().await;
806
807        for (room_id, events) in decrypted_events.into_iter() {
808            let Some(room) = self.inner.client().ok().and_then(|c| c.get_room(&room_id)) else {
809                continue;
810            };
811
812            if let Err(e) = self.update_encryption_info_for_events(&room, events).await {
813                warn!(
814                    %room_id,
815                    "Failed to replace the encryption info for in-memory events {e:?}"
816                );
817            }
818        }
819    }
820
821    /// Retry to decrypt and update the encryption info of all the events
822    /// contained in the memory part of the event cache.
823    ///
824    /// This list of events will map one-to-one to the events components
825    /// subscribed to the event cache are have received and are keeping cached.
826    ///
827    /// If components subscribed to the event cache are doing additional
828    /// caching, they'll need to listen to [`RedecryptorReport`]s and
829    /// explicitly request redecryption attempts using
830    /// [`EventCache::request_decryption`].
831    async fn retry_in_memory_events(&self) {
832        self.retry_decryption_for_in_memory_events().await;
833        self.retry_update_encryption_info_for_in_memory_events().await;
834    }
835
836    /// Explicitly request the redecryption of a set of events.
837    ///
838    /// The redecryption logic in the event cache might sometimes miss that a
839    /// room key has become available and that a certain set of events has
840    /// become decryptable.
841    ///
842    /// This might happen because some room keys might arrive in a separate
843    /// process handling push notifications or if a room key arrives but the
844    /// process shuts down before we could have decrypted the events.
845    ///
846    /// For this reason it is useful to tell the event cache explicitly that
847    /// some events should be retried to be redecrypted.
848    ///
849    /// This method allows you to do so. The events that get decrypted, if any,
850    /// will be advertised over the usual event cache subscription mechanism
851    /// which can be accessed using the [`RoomEventCache::subscribe()`]
852    /// method.
853    ///
854    /// # Examples
855    ///
856    /// ```no_run
857    /// # use matrix_sdk::{Client, event_cache::DecryptionRetryRequest};
858    /// # use url::Url;
859    /// # use ruma::owned_room_id;
860    /// # use std::collections::BTreeSet;
861    /// # async {
862    /// # let homeserver = Url::parse("http://localhost:8080")?;
863    /// # let client = Client::new(homeserver).await?;
864    /// let event_cache = client.event_cache();
865    /// let room_id = owned_room_id!("!my_room:localhost");
866    ///
867    /// let request = DecryptionRetryRequest {
868    ///     room_id,
869    ///     utd_session_ids: BTreeSet::from(["session_id".into()]),
870    ///     refresh_info_session_ids: BTreeSet::new(),
871    /// };
872    ///
873    /// event_cache.request_decryption(request);
874    /// # anyhow::Ok(()) };
875    /// ```
876    pub fn request_decryption(&self, request: DecryptionRetryRequest) {
877        let _ =
878            self.inner.redecryption_channels.decryption_request_sender.send(request).inspect_err(
879                |_| warn!("Requesting a decryption while the redecryption task has been shut down"),
880            );
881    }
882
883    /// Subscribe to reports that the redecryptor generates.
884    ///
885    /// The redecryption logic in the event cache might sometimes miss that a
886    /// room key has become available and that a certain set of events has
887    /// become decryptable.
888    ///
889    /// This might happen because some room keys might arrive in a separate
890    /// process handling push notifications or if room keys arrive faster than
891    /// we can handle them.
892    ///
893    /// This stream can be used to get notified about such situations as well as
894    /// a general channel where the event cache reports which events got
895    /// successfully redecrypted.
896    ///
897    /// # Examples
898    ///
899    /// ```no_run
900    /// # use matrix_sdk::{Client, event_cache::RedecryptorReport};
901    /// # use url::Url;
902    /// # use tokio_stream::StreamExt;
903    /// # async {
904    /// # let homeserver = Url::parse("http://localhost:8080")?;
905    /// # let client = Client::new(homeserver).await?;
906    /// let event_cache = client.event_cache();
907    ///
908    /// let mut stream = event_cache.subscribe_to_decryption_reports();
909    ///
910    /// while let Some(Ok(report)) = stream.next().await {
911    ///     match report {
912    ///         RedecryptorReport::Lagging => {
913    ///             // The event cache might have missed to redecrypt some events. We should tell
914    ///             // it which events we care about, i.e. which events we're displaying to the
915    ///             // user, and let it redecrypt things with an explicit request.
916    ///         }
917    ///         RedecryptorReport::BackupAvailable => {
918    ///             // A backup has become available. We can, just like in the Lagging case, tell
919    ///             // the event cache to attempt to redecrypt some events.
920    ///             //
921    ///             // This is only necessary with the BackupDownloadStrategy::OnDecryptionFailure
922    ///             // as the decryption attempt in this case will trigger the download of the
923    ///             // room key from the backup.
924    ///         }
925    ///         RedecryptorReport::ResolvedUtds { .. } => {
926    ///             // This may be interesting for statistical reasons or in case we'd like to
927    ///             // fetch and inspect these events in some manner.
928    ///         }
929    ///     }
930    /// }
931    /// # anyhow::Ok(()) };
932    /// ```
933    pub fn subscribe_to_decryption_reports(
934        &self,
935    ) -> impl Stream<Item = Result<RedecryptorReport, BroadcastStreamRecvError>> {
936        BroadcastStream::new(self.inner.redecryption_channels.utd_reporter.subscribe())
937    }
938}
939
940#[inline(always)]
941fn upgrade_event_cache(cache: &Weak<EventCacheInner>) -> Option<EventCache> {
942    cache.upgrade().map(|inner| EventCache { inner })
943}
944
945async fn send_report_and_retry_memory_events(
946    cache: &Weak<EventCacheInner>,
947    report: RedecryptorReport,
948) -> Result<(), ()> {
949    let Some(cache) = upgrade_event_cache(cache) else {
950        return Err(());
951    };
952
953    cache.retry_in_memory_events().await;
954    let _ = cache.inner.redecryption_channels.utd_reporter.send(report);
955
956    Ok(())
957}
958
959/// Struct holding on to the redecryption task.
960///
961/// This struct implements the bulk of the redecryption task. It listens to the
962/// various streams that should trigger redecryption attempts.
963///
964/// For more info see the [module level docs](self).
965pub(crate) struct Redecryptor {
966    _task: BackgroundTaskHandle,
967}
968
969impl Redecryptor {
970    /// Create a new [`Redecryptor`].
971    ///
972    /// This creates a task that listens to various streams and attempts to
973    /// redecrypt UTDs that can be found inside the [`EventCache`].
974    pub(super) fn new(
975        client: &Client,
976        cache: Weak<EventCacheInner>,
977        receiver: UnboundedReceiver<DecryptionRetryRequest>,
978        linked_chunk_update_sender: &Sender<RoomEventCacheLinkedChunkUpdate>,
979    ) -> Self {
980        let linked_chunk_stream = BroadcastStream::new(linked_chunk_update_sender.subscribe());
981        let backup_state_stream = client.encryption().backups().state_stream();
982
983        let task = client
984            .task_monitor()
985            .spawn_infinite_task("event_cache::redecryptor", async {
986                let request_redecryption_stream = UnboundedReceiverStream::new(receiver);
987
988                Self::listen_for_room_keys_task(
989                    cache,
990                    request_redecryption_stream,
991                    linked_chunk_stream,
992                    backup_state_stream,
993                )
994                .await;
995            })
996            .abort_on_drop();
997
998        Self { _task: task }
999    }
1000
1001    /// (Re)-subscribe to the room key stream from the [`OlmMachine`].
1002    ///
1003    /// This needs to happen any time this stream returns a `None` meaning that
1004    /// the sending part of the stream has been dropped.
1005    async fn subscribe_to_room_key_stream(
1006        cache: &Weak<EventCacheInner>,
1007    ) -> Option<(
1008        impl Stream<Item = Result<Vec<RoomKeyInfo>, BroadcastStreamRecvError>>,
1009        impl Stream<Item = Vec<RoomKeyWithheldInfo>>,
1010    )> {
1011        let event_cache = cache.upgrade()?;
1012        let client = event_cache.client().ok()?;
1013        let machine = client.olm_machine().await;
1014
1015        machine.as_ref().map(|m| {
1016            (m.store().room_keys_received_stream(), m.store().room_keys_withheld_received_stream())
1017        })
1018    }
1019
1020    async fn redecryption_loop(
1021        cache: &Weak<EventCacheInner>,
1022        decryption_request_stream: &mut Pin<&mut impl Stream<Item = DecryptionRetryRequest>>,
1023        events_stream: &mut Pin<
1024            &mut impl Stream<Item = Result<RoomEventCacheLinkedChunkUpdate, BroadcastStreamRecvError>>,
1025        >,
1026        backup_state_stream: &mut Pin<
1027            &mut impl Stream<Item = Result<BackupState, BroadcastStreamRecvError>>,
1028        >,
1029    ) -> bool {
1030        let Some((room_key_stream, withheld_stream)) =
1031            Self::subscribe_to_room_key_stream(cache).await
1032        else {
1033            return false;
1034        };
1035
1036        pin_mut!(room_key_stream);
1037        pin_mut!(withheld_stream);
1038
1039        loop {
1040            tokio::select! {
1041                // An explicit request, presumably from the timeline, has been received to decrypt
1042                // events that were encrypted with a certain room key.
1043                Some(request) = decryption_request_stream.next() => {
1044                        let Some(cache) = upgrade_event_cache(cache) else {
1045                            break false;
1046                        };
1047
1048                        trace!(?request, "Received a redecryption request");
1049
1050                        for session_id in request.utd_session_ids {
1051                            let _ = cache
1052                                .retry_decryption(&request.room_id, &session_id)
1053                                .await
1054                                .inspect_err(|e| warn!("Error redecrypting after an explicit request was received {e:?}"));
1055                        }
1056
1057                        for session_id in request.refresh_info_session_ids {
1058                            let _ = cache.update_encryption_info(&request.room_id, &session_id).await.inspect_err(|e|
1059                                warn!(
1060                                    room_id = %request.room_id,
1061                                    session_id = session_id,
1062                                    "Unable to update the encryption info {e:?}",
1063                            ));
1064                        }
1065                }
1066                // The room key stream from the OlmMachine. Needs to be recreated every time we
1067                // receive a `None` from the stream.
1068                room_keys = room_key_stream.next() => {
1069                    match room_keys {
1070                        Some(Ok(room_keys)) => {
1071                            // Alright, some room keys were received and persisted in our store,
1072                            // let's attempt to redecrypt events that were encrypted using these
1073                            // room keys.
1074                            let Some(cache) = upgrade_event_cache(cache) else {
1075                                break false;
1076                            };
1077
1078                            trace!(?room_keys, "Received new room keys");
1079
1080                            for key in &room_keys {
1081                                let _ = cache
1082                                    .retry_decryption(&key.room_id, &key.session_id)
1083                                    .await
1084                                    .inspect_err(|e| warn!("Error redecrypting {e:?}"));
1085                            }
1086
1087                            for key in room_keys {
1088                                let _ = cache.update_encryption_info(&key.room_id, &key.session_id).await.inspect_err(|e|
1089                                    warn!(
1090                                        room_id = %key.room_id,
1091                                        session_id = key.session_id,
1092                                        "Unable to update the encryption info {e:?}",
1093                                ));
1094                            }
1095                        },
1096                        Some(Err(_)) => {
1097                            // We missed some room keys, we need to report this in case a listener
1098                            // has and idea which UTDs we should attempt to redecrypt.
1099                            //
1100                            // This would most likely be the timeline from the UI crate. The
1101                            // timeline might attempt to redecrypt all UTDs it is showing to the
1102                            // user.
1103                            warn!("The room key stream lagged, reporting the lag to our listeners");
1104
1105                            if send_report_and_retry_memory_events(cache, RedecryptorReport::Lagging).await.is_err() {
1106                                break false;
1107                            }
1108                        },
1109                        // The stream got closed, this could mean that our OlmMachine got
1110                        // regenerated, let's return true and try to recreate the stream.
1111                        None => {
1112                            break true;
1113                        }
1114                    }
1115                }
1116                withheld_info = withheld_stream.next() => {
1117                    match withheld_info {
1118                        Some(infos) => {
1119                            let Some(cache) = upgrade_event_cache(cache) else {
1120                                break false;
1121                            };
1122
1123                            trace!(?infos, "Received new withheld infos");
1124
1125                            for RoomKeyWithheldInfo { room_id, session_id, .. } in &infos {
1126                                let _ = cache.update_encryption_info(room_id, session_id).await.inspect_err(|e|
1127                                    warn!(
1128                                        room_id = %room_id,
1129                                        session_id = session_id,
1130                                        "Unable to update the encryption info {e:?}",
1131                                ));
1132                            }
1133                        }
1134                        // The stream got closed, same as for the room key stream, we'll try to
1135                        // recreate the streams.
1136                        None => break true,
1137                    }
1138                }
1139                // Events that the event cache handled. If the event cache received any UTDs, let's
1140                // attempt to redecrypt them in case the room key was received before the event
1141                // cache was able to return them using `get_utds()`.
1142                Some(event_updates) = events_stream.next() => {
1143                    match event_updates {
1144                        Ok(updates) => {
1145                            let Some(cache) = upgrade_event_cache(cache) else {
1146                                break false;
1147                            };
1148
1149                            let linked_chunk_id = updates.linked_chunk_id.to_owned();
1150
1151                            let _ = cache.retry_decryption_for_event_cache_updates(updates).await.inspect_err(|e|
1152                                warn!(
1153                                    %linked_chunk_id,
1154                                    "Unable to handle UTDs from event cache updates {e:?}",
1155                                )
1156                            );
1157                        }
1158                        Err(_) => {
1159                            if send_report_and_retry_memory_events(cache, RedecryptorReport::Lagging).await.is_err() {
1160                                break false;
1161                            }
1162                        }
1163                    }
1164                }
1165                Some(backup_state_update) = backup_state_stream.next() => {
1166                    match backup_state_update {
1167                        Ok(state) => {
1168                            match state {
1169                                BackupState::Unknown |
1170                                BackupState::Creating |
1171                                BackupState::Enabling |
1172                                BackupState::Resuming |
1173                                BackupState::Downloading |
1174                                BackupState::Disabling =>{
1175                                    // Those states aren't particularly interesting to components
1176                                    // listening to R2D2 reports.
1177                                }
1178                                BackupState::Enabled => {
1179                                    // Alright, the backup got enabled, we might or might not have
1180                                    // downloaded the room keys from the backup. In case they get
1181                                    // downloaded on-demand, let's try to decrypt all the events we
1182                                    // have cached in-memory.
1183                                    if send_report_and_retry_memory_events(cache, RedecryptorReport::BackupAvailable).await.is_err() {
1184                                        break false;
1185                                    }
1186                                }
1187                            }
1188                        }
1189                        Err(_) => {
1190                            if send_report_and_retry_memory_events(cache, RedecryptorReport::Lagging).await.is_err() {
1191                                break false;
1192                            }
1193                        }
1194                    }
1195                }
1196                else => break false,
1197            }
1198        }
1199    }
1200
1201    async fn listen_for_room_keys_task(
1202        cache: Weak<EventCacheInner>,
1203        decryption_request_stream: UnboundedReceiverStream<DecryptionRetryRequest>,
1204        events_stream: BroadcastStream<RoomEventCacheLinkedChunkUpdate>,
1205        backup_state_stream: impl Stream<Item = Result<BackupState, BroadcastStreamRecvError>>,
1206    ) {
1207        // We pin the decryption request stream here since that one doesn't need to be
1208        // recreated and we don't want to miss messages coming from the stream
1209        // while recreating it unnecessarily.
1210        pin_mut!(decryption_request_stream);
1211        pin_mut!(events_stream);
1212        pin_mut!(backup_state_stream);
1213
1214        while Self::redecryption_loop(
1215            &cache,
1216            &mut decryption_request_stream,
1217            &mut events_stream,
1218            &mut backup_state_stream,
1219        )
1220        .await
1221        {
1222            info!("Regenerating the re-decryption streams");
1223
1224            // Report that the stream got recreated so listeners know about it, at the same
1225            // time retry to decrypt anything we have cached in memory.
1226            if send_report_and_retry_memory_events(&cache, RedecryptorReport::Lagging)
1227                .await
1228                .is_err()
1229            {
1230                break;
1231            }
1232        }
1233
1234        info!("Shutting down the event cache redecryptor");
1235    }
1236}
1237
1238#[cfg(not(target_family = "wasm"))]
1239#[cfg(test)]
1240mod tests {
1241    use std::{
1242        collections::BTreeSet,
1243        sync::{
1244            Arc,
1245            atomic::{AtomicBool, Ordering},
1246        },
1247        time::Duration,
1248    };
1249
1250    use assert_matches2::assert_matches;
1251    use async_trait::async_trait;
1252    use eyeball_im::VectorDiff;
1253    use matrix_sdk_base::{
1254        cross_process_lock::CrossProcessLockGeneration,
1255        crypto::types::events::{ToDeviceEvent, room::encrypted::ToDeviceEncryptedEventContent},
1256        deserialized_responses::{TimelineEventKind, VerificationState},
1257        event_cache::{
1258            Event, Gap,
1259            store::{EventCacheStore, EventCacheStoreError, MemoryStore},
1260        },
1261        linked_chunk::{
1262            ChunkIdentifier, ChunkIdentifierGenerator, ChunkMetadata, LinkedChunkId, Position,
1263            RawChunk, Update,
1264        },
1265        locks::Mutex,
1266        sleep::sleep,
1267        store::StoreConfig,
1268    };
1269    use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig;
1270    use matrix_sdk_test::{JoinedRoomBuilder, async_test, event_factory::EventFactory};
1271    use ruma::{
1272        EventId, OwnedEventId, RoomId, RoomVersionId, device_id, event_id,
1273        events::{AnySyncTimelineEvent, relation::RelationType},
1274        room_id,
1275        serde::Raw,
1276        user_id,
1277    };
1278    use serde_json::json;
1279    use tokio::sync::oneshot::{self, Sender};
1280    use tracing::{Instrument, info};
1281
1282    use crate::{
1283        Client, assert_let_timeout,
1284        encryption::EncryptionSettings,
1285        event_cache::{
1286            DecryptionRetryRequest, RoomEventCacheGenericUpdate, RoomEventCacheUpdate,
1287            TimelineVectorDiffs,
1288        },
1289        test_utils::mocks::MatrixMockServer,
1290    };
1291
1292    /// A wrapper for the memory store for the event cache.
1293    ///
1294    /// Delays the persisting of events, or linked chunk updates, to allow the
1295    /// testing of race conditions between the event cache and R2D2.
1296    #[derive(Debug, Clone)]
1297    struct DelayingStore {
1298        memory_store: MemoryStore,
1299        delaying: Arc<AtomicBool>,
1300        foo: Arc<Mutex<Option<Sender<()>>>>,
1301    }
1302
1303    impl DelayingStore {
1304        fn new() -> Self {
1305            Self {
1306                memory_store: MemoryStore::new(),
1307                delaying: AtomicBool::new(true).into(),
1308                foo: Arc::new(Mutex::new(None)),
1309            }
1310        }
1311
1312        async fn stop_delaying(&self) {
1313            let (sender, receiver) = oneshot::channel();
1314
1315            {
1316                *self.foo.lock() = Some(sender);
1317            }
1318
1319            self.delaying.store(false, Ordering::SeqCst);
1320
1321            receiver.await.expect("We should be able to receive a response")
1322        }
1323    }
1324
1325    #[cfg_attr(target_family = "wasm", async_trait(?Send))]
1326    #[cfg_attr(not(target_family = "wasm"), async_trait)]
1327    impl EventCacheStore for DelayingStore {
1328        type Error = EventCacheStoreError;
1329
1330        async fn close(&self) -> Result<(), EventCacheStoreError> {
1331            self.memory_store.close().await
1332        }
1333
1334        async fn reopen(&self) -> Result<(), EventCacheStoreError> {
1335            self.memory_store.reopen().await
1336        }
1337
1338        async fn try_take_leased_lock(
1339            &self,
1340            lease_duration_ms: u32,
1341            key: &str,
1342            holder: &str,
1343        ) -> Result<Option<CrossProcessLockGeneration>, Self::Error> {
1344            self.memory_store.try_take_leased_lock(lease_duration_ms, key, holder).await
1345        }
1346
1347        async fn handle_linked_chunk_updates(
1348            &self,
1349            linked_chunk_id: LinkedChunkId<'_>,
1350            updates: Vec<Update<Event, Gap>>,
1351        ) -> Result<(), Self::Error> {
1352            // This is the key behaviour of this store - we wait to set this value until
1353            // someone calls `stop_delaying`.
1354            //
1355            // We use `sleep` here for simplicity. The cool way would be to use a custom
1356            // waker or something like that.
1357            while self.delaying.load(Ordering::SeqCst) {
1358                sleep(Duration::from_millis(10)).await;
1359            }
1360
1361            let sender = self.foo.lock().take();
1362            let ret = self.memory_store.handle_linked_chunk_updates(linked_chunk_id, updates).await;
1363
1364            if let Some(sender) = sender {
1365                sender.send(()).expect("We should be able to notify the other side that we're done with the storage operation");
1366            }
1367
1368            ret
1369        }
1370
1371        async fn load_all_chunks(
1372            &self,
1373            linked_chunk_id: LinkedChunkId<'_>,
1374        ) -> Result<Vec<RawChunk<Event, Gap>>, Self::Error> {
1375            self.memory_store.load_all_chunks(linked_chunk_id).await
1376        }
1377
1378        async fn load_all_chunks_metadata(
1379            &self,
1380            linked_chunk_id: LinkedChunkId<'_>,
1381        ) -> Result<Vec<ChunkMetadata>, Self::Error> {
1382            self.memory_store.load_all_chunks_metadata(linked_chunk_id).await
1383        }
1384
1385        async fn load_last_chunk(
1386            &self,
1387            linked_chunk_id: LinkedChunkId<'_>,
1388        ) -> Result<(Option<RawChunk<Event, Gap>>, ChunkIdentifierGenerator), Self::Error> {
1389            self.memory_store.load_last_chunk(linked_chunk_id).await
1390        }
1391
1392        async fn load_previous_chunk(
1393            &self,
1394            linked_chunk_id: LinkedChunkId<'_>,
1395            before_chunk_identifier: ChunkIdentifier,
1396        ) -> Result<Option<RawChunk<Event, Gap>>, Self::Error> {
1397            self.memory_store.load_previous_chunk(linked_chunk_id, before_chunk_identifier).await
1398        }
1399
1400        async fn remember_thread(
1401            &self,
1402            room_id: &RoomId,
1403            thread_id: &EventId,
1404        ) -> Result<(), Self::Error> {
1405            self.memory_store.remember_thread(room_id, thread_id).await
1406        }
1407
1408        async fn clear_all_events(&self, room_id: Option<&RoomId>) -> Result<(), Self::Error> {
1409            self.memory_store.clear_all_events(room_id).await
1410        }
1411
1412        async fn filter_duplicated_events(
1413            &self,
1414            linked_chunk_id: LinkedChunkId<'_>,
1415            events: Vec<OwnedEventId>,
1416        ) -> Result<Vec<(OwnedEventId, Position)>, Self::Error> {
1417            self.memory_store.filter_duplicated_events(linked_chunk_id, events).await
1418        }
1419
1420        async fn find_event(
1421            &self,
1422            room_id: &RoomId,
1423            event_id: &EventId,
1424        ) -> Result<Option<Event>, Self::Error> {
1425            self.memory_store.find_event(room_id, event_id).await
1426        }
1427
1428        async fn find_event_relations(
1429            &self,
1430            room_id: &RoomId,
1431            event_id: &EventId,
1432            filters: Option<&[RelationType]>,
1433        ) -> Result<Vec<(Event, Option<Position>)>, Self::Error> {
1434            self.memory_store.find_event_relations(room_id, event_id, filters).await
1435        }
1436
1437        async fn get_room_events(
1438            &self,
1439            room_id: &RoomId,
1440            event_type: Option<&str>,
1441            session_id: Option<&str>,
1442        ) -> Result<Vec<Event>, Self::Error> {
1443            self.memory_store.get_room_events(room_id, event_type, session_id).await
1444        }
1445
1446        async fn save_event(&self, room_id: &RoomId, event: Event) -> Result<(), Self::Error> {
1447            self.memory_store.save_event(room_id, event).await
1448        }
1449
1450        async fn optimize(&self) -> Result<(), Self::Error> {
1451            self.memory_store.optimize().await
1452        }
1453
1454        async fn get_size(&self) -> Result<Option<usize>, Self::Error> {
1455            self.memory_store.get_size().await
1456        }
1457    }
1458
1459    async fn set_up_clients(
1460        room_id: &RoomId,
1461        alice_enables_cross_signing: bool,
1462        use_delayed_store: bool,
1463    ) -> (Client, Client, MatrixMockServer, Option<DelayingStore>) {
1464        let alice_span = tracing::info_span!("alice");
1465        let bob_span = tracing::info_span!("bob");
1466
1467        let alice_user_id = user_id!("@alice:localhost");
1468        let alice_device_id = device_id!("ALICEDEVICE");
1469        let bob_user_id = user_id!("@bob:localhost");
1470        let bob_device_id = device_id!("BOBDEVICE");
1471
1472        let matrix_mock_server = MatrixMockServer::new().await;
1473        matrix_mock_server.mock_crypto_endpoints_preset().await;
1474
1475        let encryption_settings = EncryptionSettings {
1476            auto_enable_cross_signing: alice_enables_cross_signing,
1477            ..Default::default()
1478        };
1479
1480        // Create some clients for Alice and Bob.
1481
1482        let alice = matrix_mock_server
1483            .client_builder_for_crypto_end_to_end(alice_user_id, alice_device_id)
1484            .on_builder(|builder| {
1485                builder
1486                    .with_enable_share_history_on_invite(true)
1487                    .with_encryption_settings(encryption_settings)
1488            })
1489            .build()
1490            .instrument(alice_span.clone())
1491            .await;
1492
1493        let encryption_settings =
1494            EncryptionSettings { auto_enable_cross_signing: true, ..Default::default() };
1495
1496        let (store_config, store) = if use_delayed_store {
1497            let store = DelayingStore::new();
1498
1499            (
1500                StoreConfig::new(CrossProcessLockConfig::multi_process(
1501                    "delayed_store_event_cache_test",
1502                ))
1503                .event_cache_store(store.clone()),
1504                Some(store),
1505            )
1506        } else {
1507            (
1508                StoreConfig::new(CrossProcessLockConfig::multi_process(
1509                    "normal_store_event_cache_test",
1510                )),
1511                None,
1512            )
1513        };
1514
1515        let bob = matrix_mock_server
1516            .client_builder_for_crypto_end_to_end(bob_user_id, bob_device_id)
1517            .on_builder(|builder| {
1518                builder
1519                    .with_enable_share_history_on_invite(true)
1520                    .with_encryption_settings(encryption_settings)
1521                    .store_config(store_config)
1522            })
1523            .build()
1524            .instrument(bob_span.clone())
1525            .await;
1526
1527        bob.event_cache().subscribe().expect("Bob should be able to enable the event cache");
1528
1529        // Ensure that Alice and Bob are aware of their devices and identities.
1530        matrix_mock_server.exchange_e2ee_identities(&alice, &bob).await;
1531
1532        let event_factory = EventFactory::new().room(room_id).sender(alice_user_id);
1533
1534        // Let us now create a room for them.
1535        let room_builder = JoinedRoomBuilder::new(room_id)
1536            .add_state_event(event_factory.create(alice_user_id, RoomVersionId::V1))
1537            .add_state_event(event_factory.room_encryption());
1538
1539        matrix_mock_server
1540            .mock_sync()
1541            .ok_and_run(&alice, |builder| {
1542                builder.add_joined_room(room_builder.clone());
1543            })
1544            .instrument(alice_span)
1545            .await;
1546
1547        matrix_mock_server
1548            .mock_sync()
1549            .ok_and_run(&bob, |builder| {
1550                builder.add_joined_room(room_builder);
1551            })
1552            .instrument(bob_span)
1553            .await;
1554
1555        (alice, bob, matrix_mock_server, store)
1556    }
1557
1558    async fn prepare_room(
1559        matrix_mock_server: &MatrixMockServer,
1560        event_factory: &EventFactory,
1561        alice: &Client,
1562        bob: &Client,
1563        room_id: &RoomId,
1564    ) -> (Raw<AnySyncTimelineEvent>, Raw<ToDeviceEvent<ToDeviceEncryptedEventContent>>) {
1565        let alice_user_id = alice.user_id().unwrap();
1566        let bob_user_id = bob.user_id().unwrap();
1567
1568        let alice_member_event = event_factory.member(alice_user_id).into_raw();
1569        let bob_member_event = event_factory.member(bob_user_id).into_raw();
1570
1571        let room = alice
1572            .get_room(room_id)
1573            .expect("Alice should have access to the room now that we synced");
1574
1575        // Alice will send a single event to the room, but this will trigger a to-device
1576        // message containing the room key to be sent as well. We capture both the event
1577        // and the to-device message.
1578
1579        let event_type = "m.room.message";
1580        let content = json!({"body": "It's a secret to everybody", "msgtype": "m.text"});
1581
1582        let event_id = event_id!("$some_id");
1583        let (event_receiver, mock) =
1584            matrix_mock_server.mock_room_send().ok_with_capture(event_id, alice_user_id);
1585        let (_guard, room_key) = matrix_mock_server.mock_capture_put_to_device(alice_user_id).await;
1586
1587        {
1588            let _guard = mock.mock_once().mount_as_scoped().await;
1589
1590            matrix_mock_server
1591                .mock_get_members()
1592                .ok(vec![alice_member_event.clone(), bob_member_event.clone()])
1593                .mock_once()
1594                .mount()
1595                .await;
1596
1597            room.send_raw(event_type, content)
1598                .await
1599                .expect("We should be able to send an initial message");
1600        };
1601
1602        // Let us retrieve the captured event and to-device message.
1603        let event = event_receiver.await.expect("Alice should have sent the event by now");
1604        let room_key = room_key.await;
1605
1606        (event, room_key)
1607    }
1608
1609    #[async_test]
1610    async fn test_redecryptor() {
1611        let room_id = room_id!("!test:localhost");
1612
1613        let event_factory = EventFactory::new().room(room_id);
1614        let (alice, bob, matrix_mock_server, _) = set_up_clients(room_id, true, false).await;
1615
1616        let (event, room_key) =
1617            prepare_room(&matrix_mock_server, &event_factory, &alice, &bob, room_id).await;
1618
1619        // Let's now see what Bob's event cache does.
1620
1621        let event_cache = bob.event_cache();
1622        let (room_cache, _) = event_cache
1623            .room(room_id)
1624            .await
1625            .expect("We should be able to get to the event cache for a specific room");
1626
1627        let (_, mut subscriber) = room_cache.subscribe().await.unwrap();
1628        let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
1629
1630        // We regenerate the Olm machine to check if the room key stream is recreated to
1631        // correctly.
1632        bob.inner
1633            .base_client
1634            .regenerate_olm(None)
1635            .await
1636            .expect("We should be able to regenerate the Olm machine");
1637
1638        // Let us forward the event to Bob.
1639        matrix_mock_server
1640            .mock_sync()
1641            .ok_and_run(&bob, |builder| {
1642                builder.add_joined_room(JoinedRoomBuilder::new(room_id).add_timeline_event(event));
1643            })
1644            .await;
1645
1646        // Alright, Bob has received an update from the cache.
1647
1648        assert_let_timeout!(
1649            Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1650                subscriber.recv()
1651        );
1652
1653        // There should be a single new event, and it should be a UTD as we did not
1654        // receive the room key yet.
1655        assert_eq!(diffs.len(), 1);
1656        assert_matches!(&diffs[0], VectorDiff::Append { values });
1657        assert_eq!(values.len(), 1);
1658        assert_matches!(&values[0].kind, TimelineEventKind::UnableToDecrypt { .. });
1659
1660        assert_let_timeout!(
1661            Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) = generic_stream.recv()
1662        );
1663        assert_eq!(expected_room_id, room_id);
1664        assert!(generic_stream.is_empty());
1665
1666        // Now we send the room key to Bob.
1667        matrix_mock_server
1668            .mock_sync()
1669            .ok_and_run(&bob, |builder| {
1670                builder.add_to_device_event(
1671                    room_key
1672                        .deserialize_as()
1673                        .expect("We should be able to deserialize the room key"),
1674                );
1675            })
1676            .await;
1677
1678        // Bob should receive a new update from the cache.
1679        assert_let_timeout!(
1680            Duration::from_secs(1),
1681            Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1682                subscriber.recv()
1683        );
1684
1685        // It should replace the UTD with a decrypted event.
1686        assert_eq!(diffs.len(), 1);
1687        assert_matches!(&diffs[0], VectorDiff::Set { index, value });
1688        assert_eq!(*index, 0);
1689        assert_matches!(&value.kind, TimelineEventKind::Decrypted { .. });
1690
1691        assert_let_timeout!(
1692            Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) = generic_stream.recv()
1693        );
1694        assert_eq!(expected_room_id, room_id);
1695        assert!(generic_stream.is_empty());
1696    }
1697
1698    #[async_test]
1699    async fn test_redecryptor_updating_encryption_info() {
1700        let bob_span = tracing::info_span!("bob");
1701
1702        let room_id = room_id!("!test:localhost");
1703
1704        let event_factory = EventFactory::new().room(room_id);
1705        let (alice, bob, matrix_mock_server, _) = set_up_clients(room_id, false, false).await;
1706
1707        let (event, room_key) =
1708            prepare_room(&matrix_mock_server, &event_factory, &alice, &bob, room_id).await;
1709
1710        // Let's now see what Bob's event cache does.
1711
1712        let event_cache = bob.event_cache();
1713        let (room_cache, _) = event_cache
1714            .room(room_id)
1715            .instrument(bob_span.clone())
1716            .await
1717            .expect("We should be able to get to the event cache for a specific room");
1718
1719        let (_, mut subscriber) = room_cache.subscribe().await.unwrap();
1720        let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
1721
1722        // Let us forward the event to Bob.
1723        matrix_mock_server
1724            .mock_sync()
1725            .ok_and_run(&bob, |builder| {
1726                builder.add_joined_room(JoinedRoomBuilder::new(room_id).add_timeline_event(event));
1727            })
1728            .instrument(bob_span.clone())
1729            .await;
1730
1731        // Alright, Bob has received an update from the cache.
1732
1733        assert_let_timeout!(
1734            Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1735                subscriber.recv()
1736        );
1737
1738        // There should be a single new event, and it should be a UTD as we did not
1739        // receive the room key yet.
1740        assert_eq!(diffs.len(), 1);
1741        assert_matches!(&diffs[0], VectorDiff::Append { values });
1742        assert_eq!(values.len(), 1);
1743        assert_matches!(&values[0].kind, TimelineEventKind::UnableToDecrypt { .. });
1744
1745        assert_let_timeout!(
1746            Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) = generic_stream.recv()
1747        );
1748        assert_eq!(expected_room_id, room_id);
1749        assert!(generic_stream.is_empty());
1750
1751        // Now we send the room key to Bob.
1752        matrix_mock_server
1753            .mock_sync()
1754            .ok_and_run(&bob, |builder| {
1755                builder.add_to_device_event(
1756                    room_key
1757                        .deserialize_as()
1758                        .expect("We should be able to deserialize the room key"),
1759                );
1760            })
1761            .instrument(bob_span.clone())
1762            .await;
1763
1764        // Bob should receive a new update from the cache.
1765        assert_let_timeout!(
1766            Duration::from_secs(1),
1767            Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1768                subscriber.recv()
1769        );
1770
1771        // It should replace the UTD with a decrypted event.
1772        assert_eq!(diffs.len(), 1);
1773        assert_matches!(&diffs[0], VectorDiff::Set { index: 0, value });
1774        assert_matches!(&value.kind, TimelineEventKind::Decrypted { .. });
1775
1776        let encryption_info = value.encryption_info().unwrap();
1777        assert_matches!(&encryption_info.verification_state, VerificationState::Unverified(_));
1778
1779        assert_let_timeout!(
1780            Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) = generic_stream.recv()
1781        );
1782        assert_eq!(expected_room_id, room_id);
1783        assert!(generic_stream.is_empty());
1784
1785        let session_id = encryption_info.session_id().unwrap().to_owned();
1786        let alice_user_id = alice.user_id().unwrap();
1787
1788        // Alice now creates the identity.
1789        alice
1790            .encryption()
1791            .bootstrap_cross_signing(None)
1792            .await
1793            .expect("Alice should be able to create the cross-signing keys");
1794
1795        bob.update_tracked_users_for_testing([alice_user_id]).instrument(bob_span.clone()).await;
1796        matrix_mock_server
1797            .mock_sync()
1798            .ok_and_run(&bob, |builder| {
1799                builder.add_change_device(alice_user_id);
1800            })
1801            .instrument(bob_span.clone())
1802            .await;
1803
1804        bob.event_cache().request_decryption(DecryptionRetryRequest {
1805            room_id: room_id.into(),
1806            utd_session_ids: BTreeSet::new(),
1807            refresh_info_session_ids: BTreeSet::from([session_id]),
1808        });
1809
1810        // Bob should again receive a new update from the cache, this time updating the
1811        // encryption info.
1812        assert_let_timeout!(
1813            Duration::from_secs(1),
1814            Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1815                subscriber.recv()
1816        );
1817
1818        assert_eq!(diffs.len(), 1);
1819        assert_matches!(&diffs[0], VectorDiff::Set { index: 0, value });
1820        assert_matches!(&value.kind, TimelineEventKind::Decrypted { .. });
1821        let encryption_info = value.encryption_info().unwrap();
1822
1823        assert_matches!(
1824            &encryption_info.verification_state,
1825            VerificationState::Unverified(_),
1826            "The event should now know about the identity but still be unverified"
1827        );
1828
1829        assert_let_timeout!(
1830            Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) = generic_stream.recv()
1831        );
1832        assert_eq!(expected_room_id, room_id);
1833        assert!(generic_stream.is_empty());
1834    }
1835
1836    #[async_test]
1837    async fn test_event_is_redecrypted_even_if_key_arrives_while_event_processing() {
1838        let room_id = room_id!("!test:localhost");
1839
1840        let event_factory = EventFactory::new().room(room_id);
1841        let (alice, bob, matrix_mock_server, delayed_store) =
1842            set_up_clients(room_id, true, true).await;
1843
1844        let delayed_store = delayed_store.unwrap();
1845
1846        let (event, room_key) =
1847            prepare_room(&matrix_mock_server, &event_factory, &alice, &bob, room_id).await;
1848
1849        let event_cache = bob.event_cache();
1850
1851        // Let's now see what Bob's event cache does.
1852        let (room_cache, _) = event_cache
1853            .room(room_id)
1854            .await
1855            .expect("We should be able to get to the event cache for a specific room");
1856
1857        let (_, mut subscriber) = room_cache.subscribe().await.unwrap();
1858        let mut generic_stream = event_cache.subscribe_to_room_generic_updates();
1859
1860        // Let us forward the event to Bob.
1861        matrix_mock_server
1862            .mock_sync()
1863            .ok_and_run(&bob, |builder| {
1864                builder.add_joined_room(JoinedRoomBuilder::new(room_id).add_timeline_event(event));
1865            })
1866            .await;
1867
1868        // Now we send the room key to Bob.
1869        matrix_mock_server
1870            .mock_sync()
1871            .ok_and_run(&bob, |builder| {
1872                builder.add_to_device_event(
1873                    room_key
1874                        .deserialize_as()
1875                        .expect("We should be able to deserialize the room key"),
1876                );
1877            })
1878            .await;
1879
1880        info!("Stopping the delay");
1881        delayed_store.stop_delaying().await;
1882
1883        // The first decryption attempt has failed because the first sync (the
1884        // one with the event) did not contain the room key. The decryptor has
1885        // later received the room key.
1886
1887        // Alright, Bob has received an update from the cache.
1888        assert_let_timeout!(
1889            Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1890                subscriber.recv()
1891        );
1892
1893        // There should be a single new event, and it should be a UTD as we did not
1894        // receive the room key yet.
1895        assert_eq!(diffs.len(), 1);
1896        assert_matches!(&diffs[0], VectorDiff::Append { values });
1897        assert_eq!(values.len(), 1);
1898        assert_matches!(&values[0].kind, TimelineEventKind::UnableToDecrypt { .. });
1899
1900        // And the companion generic update.
1901        assert_let_timeout!(
1902            Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) = generic_stream.recv()
1903        );
1904        assert_eq!(expected_room_id, room_id);
1905
1906        // Bob should receive a new update from the cache.
1907        assert_let_timeout!(
1908            Duration::from_secs(1),
1909            Ok(RoomEventCacheUpdate::UpdateTimelineEvents(TimelineVectorDiffs { diffs, .. })) =
1910                subscriber.recv()
1911        );
1912
1913        // It should replace the UTD with a decrypted event.
1914        assert_eq!(diffs.len(), 1);
1915        assert_matches!(&diffs[0], VectorDiff::Set { index, value });
1916        assert_eq!(*index, 0);
1917        assert_matches!(&value.kind, TimelineEventKind::Decrypted { .. });
1918
1919        // And the companion generic update.
1920        assert_let_timeout!(
1921            Ok(RoomEventCacheGenericUpdate { room_id: expected_room_id }) = generic_stream.recv()
1922        );
1923        assert_eq!(expected_room_id, room_id);
1924        assert!(generic_stream.is_empty());
1925    }
1926}