diff --git a/mls_validation_service/src/handlers.rs b/mls_validation_service/src/handlers.rs index 10191b9c0..aa769f9ed 100644 --- a/mls_validation_service/src/handlers.rs +++ b/mls_validation_service/src/handlers.rs @@ -129,10 +129,10 @@ fn validate_key_package(key_package_bytes: Vec) -> Result, - ) -> Self { + pub fn new(group_name: String, allow_list_account_addresses: Vec) -> Self { Self { group_name, allow_list_account_addresses, } } - pub(crate) fn from_proto(proto: GroupMutableMetadataProto) -> Result { + pub(crate) fn from_proto( + proto: GroupMutableMetadataProto, + ) -> Result { Ok(Self::new( proto.group_name.clone(), proto.allow_list_account_addresses.clone(), @@ -75,12 +77,13 @@ impl TryFrom for GroupMutableMetadata { } } -pub fn extract_group_mutable_metadata(group: &OpenMlsGroup) -> Result { - let extensions = group - .export_group_context() - .extensions(); +pub fn extract_group_mutable_metadata( + group: &OpenMlsGroup, +) -> Result { + let extensions = group.export_group_context().extensions(); for extension in extensions.iter() { - if let Extension::Unknown(0xff00, UnknownExtension(meta_data)) = extension { + // TODO: Replace with Constant + if let Extension::Unknown(0xff11, UnknownExtension(meta_data)) = extension { return GroupMutableMetadata::try_from(meta_data); } } diff --git a/xmtp_mls/src/groups/group_permissions.rs b/xmtp_mls/src/groups/group_permissions.rs index 2c9185ead..f1d68c4e0 100644 --- a/xmtp_mls/src/groups/group_permissions.rs +++ b/xmtp_mls/src/groups/group_permissions.rs @@ -318,13 +318,13 @@ impl PolicySet { )?, MembershipPolicies::try_from( proto - .update_group_name_policy - .ok_or(PolicyError::InvalidPolicy)?, + .update_group_name_policy + .ok_or(PolicyError::InvalidPolicy)?, )?, MembershipPolicies::try_from( proto - .update_allow_list_policy - .ok_or(PolicyError::InvalidPolicy)?, + .update_allow_list_policy + .ok_or(PolicyError::InvalidPolicy)?, )?, )) } @@ -353,10 +353,11 @@ fn default_remove_installation_policy() -> MembershipPolicies { /// A policy where any member can add or remove any other member or set group name pub(crate) fn policy_everyone_is_admin() -> PolicySet { PolicySet::new( - MembershipPolicies::allow(), - MembershipPolicies::allow(), - MembershipPolicies::allow(), - MembershipPolicies::deny()) + MembershipPolicies::allow(), + MembershipPolicies::allow(), + MembershipPolicies::allow(), + MembershipPolicies::deny(), + ) } /// A policy where only the group creator can add or remove members @@ -471,7 +472,7 @@ mod tests { #[test] fn test_allow_all() { let permissions = PolicySet::new( - MembershipPolicies::allow(), + MembershipPolicies::allow(), MembershipPolicies::allow(), MembershipPolicies::allow(), MembershipPolicies::allow(), @@ -484,7 +485,7 @@ mod tests { #[test] fn test_deny() { let permissions = PolicySet::new( - MembershipPolicies::deny(), + MembershipPolicies::deny(), MembershipPolicies::deny(), MembershipPolicies::deny(), MembershipPolicies::deny(), diff --git a/xmtp_mls/src/groups/intents.rs b/xmtp_mls/src/groups/intents.rs index ab47f58b0..c2ee7dd53 100644 --- a/xmtp_mls/src/groups/intents.rs +++ b/xmtp_mls/src/groups/intents.rs @@ -13,11 +13,12 @@ use xmtp_proto::xmtp::mls::database::{ SendWelcomes as SendWelcomesProto, }, remove_members_data::{Version as RemoveMembersVersion, V1 as RemoveMembersV1}, - update_metadata_data::{Version as UpdateMetadataVersion, V1 as UpdateMetadataV1}, send_message_data::{Version as SendMessageVersion, V1 as SendMessageV1}, + update_metadata_data::{Version as UpdateMetadataVersion, V1 as UpdateMetadataV1}, AccountAddresses, AddMembersData, AddressesOrInstallationIds as AddressesOrInstallationIdsProtoWrapper, InstallationIds, - PostCommitAction as PostCommitActionProto, RemoveMembersData, SendMessageData, UpdateMetadataData, + PostCommitAction as PostCommitActionProto, RemoveMembersData, SendMessageData, + UpdateMetadataData, }; use crate::{ @@ -231,7 +232,10 @@ pub struct UpdateMetadataIntentData { impl UpdateMetadataIntentData { pub fn new(group_name: String, allow_list_account_addresses: Vec) -> Self { - Self { group_name, allow_list_account_addresses } + Self { + group_name, + allow_list_account_addresses, + } } pub(crate) fn to_bytes(&self) -> Vec { @@ -252,13 +256,11 @@ impl UpdateMetadataIntentData { pub(crate) fn from_bytes(data: &[u8]) -> Result { let msg = UpdateMetadataData::decode(data)?; let group_name = match msg.version { - Some(UpdateMetadataVersion::V1(ref v1)) => v1 - .group_name.clone(), + Some(UpdateMetadataVersion::V1(ref v1)) => v1.group_name.clone(), None => return Err(IntentError::Generic("missing payload".to_string())), }; let allow_list_account_addresses = match msg.version { - Some(UpdateMetadataVersion::V1(v1)) => v1 - .allow_list_account_addresses.clone(), + Some(UpdateMetadataVersion::V1(v1)) => v1.allow_list_account_addresses.clone(), None => return Err(IntentError::Generic("missing payload".to_string())), }; @@ -420,7 +422,10 @@ mod tests { let wallet = generate_local_wallet(); let account_address = wallet.get_address(); - let intent = UpdateMetadataIntentData::new("group name".to_string(), vec![account_address.clone()].into()); + let intent = UpdateMetadataIntentData::new( + "group name".to_string(), + vec![account_address.clone()].into(), + ); let as_bytes: Vec = intent.clone().try_into().unwrap(); let restored_intent = UpdateMetadataIntentData::from_bytes(as_bytes.as_slice()).unwrap(); diff --git a/xmtp_mls/src/groups/mod.rs b/xmtp_mls/src/groups/mod.rs index 798520118..837523442 100644 --- a/xmtp_mls/src/groups/mod.rs +++ b/xmtp_mls/src/groups/mod.rs @@ -9,8 +9,14 @@ pub mod validated_commit; use intents::SendMessageIntentData; use openmls::{ - extensions::{Extension, Extensions, Metadata, UnknownExtension}, - group::{MlsGroupCreateConfig, MlsGroupJoinConfig, ProposalError}, + credentials::CredentialType, + extensions::{ + Extension, ExtensionType, Extensions, Metadata, RequiredCapabilitiesExtension, + UnknownExtension, + }, + group::{ + CommitToPendingProposalsError, MlsGroupCreateConfig, MlsGroupJoinConfig, ProposalError, + }, prelude::{ CredentialWithKey, CryptoConfig, Error as TlsCodecError, GroupId, MlsGroup as OpenMlsGroup, StagedWelcome, Welcome as MlsWelcome, WireFormatPolicy, @@ -23,16 +29,26 @@ use thiserror::Error; use xmtp_cryptography::signature::is_valid_ed25519_public_key; use xmtp_proto::{ api_client::XmtpMlsClient, - xmtp::mls::{api::v1::{ - group_message::{Version as GroupMessageVersion, V1 as GroupMessageV1}, - GroupMessage, - }, database::AccountAddresses, message_contents::{plaintext_envelope::{Content, V1}, GroupMutableMetadataV1, PlaintextEnvelope}}, + xmtp::mls::{ + api::v1::{ + group_message::{Version as GroupMessageVersion, V1 as GroupMessageV1}, + GroupMessage, + }, + message_contents::{ + plaintext_envelope::{Content, V1}, + GroupMutableMetadataV1, PlaintextEnvelope, + }, + }, }; -use self::{group_metadata::extract_group_metadata, group_mutable_metadata::{GroupMutableMetadata, GroupMutableMetadataError}, intents::UpdateMetadataIntentData}; use self::group_mutable_metadata::extract_group_mutable_metadata; pub use self::group_permissions::PreconfiguredPolicies; pub use self::intents::{AddressesOrInstallationIds, IntentError}; +use self::{ + group_metadata::extract_group_metadata, + group_mutable_metadata::{GroupMutableMetadata, GroupMutableMetadataError}, + intents::UpdateMetadataIntentData, +}; use self::{ group_metadata::{ConversationType, GroupMetadata, GroupMetadataError}, group_permissions::PolicySet, @@ -122,6 +138,8 @@ pub enum GroupError { EncodeError(#[from] prost::EncodeError), #[error("group context extension proposal error: {0}")] ProposalError(#[from] ProposalError<()>), + #[error("commit to pending proposal error: {0}")] + CommitToPendingProposalsError(#[from] CommitToPendingProposalsError), } impl RetryableError for GroupError { @@ -355,13 +373,9 @@ where self.sync_until_intent_resolved(conn, intent.id).await } - pub async fn update_group_metadata( - &self, - group_name: String, - ) -> Result<(), GroupError> { + pub async fn update_group_metadata(&self, group_name: String) -> Result<(), GroupError> { // TODO: enable mutable metadata allow list let empty_account_addresses = vec![]; - let conn = &mut self.client.store.conn()?; let intent_data: Vec = UpdateMetadataIntentData::new(group_name, empty_account_addresses).to_bytes(); @@ -504,7 +518,6 @@ pub fn build_mutable_metadata_extension( group_name: String, allow_list_account_addresses: Vec, ) -> Result { - let mutable_metadata: GroupMutableMetadataV1 = GroupMutableMetadataV1 { group_name, allow_list_account_addresses, @@ -513,16 +526,23 @@ pub fn build_mutable_metadata_extension( let unknown_gc_extension = UnknownExtension(mutable_metadata.encode_to_vec()); // TODO: Where should the constant hex value live? - Ok(Extension::Unknown(0xff00, unknown_gc_extension)) + Ok(Extension::Unknown(0xff11, unknown_gc_extension)) } fn build_group_config( protected_metadata_extension: Extension, - mutable_metadata_extension: Extension + mutable_metadata_extension: Extension, ) -> Result { let mut extensions = Extensions::single(protected_metadata_extension); extensions.add(mutable_metadata_extension)?; + let rc_extensions = &[ExtensionType::Unknown(0xff11)]; + let proposals = &[]; + let credentials = &[CredentialType::Basic]; + let required_capabilities = + RequiredCapabilitiesExtension::new(rc_extensions, proposals, credentials); + + let _ = extensions.add(Extension::RequiredCapabilities(required_capabilities))?; Ok(MlsGroupCreateConfig::builder() .with_group_context_extensions(extensions)? .crypto_config(CryptoConfig::with_default_version(CIPHERSUITE)) @@ -542,7 +562,7 @@ fn build_group_join_config() -> MlsGroupJoinConfig { #[cfg(test)] mod tests { - use openmls::{group, prelude::Member}; + use openmls::prelude::Member; use prost::Message; use xmtp_api_grpc::grpc_api_helper::Client as GrpcClient; use xmtp_cryptography::utils::generate_local_wallet; @@ -1002,19 +1022,28 @@ mod tests { // TODO: Add check for updating group mutable metadata let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; let bola = ClientBuilder::new_test_client(&generate_local_wallet()).await; - let charlie = ClientBuilder::new_test_client(&generate_local_wallet()).await; let policies = Some(PreconfiguredPolicies::GroupCreatorIsAdmin); - let amal_group: MlsGroup<_> = amal.create_group(policies) - .unwrap(); + let amal_group: MlsGroup<_> = amal.create_group(policies).unwrap(); amal_group.sync().await.unwrap(); + // Add bola to the group + println!("Adding Bola to the group"); + amal_group + .add_members(vec![bola.account_address()]) + .await + .unwrap(); + + let bola_group = receive_group_invite(&bola).await; + bola_group.sync().await.unwrap(); + let mut group_mutable_metadata = amal_group.mutable_metadata().unwrap(); assert!(group_mutable_metadata.group_name.eq("New Group")); - // Update group name + // Update group name + println!("Updating group name"); amal_group .update_group_metadata("New Group Name 1".to_string()) .await diff --git a/xmtp_mls/src/groups/sync.rs b/xmtp_mls/src/groups/sync.rs index fb2ca4741..cacbe2ed9 100644 --- a/xmtp_mls/src/groups/sync.rs +++ b/xmtp_mls/src/groups/sync.rs @@ -2,11 +2,15 @@ use std::{collections::HashMap, mem::discriminant}; use log::debug; use openmls::{ - credentials::BasicCredential, extensions::Extensions, framing::ProtocolMessage, group::MergePendingCommitError, messages::proposals::GroupContextExtensionProposal, prelude::{ + credentials::BasicCredential, + framing::ProtocolMessage, + group::MergePendingCommitError, + prelude::{ tls_codec::{Deserialize, Serialize}, LeafNodeIndex, MlsGroup as OpenMlsGroup, MlsMessageBodyIn, MlsMessageIn, PrivateMessageIn, ProcessedMessage, ProcessedMessageContent, Sender, - }, prelude_test::KeyPackage + }, + prelude_test::KeyPackage, }; use openmls_traits::OpenMlsProvider; use prost::bytes::Bytes; @@ -29,7 +33,7 @@ use xmtp_proto::{ use super::{ intents::{ AddMembersIntentData, AddressesOrInstallationIds, Installation, PostCommitAction, - RemoveMembersIntentData, SendMessageIntentData, SendWelcomesAction, UpdateMetadataIntentData, + RemoveMembersIntentData, SendMessageIntentData, SendWelcomesAction, }, members::GroupMember, GroupError, MlsGroup, @@ -170,7 +174,10 @@ where let conn = provider.conn(); match intent.kind { - IntentKind::AddMembers | IntentKind::RemoveMembers | IntentKind::KeyUpdate | IntentKind::MetadataUpdate => { + IntentKind::AddMembers + | IntentKind::RemoveMembers + | IntentKind::KeyUpdate + | IntentKind::MetadataUpdate => { if !allow_epoch_increment { return Err(MessageProcessingError::EpochIncrementNotAllowed); } @@ -241,7 +248,6 @@ where }; } }; - conn.set_group_intent_committed(intent.id)?; Ok(()) @@ -361,6 +367,7 @@ where envelope.created_ns, allow_epoch_increment, ), + // No matching intent found Ok(None) => self.process_external_message( openmls_group, @@ -369,6 +376,7 @@ where envelope.created_ns, allow_epoch_increment, ), + Err(err) => Err(MessageProcessingError::Storage(err)), } } @@ -651,16 +659,19 @@ where Ok((commit.tls_serialize_detached()?, None)) } IntentKind::MetadataUpdate => { - // TODO: Not implemented - let intent_data = UpdateMetadataIntentData::from_bytes(intent.data.as_slice())?; - println!("Trying to process Update metadata intent data: {}", intent_data.group_name); let mutable_metadata = build_mutable_metadata_extension( "New Group".to_string(), vec![self.client.account_address().clone()], )?; + let mut extensions = openmls_group.extensions().clone(); + extensions.add_or_replace(mutable_metadata); let (commit, _) = openmls_group.propose_group_context_extensions( provider, - Extensions::single(mutable_metadata), + extensions, + &self.client.identity.installation_keys, + )?; + openmls_group.commit_to_pending_proposals( + provider, &self.client.identity.installation_keys, )?; diff --git a/xmtp_mls/src/groups/validated_commit.rs b/xmtp_mls/src/groups/validated_commit.rs index 7e6076934..227be3d25 100644 --- a/xmtp_mls/src/groups/validated_commit.rs +++ b/xmtp_mls/src/groups/validated_commit.rs @@ -81,6 +81,7 @@ pub struct ValidatedCommit { pub(crate) installations_removed: Vec, } +// TODO - Validate the commit against the group mutable metadata impl ValidatedCommit { // Build a ValidatedCommit from a StagedCommit and OpenMlsGroup pub fn from_staged_commit( diff --git a/xmtp_mls/src/identity.rs b/xmtp_mls/src/identity.rs index 68d3d0ba9..2c46ad2ad 100644 --- a/xmtp_mls/src/identity.rs +++ b/xmtp_mls/src/identity.rs @@ -5,14 +5,11 @@ use openmls::{ credentials::{ errors::{BasicCredentialError, CredentialError}, BasicCredential, - }, - extensions::{errors::InvalidExtensionError, ApplicationIdExtension, LastResortExtension}, - prelude::{ + }, extensions::{errors::InvalidExtensionError, ApplicationIdExtension, LastResortExtension}, messages::proposals::ProposalType, prelude::{ tls_codec::{Error as TlsCodecError, Serialize}, Capabilities, Credential as OpenMlsCredential, CredentialWithKey, CryptoConfig, Extension, ExtensionType, Extensions, KeyPackage, KeyPackageNewError, Lifetime, - }, - versions::ProtocolVersion, + }, versions::ProtocolVersion }; use openmls_basic_credential::SignatureKeyPair; use openmls_traits::{types::CryptoError, OpenMlsProvider}; @@ -198,7 +195,7 @@ impl Identity { let capabilities = Capabilities::new( None, Some(&[CIPHERSUITE]), - Some(&[ExtensionType::LastResort, ExtensionType::ApplicationId]), + Some(&[ExtensionType::LastResort, ExtensionType::ApplicationId, ExtensionType::Unknown(0xff11), ExtensionType::ImmutableMetadata]), None, None, );