core_crypto/transaction_context/conversation/
mod.rs1pub mod external_commit;
4mod persistence;
5pub mod welcome;
6
7use core_crypto_keystore::{entities::PersistedMlsPendingGroup, traits::FetchFromDatabase as _};
8use openmls::group::MlsGroup;
9
10use super::{Error, Result, TransactionContext};
11use crate::{
12 ConversationConfiguration, CredentialRef, KeystoreError, LeafError, OpenMlsError, RecursiveError,
13 mls::conversation::{ConversationIdRef, ConversationMut, PendingConversation},
14};
15
16impl TransactionContext {
17 pub async fn conversation_exists(&self, id: &ConversationIdRef) -> Result<bool> {
21 let database = self.database().await?.into();
22 self.mls_groups()
23 .await?
24 .exists(id, &database)
25 .await
26 .map_err(RecursiveError::root("checking for conversation existence"))
27 .map_err(Into::into)
28 }
29
30 pub async fn conversation(&self, id: &ConversationIdRef) -> Result<ConversationMut> {
34 let inner = self.inner().await?;
35 let session = self.session().await?;
36 let conversation = self
37 .mls_groups()
38 .await?
39 .get_or_fetch(id, &inner.transaction, session)
40 .await
41 .map_err(RecursiveError::root("fetching conversation from mls groups by id"))?;
42
43 if let Some(conversation) = conversation {
44 return Ok(ConversationMut::new(conversation, self.clone()));
45 }
46 let pending = self.pending_conversation(id).await.map(Error::PendingConversation)?;
49 Err(pending)
50 }
51
52 pub(crate) async fn pending_conversation(&self, id: &ConversationIdRef) -> Result<PendingConversation> {
53 let inner = self.inner().await?;
54 let Some(pending_group) = inner
55 .transaction
56 .get_borrowed::<PersistedMlsPendingGroup>(id.as_ref())
57 .await
58 .map_err(KeystoreError::wrap("finding persisted mls pending group"))?
59 else {
60 return Err(LeafError::ConversationNotFound(id.to_owned()).into());
61 };
62 Ok(PendingConversation::new(pending_group, self.clone()))
63 }
64
65 #[cfg_attr(test, crate::dispotent)]
76 pub async fn new_conversation(
77 &self,
78 id: &ConversationIdRef,
79 credential_ref: &CredentialRef,
80 configuration: ConversationConfiguration,
81 ) -> Result<()> {
82 let database = self.database().await?;
83 let provider = self.crypto_provider().await?;
84 if self.conversation_exists(id).await? || self.pending_conversation_exists(id).await? {
85 return Err(LeafError::ConversationAlreadyExists(id.to_owned()).into());
86 }
87
88 let credential = credential_ref
89 .load(&*database)
90 .await
91 .map_err(RecursiveError::mls_credential_ref(
92 "loading credential from database to create new conversation",
93 ))?;
94
95 let config = configuration
96 .as_openmls_default_configuration()
97 .map_err(RecursiveError::mls_conversation("converting config to openmls default"))?;
98
99 let group = MlsGroup::new_with_group_id(
100 &provider,
101 &credential.signature_key_pair,
102 &config,
103 openmls::prelude::GroupId::from_slice(id.as_ref()),
104 credential.to_mls_credential_with_key(),
105 )
106 .await
107 .map_err(OpenMlsError::wrap("creating group with id"))?;
108
109 self.persist_conversation_from_mls_group(group, configuration, Default::default())
110 .await?;
111
112 Ok(())
113 }
114}