Skip to main content

core_crypto/mls/conversation/immutable/
clients.rs

1use std::collections::HashSet;
2
3use log::trace;
4use openmls::prelude::{LeafNodeIndex, Proposal};
5
6use crate::{ClientId, HISTORY_CLIENT_ID_PREFIX, RecursiveError, mls::conversation::immutable::Result};
7
8impl super::Conversation {
9    /// Exports the clients from a conversation
10    /// Does NOT include history client ids.
11    pub async fn get_client_ids(&self) -> Result<Vec<ClientId>> {
12        let prefix = HISTORY_CLIENT_ID_PREFIX.as_bytes();
13        self.group()
14            .await
15            .members()
16            .filter(|member| !member.credential.identity().starts_with(prefix))
17            .map(|kp| {
18                ClientId::new_from_bytes(kp.credential.identity().to_owned())
19                    .map_err(RecursiveError::mls_client("new client id from bytes"))
20                    .map_err(Into::into)
21            })
22            .collect()
23    }
24
25    /// Exports the history client ids from a conversation
26    pub async fn get_history_client_ids(&self) -> Vec<Vec<u8>> {
27        let prefix = HISTORY_CLIENT_ID_PREFIX.as_bytes();
28
29        self.group()
30            .await
31            .members()
32            .filter_map(|kp| {
33                let identity = kp.credential.identity();
34
35                identity.starts_with(prefix).then(|| identity.to_owned())
36            })
37            .collect()
38    }
39    /// Gather pending remove proposals
40    async fn pending_removals(&self) -> Vec<LeafNodeIndex> {
41        self.group()
42            .await
43            .pending_proposals()
44            .filter_map(|proposal| match proposal.proposal() {
45                Proposal::Remove(remove) => Some(remove.removed()),
46                _ => None,
47            })
48            .collect::<Vec<_>>()
49    }
50
51    /// Get actual group members and subtract pending remove proposals
52    pub async fn members_in_next_epoch(&self) -> Result<Vec<ClientId>> {
53        let pending_removals = self.pending_removals().await;
54        let existing_clients = self
55            .group()
56            .await
57            .members()
58            .filter_map(|member| {
59                if !pending_removals.contains(&member.index) {
60                    let client_id_result = ClientId::new_from_bytes(member.credential.identity().to_owned())
61                        .map_err(RecursiveError::mls_client("new client id from bytes"))
62                        .map_err(Into::into);
63                    Some(client_id_result)
64                } else {
65                    trace!(client_index:% = member.index; "Client is pending removal");
66                    None
67                }
68            })
69            .collect::<Result<HashSet<_>>>()?;
70        Ok(existing_clients.into_iter().collect())
71    }
72}