core_crypto/mls/conversation/
merge.rs1use core_crypto_keystore::entities::MlsEncryptionKeyPair;
15use openmls_traits::OpenMlsCryptoProvider;
16
17use mls_crypto_provider::MlsCryptoProvider;
18
19use super::Result;
20use crate::{MlsError, mls::MlsConversation, prelude::Session};
21
22impl MlsConversation {
24 #[cfg_attr(test, crate::durable)]
26 pub(crate) async fn commit_accepted(&mut self, client: &Session, backend: &MlsCryptoProvider) -> Result<()> {
27 let previous_own_leaf_nodes = self.group.own_leaf_nodes.clone();
29
30 self.group
31 .merge_pending_commit(backend)
32 .await
33 .map_err(MlsError::wrap("merging pending commit"))?;
34 self.persist_group_when_changed(&backend.keystore(), false).await?;
35
36 for oln in &previous_own_leaf_nodes {
38 let ek = oln.encryption_key().as_slice();
39 let _ = backend.key_store().remove::<MlsEncryptionKeyPair, _>(ek).await;
40 }
41
42 client
43 .notify_epoch_changed(self.id.clone(), self.group.epoch().as_u64())
44 .await;
45
46 Ok(())
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use wasm_bindgen_test::*;
53
54 use crate::test_utils::*;
55
56 wasm_bindgen_test_configure!(run_in_browser);
57
58 mod commit_accepted {
59 use super::*;
60
61 #[apply(all_cred_cipher)]
62 #[wasm_bindgen_test]
63 async fn should_apply_pending_commit(case: TestContext) {
64 let [alice, bob] = case.sessions().await;
65 Box::pin(async move {
66 let conversation = case.create_conversation([&alice, &bob]).await;
67 let commit_guard = conversation.update_unmerged().await.notify_member(&bob).await;
68 let conversation = commit_guard.conversation();
69
70 assert!(conversation.has_pending_commit().await);
71
72 conversation
73 .guard()
74 .await
75 .conversation_mut()
76 .await
77 .commit_accepted(
78 &alice.transaction.session().await.unwrap(),
79 &alice.session.crypto_provider,
80 )
81 .await
82 .unwrap();
83
84 assert_eq!(conversation.member_count().await, 2);
85 assert!(conversation.is_functional_and_contains([&alice, &bob]).await);
86 })
87 .await
88 }
89
90 #[apply(all_cred_cipher)]
91 #[wasm_bindgen_test]
92 async fn should_clear_pending_commit_and_proposals(case: TestContext) {
93 let [alice] = case.sessions().await;
94 Box::pin(async move {
95 let commit = case
96 .create_conversation([&alice])
97 .await
98 .update_proposal_notify()
99 .await
100 .update_unmerged()
101 .await;
102
103 let conversation = commit.conversation();
104
105 assert!(conversation.has_pending_proposals().await);
106 assert!(conversation.has_pending_commit().await);
107
108 conversation
109 .guard()
110 .await
111 .conversation_mut()
112 .await
113 .commit_accepted(
114 &alice.transaction.session().await.unwrap(),
115 &alice.session.crypto_provider,
116 )
117 .await
118 .unwrap();
119 assert!(!conversation.has_pending_proposals().await);
120 assert!(!conversation.has_pending_commit().await);
121 })
122 .await
123 }
124
125 #[apply(all_cred_cipher)]
126 #[wasm_bindgen_test]
127 async fn should_clean_associated_key_material(case: TestContext) {
128 let [alice] = case.sessions().await;
129 Box::pin(async move {
130 let conversation = case.create_conversation([&alice]).await;
131 let initial_count = alice.transaction.count_entities().await;
132
133 let conversation = conversation.update_proposal_notify().await;
134 let post_proposal_count = alice.transaction.count_entities().await;
135 assert_eq!(
136 post_proposal_count.encryption_keypair,
137 initial_count.encryption_keypair + 1
138 );
139
140 conversation.commit_pending_proposals_notify().await;
141
142 let final_count = alice.transaction.count_entities().await;
143 assert_eq!(initial_count, final_count);
144 })
145 .await
146 }
147 }
148}