diff --git a/mls-rs/src/group/commit.rs b/mls-rs/src/group/commit.rs index f8caa7c8..f01fb31c 100644 --- a/mls-rs/src/group/commit.rs +++ b/mls-rs/src/group/commit.rs @@ -94,6 +94,9 @@ pub struct CommitOutput { /// functionality. This value is set if [`MlsRules::commit_options`] returns /// `allow_external_commit` set to true. pub external_commit_group_info: Option, + /// Proposals that were received in the prior epoch but not included in the following commit. + #[cfg(feature = "by_ref_proposal")] + pub unused_proposals: Vec>, } #[cfg_attr(all(feature = "ffi", not(test)), ::safer_ffi_gen::safer_ffi_gen)] @@ -117,6 +120,20 @@ impl CommitOutput { pub fn ratchet_tree(&self) -> Option<&ExportedTree<'static>> { self.ratchet_tree.as_ref() } + + /// A group info that can be provided to new members in order to enable external commit + /// functionality. This value is set if [`MlsRules::commit_options`] returns + /// `allow_external_commit` set to true. + #[cfg(feature = "ffi")] + pub fn external_commit_group_info(&self) -> Option<&MlsMessage> { + self.external_commit_group_info.as_ref() + } + + /// Proposals that were received in the prior epoch but not included in the following commit. + #[cfg(all(feature = "ffi", feature = "by_ref_proposal"))] + pub fn unused_proposals(&self) -> &[crate::mls_rules::ProposalInfo] { + &self.unused_proposals + } } /// Build a commit with multiple proposals by-value. @@ -722,6 +739,8 @@ where welcome_messages, ratchet_tree, external_commit_group_info, + #[cfg(feature = "by_ref_proposal")] + unused_proposals: provisional_state.rejected_proposals, }) } diff --git a/mls-rs/src/group/framing.rs b/mls-rs/src/group/framing.rs index 31527e5b..891127a4 100644 --- a/mls-rs/src/group/framing.rs +++ b/mls-rs/src/group/framing.rs @@ -36,6 +36,10 @@ impl From<&Content> for ContentType { } } +#[cfg_attr( + all(feature = "ffi", not(test)), + safer_ffi_gen::ffi_type(clone, opaque) +)] #[derive(Clone, Copy, Debug, PartialEq, Eq, MlsSize, MlsEncode, MlsDecode)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[repr(u8)] diff --git a/mls-rs/src/group/message_processor.rs b/mls-rs/src/group/message_processor.rs index ba70ecac..b11c24bc 100644 --- a/mls-rs/src/group/message_processor.rs +++ b/mls-rs/src/group/message_processor.rs @@ -35,10 +35,10 @@ use super::proposal_ref::ProposalRef; #[cfg(not(feature = "by_ref_proposal"))] use crate::group::proposal_cache::resolve_for_commit; -#[cfg(any(feature = "state_update", feature = "by_ref_proposal"))] +#[cfg(feature = "by_ref_proposal")] use super::proposal::Proposal; -#[cfg(all(feature = "state_update", feature = "custom_proposal"))] +#[cfg(feature = "custom_proposal")] use super::proposal_filter::ProposalInfo; #[cfg(feature = "state_update")] @@ -72,7 +72,7 @@ pub(crate) struct ProvisionalState { pub(crate) group_context: GroupContext, pub(crate) external_init_index: Option, pub(crate) indexes_of_added_kpkgs: Vec, - #[cfg(all(feature = "state_update", feature = "by_ref_proposal"))] + #[cfg(feature = "by_ref_proposal")] pub(crate) rejected_proposals: Vec>, } diff --git a/mls-rs/src/group/proposal_cache.rs b/mls-rs/src/group/proposal_cache.rs index 211a5c48..26f6d8d3 100644 --- a/mls-rs/src/group/proposal_cache.rs +++ b/mls-rs/src/group/proposal_cache.rs @@ -194,7 +194,7 @@ impl GroupState { let roster = self.public_tree.roster(); let group_extensions = &self.context.extensions; - #[cfg(all(feature = "by_ref_proposal", feature = "state_update"))] + #[cfg(feature = "by_ref_proposal")] let all_proposals = proposals.clone(); let origin = match sender { @@ -248,7 +248,7 @@ impl GroupState { .apply_proposals(&sender, &proposals, commit_time) .await?; - #[cfg(all(feature = "by_ref_proposal", feature = "state_update"))] + #[cfg(feature = "by_ref_proposal")] let rejected_proposals = rejected_proposals(all_proposals, &applier_output.applied_proposals); @@ -268,7 +268,7 @@ impl GroupState { applied_proposals: proposals, external_init_index: applier_output.external_init_index, indexes_of_added_kpkgs: applier_output.indexes_of_added_kpkgs, - #[cfg(all(feature = "by_ref_proposal", feature = "state_update"))] + #[cfg(feature = "by_ref_proposal")] rejected_proposals, }) } @@ -284,14 +284,14 @@ impl Extend<(ProposalRef, CachedProposal)> for ProposalCache { } } -#[cfg(all(feature = "by_ref_proposal", feature = "state_update"))] +#[cfg(feature = "by_ref_proposal")] fn has_ref(proposals: &ProposalBundle, reference: &ProposalRef) -> bool { proposals .iter_proposals() .any(|p| matches!(&p.source, ProposalSource::ByReference(r) if r == reference)) } -#[cfg(all(feature = "by_ref_proposal", feature = "state_update"))] +#[cfg(feature = "by_ref_proposal")] fn rejected_proposals( all_proposals: ProposalBundle, accepted_proposals: &ProposalBundle, diff --git a/mls-rs/src/group/proposal_filter/bundle.rs b/mls-rs/src/group/proposal_filter/bundle.rs index 69d21f45..a1783625 100644 --- a/mls-rs/src/group/proposal_filter/bundle.rs +++ b/mls-rs/src/group/proposal_filter/bundle.rs @@ -434,6 +434,10 @@ impl ProposalBundle { } } +#[cfg_attr( + all(feature = "ffi", not(test)), + safer_ffi_gen::ffi_type(clone, opaque) +)] #[derive(Clone, Debug, PartialEq)] pub enum ProposalSource { ByValue, @@ -442,6 +446,7 @@ pub enum ProposalSource { Local, } +#[cfg_attr(all(feature = "ffi", not(test)), safer_ffi_gen::ffi_type(opaque))] #[derive(Clone, Debug, PartialEq)] #[non_exhaustive] /// Proposal description used as input to a @@ -455,7 +460,8 @@ pub struct ProposalInfo { pub source: ProposalSource, } -impl ProposalInfo { +#[cfg_attr(all(feature = "ffi", not(test)), ::safer_ffi_gen::safer_ffi_gen)] +impl ProposalInfo { /// Create a new ProposalInfo. /// /// The resulting value will be either transmitted with a commit or @@ -464,7 +470,8 @@ impl ProposalInfo { /// /// This function is useful when implementing custom /// [`MlsRules`](crate::MlsRules). - pub fn new(proposal: Proposal, sender: Sender, can_transmit: bool) -> ProposalInfo { + #[cfg_attr(all(feature = "ffi", not(test)), safer_ffi_gen::safer_ffi_gen_ignore)] + pub fn new(proposal: T, sender: Sender, can_transmit: bool) -> Self { let source = if can_transmit { ProposalSource::ByValue } else { @@ -477,9 +484,18 @@ impl ProposalInfo { source, } } -} -impl ProposalInfo { + #[cfg(all(feature = "ffi", not(test)))] + pub fn sender(&self) -> &Sender { + &self.sender + } + + #[cfg(all(feature = "ffi", not(test)))] + pub fn source(&self) -> &ProposalSource { + &self.source + } + + #[cfg_attr(all(feature = "ffi", not(test)), safer_ffi_gen::safer_ffi_gen_ignore)] pub fn map(self, f: F) -> ProposalInfo where F: FnOnce(T) -> U, @@ -491,6 +507,7 @@ impl ProposalInfo { } } + #[cfg_attr(all(feature = "ffi", not(test)), safer_ffi_gen::safer_ffi_gen_ignore)] pub fn as_ref(&self) -> ProposalInfo<&T> { ProposalInfo { proposal: &self.proposal, @@ -519,6 +536,9 @@ impl ProposalInfo { } } +#[cfg(all(feature = "ffi", not(test)))] +safer_ffi_gen::specialize!(ProposalInfoFfi = ProposalInfo); + pub trait Proposable: Sized { const TYPE: ProposalType; diff --git a/mls-rs/src/group/proposal_ref.rs b/mls-rs/src/group/proposal_ref.rs index c6b10b23..9e9ec483 100644 --- a/mls-rs/src/group/proposal_ref.rs +++ b/mls-rs/src/group/proposal_ref.rs @@ -5,6 +5,10 @@ use super::*; use crate::hash_reference::HashReference; +#[cfg_attr( + all(feature = "ffi", not(test)), + safer_ffi_gen::ffi_type(clone, opaque) +)] #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, MlsSize, MlsEncode, MlsDecode)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] /// Unique identifier for a proposal message. @@ -18,6 +22,7 @@ impl Deref for ProposalRef { } } +#[cfg_attr(all(feature = "ffi", not(test)), ::safer_ffi_gen::safer_ffi_gen)] impl ProposalRef { #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)] pub(crate) async fn from_content( @@ -31,6 +36,10 @@ impl ProposalRef { .await?, )) } + + pub fn as_slice(&self) -> &[u8] { + &self.0 + } } #[cfg(test)]