Skip to main content

core_crypto/mls/conversation/mutable/
mod.rs

1mod commit;
2pub(crate) mod decrypt;
3mod encrypt;
4mod group_mutation;
5mod history_sharing;
6mod merge;
7mod own_commit;
8mod proposal;
9mod wipe;
10
11use std::sync::Arc;
12
13use core_crypto_keystore::Database;
14use openmls::prelude::group_info::GroupInfo;
15
16use super::{Error, Result};
17use crate::{
18    CryptoProvider, GroupInfoBundle, LeafError, MlsTransport, RecursiveError, Session,
19    mls::{conversation::Conversation, credential::Credential},
20    transaction_context::TransactionContext,
21};
22
23/// A mutable view of an MLS conversation.
24///
25/// The conversation is ultimately owned by the conversation
26/// cache, but we take an `Arc` here so that we don't have to tie
27/// the lifetime of the guard to the cache.
28///
29/// More generally, the conversation guard gives us convenient mutable accesses to a single
30/// conversation. This in turn means that we don't have to duplicate the entire
31/// conversation API on `TransactionContext`.
32#[derive(Debug, derive_more::Constructor, derive_more::Deref)]
33pub struct ConversationMut {
34    #[deref(forward)]
35    inner: Arc<Conversation>,
36    tx_context: TransactionContext,
37}
38
39impl ConversationMut {
40    async fn transport(&self) -> Result<Arc<dyn MlsTransport>> {
41        self.tx_context
42            .mls_transport()
43            .await
44            .map_err(RecursiveError::transaction("getting transport for conversation guard"))
45            .map_err(Into::into)
46    }
47
48    async fn database(&self) -> Result<Database> {
49        self.tx_context
50            .database()
51            .await
52            .map_err(RecursiveError::transaction("getting database from context"))
53            .map_err(Into::into)
54    }
55
56    async fn crypto_provider(&self) -> Result<CryptoProvider> {
57        self.tx_context
58            .crypto_provider()
59            .await
60            .map_err(RecursiveError::transaction(
61                "acquiring crypto provider for conversation guard from tx context",
62            ))
63            .map_err(Into::into)
64    }
65
66    pub(crate) async fn credential(&self) -> Result<Arc<Credential>> {
67        self.find_current_credential()
68            .await
69            .map_err(|_| Error::IdentityInitializationError)
70    }
71
72    /// Get access to the MLS session for this guard
73    pub(super) async fn session(&self) -> Result<Session> {
74        self.tx_context
75            .session()
76            .await
77            .map_err(RecursiveError::transaction("getting session from transaction context"))
78            .map_err(Into::into)
79    }
80
81    fn group_info(group_info: Option<GroupInfo>) -> Result<GroupInfoBundle> {
82        let group_info = group_info.ok_or(LeafError::MissingGroupInfo)?;
83        GroupInfoBundle::try_new_full_plaintext(group_info)
84    }
85}
86
87#[cfg(test)]
88mod test_utils {
89    use super::ConversationMut;
90    use crate::mls::conversation::Conversation;
91
92    impl ConversationMut {
93        /// Replaces the MLS group in memory with the one from keystore.
94        pub async fn drop_and_restore(&mut self) {
95            let session = self.tx_context.session().await.unwrap();
96            let id = self.id();
97
98            let conversation = Conversation::load(session, id).await.unwrap().unwrap();
99            self.tx_context.mls_groups().await.unwrap().insert(conversation);
100        }
101    }
102}