core_crypto/transaction_context/conversation/
mod.rs1pub mod external_commit;
4mod persistence;
5pub mod welcome;
6
7use std::sync::Arc;
8
9use core_crypto_keystore::{
10 entities::{PersistedMlsPendingGroup, StoredBufferedCommit},
11 traits::FetchFromDatabase as _,
12};
13use openmls::group::MlsGroup;
14
15use super::{Error, Result, TransactionContext};
16use crate::{
17 ConversationConfiguration, CredentialRef, KeystoreError, LeafError, OpenMlsError, RecursiveError,
18 mls::conversation::{ConversationIdRef, ConversationMut, PendingConversation},
19};
20
21impl TransactionContext {
22 pub async fn conversation_exists(&self, id: &ConversationIdRef) -> Result<bool> {
26 let database = self.database().await?.into();
27 self.mls_groups()
28 .await?
29 .exists(id, &database)
30 .await
31 .map_err(RecursiveError::root("checking for conversation existence"))
32 .map_err(Into::into)
33 }
34
35 pub async fn conversation(&self, id: &ConversationIdRef) -> Result<ConversationMut> {
39 let inner = self.inner().await?;
40 let session = self.session().await?;
41 let conversation = self
42 .mls_groups()
43 .await?
44 .get_or_fetch(id, &inner.transaction, session)
45 .await
46 .map_err(RecursiveError::root("fetching conversation from mls groups by id"))?;
47
48 if let Some(conversation) = conversation {
49 return Ok(ConversationMut::new(conversation, self.clone()));
50 }
51 let pending = self.pending_conversation(id).await.map(Error::PendingConversation)?;
54 Err(pending)
55 }
56
57 pub(crate) async fn clear_orphaned_conversation_buffers(&self, id: &ConversationIdRef) -> Result<()> {
76 let database = self.database().await?;
77
78 let conversation_remains = database.mls_group_exists(id).await
79 || database
80 .get_borrowed::<PersistedMlsPendingGroup>(id.as_ref())
81 .await
82 .map_err(KeystoreError::wrap(
83 "looking for a pending group of a removed conversation",
84 ))?
85 .is_some();
86 if conversation_remains {
87 return Ok(());
88 }
89
90 database
91 .remove_pending_messages_by_conversation_id(id)
92 .await
93 .map_err(KeystoreError::wrap(
94 "clearing buffered messages of a removed conversation",
95 ))?;
96
97 self.inner()
98 .await?
99 .transaction()
100 .remove_borrowed::<StoredBufferedCommit>(id.as_ref())
101 .await
102 .map_err(KeystoreError::wrap(
103 "clearing the buffered commit of a removed conversation",
104 ))?;
105
106 Ok(())
107 }
108
109 pub(crate) async fn pending_conversation(&self, id: &ConversationIdRef) -> Result<PendingConversation> {
110 let inner = self.inner().await?;
111 let Some(pending_group) = inner
112 .transaction
113 .get_borrowed::<PersistedMlsPendingGroup>(id.as_ref())
114 .await
115 .map_err(KeystoreError::wrap("finding persisted mls pending group"))?
116 else {
117 return Err(LeafError::ConversationNotFound(id.to_owned()).into());
118 };
119 let pending_group = Arc::unwrap_or_clone(pending_group);
120 Ok(PendingConversation::new(pending_group, self.clone()))
121 }
122
123 #[cfg_attr(test, crate::dispotent)]
134 pub async fn new_conversation(
135 &self,
136 id: &ConversationIdRef,
137 credential_ref: &CredentialRef,
138 configuration: ConversationConfiguration,
139 ) -> Result<()> {
140 let database = self.database().await?;
141 let provider = self.crypto_provider().await?;
142 if self.conversation_exists(id).await? || self.pending_conversation_exists(id).await? {
143 return Err(LeafError::ConversationAlreadyExists(id.to_owned()).into());
144 }
145
146 let credential = credential_ref
147 .load(&*database)
148 .await
149 .map_err(RecursiveError::mls_credential_ref(
150 "loading credential from database to create new conversation",
151 ))?;
152
153 let config = configuration
154 .as_openmls_default_configuration()
155 .map_err(RecursiveError::mls_conversation("converting config to openmls default"))?;
156
157 let group = MlsGroup::new_with_group_id(
158 &provider,
159 &credential.signature_key_pair,
160 &config,
161 openmls::prelude::GroupId::from_slice(id.as_ref()),
162 credential.to_mls_credential_with_key(),
163 )
164 .await
165 .map_err(OpenMlsError::wrap("creating group with id"))?;
166
167 self.persist_conversation_from_mls_group(group, configuration, Default::default())
168 .await?;
169
170 Ok(())
171 }
172}