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