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::Client};
21
22impl MlsConversation {
24 #[cfg_attr(test, crate::durable)]
26 pub(crate) async fn commit_accepted(&mut self, client: &Client, 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: TestCase) {
64 run_test_with_client_ids(case.clone(), ["alice", "bob"], move |[alice_central, bob_central]| {
65 Box::pin(async move {
66 let id = conversation_id();
67 alice_central
68 .context
69 .new_conversation(&id, case.credential_type, case.cfg.clone())
70 .await
71 .unwrap();
72 alice_central.invite_all(&case, &id, [&bob_central]).await.unwrap();
73 assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 2);
74 alice_central
75 .context
76 .conversation(&id)
77 .await
78 .unwrap()
79 .remove_members(&[bob_central.get_client_id().await])
80 .await
81 .unwrap();
82 assert_eq!(alice_central.get_conversation_unchecked(&id).await.members().len(), 1);
83 })
84 })
85 .await
86 }
87
88 #[apply(all_cred_cipher)]
89 #[wasm_bindgen_test]
90 async fn should_clear_pending_commit_and_proposals(case: TestCase) {
91 use crate::mls::HasClientAndProvider as _;
92
93 run_test_with_client_ids(case.clone(), ["alice"], move |[mut alice_central]| {
94 Box::pin(async move {
95 let id = conversation_id();
96 alice_central
97 .context
98 .new_conversation(&id, case.credential_type, case.cfg.clone())
99 .await
100 .unwrap();
101 alice_central.context.new_update_proposal(&id).await.unwrap();
102 alice_central.create_unmerged_commit(&id).await;
103 assert!(!alice_central.pending_proposals(&id).await.is_empty());
104 assert!(alice_central.pending_commit(&id).await.is_some());
105 alice_central
106 .get_conversation_unchecked(&id)
107 .await
108 .commit_accepted(
109 &alice_central.context.client().await.unwrap(),
110 &alice_central.central.mls_backend,
111 )
112 .await
113 .unwrap();
114 assert!(alice_central.pending_commit(&id).await.is_none());
115 assert!(alice_central.pending_proposals(&id).await.is_empty());
116 })
117 })
118 .await
119 }
120
121 #[apply(all_cred_cipher)]
122 #[wasm_bindgen_test]
123 async fn should_clean_associated_key_material(case: TestCase) {
124 run_test_with_client_ids(case.clone(), ["alice"], move |[alice_central]| {
125 Box::pin(async move {
126 let id = conversation_id();
127 alice_central
128 .context
129 .new_conversation(&id, case.credential_type, case.cfg.clone())
130 .await
131 .unwrap();
132
133 let initial_count = alice_central.context.count_entities().await;
134
135 alice_central.context.new_update_proposal(&id).await.unwrap();
136 let post_proposal_count = alice_central.context.count_entities().await;
137 assert_eq!(
138 post_proposal_count.encryption_keypair,
139 initial_count.encryption_keypair + 1
140 );
141
142 alice_central
143 .context
144 .conversation(&id)
145 .await
146 .unwrap()
147 .commit_pending_proposals()
148 .await
149 .unwrap();
150
151 let final_count = alice_central.context.count_entities().await;
152 assert_eq!(initial_count, final_count);
153 })
154 })
155 .await
156 }
157 }
158}