From eeeb0db43d732177942c86fe7b9f4c283fd114bb Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Wed, 18 Sep 2024 10:11:59 -0400 Subject: [PATCH 01/17] provide server timestamps in identity action updates --- xmtp_id/src/associations/association_log.rs | 35 +- xmtp_id/src/associations/member.rs | 9 +- xmtp_id/src/associations/mod.rs | 4 +- xmtp_id/src/associations/serialization.rs | 2 + xmtp_id/src/associations/state.rs | 8 +- .../src/gen/xmtp.identity.associations.rs | 827 +++++++++--------- .../gen/xmtp.identity.associations.serde.rs | 22 + 7 files changed, 481 insertions(+), 426 deletions(-) diff --git a/xmtp_id/src/associations/association_log.rs b/xmtp_id/src/associations/association_log.rs index ded06670c..31f81ee0d 100644 --- a/xmtp_id/src/associations/association_log.rs +++ b/xmtp_id/src/associations/association_log.rs @@ -40,6 +40,7 @@ pub trait IdentityAction: Send + 'static { fn update_state( &self, existing_state: Option, + server_timestamp_ns: u64, ) -> Result; fn signatures(&self) -> Vec>; fn replay_check(&self, state: &AssociationState) -> Result<(), AssociationError> { @@ -66,6 +67,7 @@ impl IdentityAction for CreateInbox { fn update_state( &self, existing_state: Option, + _server_timestamp_ns: u64, ) -> Result { if existing_state.is_some() { return Err(AssociationError::MultipleCreate); @@ -106,6 +108,7 @@ impl IdentityAction for AddAssociation { fn update_state( &self, maybe_existing_state: Option, + server_timestamp_ns: u64, ) -> Result { let existing_state = maybe_existing_state.ok_or(AssociationError::NotCreated)?; self.replay_check(&existing_state)?; @@ -176,7 +179,11 @@ impl IdentityAction for AddAssociation { self.new_member_identifier.kind(), )?; - let new_member = Member::new(new_member_address.clone(), Some(existing_entity_id)); + let new_member = Member::new( + new_member_address.clone(), + Some(existing_entity_id), + Some(server_timestamp_ns), + ); Ok(existing_state.add(new_member)) } @@ -200,6 +207,7 @@ impl IdentityAction for RevokeAssociation { fn update_state( &self, maybe_existing_state: Option, + _server_timestamp_ns: u64, ) -> Result { let existing_state = maybe_existing_state.ok_or(AssociationError::NotCreated)?; self.replay_check(&existing_state)?; @@ -255,6 +263,7 @@ impl IdentityAction for ChangeRecoveryAddress { fn update_state( &self, existing_state: Option, + _server_timestamp_ns: u64, ) -> Result { let existing_state = existing_state.ok_or(AssociationError::NotCreated)?; self.replay_check(&existing_state)?; @@ -292,12 +301,19 @@ impl IdentityAction for Action { fn update_state( &self, existing_state: Option, + server_timestamp_ns: u64, ) -> Result { match self { - Action::CreateInbox(event) => event.update_state(existing_state), - Action::AddAssociation(event) => event.update_state(existing_state), - Action::RevokeAssociation(event) => event.update_state(existing_state), - Action::ChangeRecoveryAddress(event) => event.update_state(existing_state), + Action::CreateInbox(event) => event.update_state(existing_state, server_timestamp_ns), + Action::AddAssociation(event) => { + event.update_state(existing_state, server_timestamp_ns) + } + Action::RevokeAssociation(event) => { + event.update_state(existing_state, server_timestamp_ns) + } + Action::ChangeRecoveryAddress(event) => { + event.update_state(existing_state, server_timestamp_ns) + } } } @@ -315,16 +331,16 @@ impl IdentityAction for Action { #[derive(Debug, Clone)] pub struct IdentityUpdate { pub inbox_id: String, - pub client_timestamp_ns: u64, + pub server_timestamp_ns: u64, pub actions: Vec, } impl IdentityUpdate { - pub fn new(actions: Vec, inbox_id: String, client_timestamp_ns: u64) -> Self { + pub fn new(actions: Vec, inbox_id: String, server_timestamp_ns: u64) -> Self { Self { inbox_id, actions, - client_timestamp_ns, + server_timestamp_ns, } } } @@ -333,10 +349,11 @@ impl IdentityAction for IdentityUpdate { fn update_state( &self, existing_state: Option, + server_timestamp_ns: u64, ) -> Result { let mut state = existing_state.clone(); for action in &self.actions { - state = Some(action.update_state(state)?); + state = Some(action.update_state(state, server_timestamp_ns)?); } let new_state = state.ok_or(AssociationError::NotCreated)?; diff --git a/xmtp_id/src/associations/member.rs b/xmtp_id/src/associations/member.rs index 44a31cde9..62088e4fd 100644 --- a/xmtp_id/src/associations/member.rs +++ b/xmtp_id/src/associations/member.rs @@ -77,13 +77,19 @@ impl From> for MemberIdentifier { pub struct Member { pub identifier: MemberIdentifier, pub added_by_entity: Option, + pub created_at_ns: Option, } impl Member { - pub fn new(identifier: MemberIdentifier, added_by_entity: Option) -> Self { + pub fn new( + identifier: MemberIdentifier, + added_by_entity: Option, + created_at_ns: Option, + ) -> Self { Self { identifier, added_by_entity, + created_at_ns, } } @@ -118,6 +124,7 @@ mod tests { Self { identifier: MemberIdentifier::default(), added_by_entity: None, + created_at_ns: None, } } } diff --git a/xmtp_id/src/associations/mod.rs b/xmtp_id/src/associations/mod.rs index ed8f90e2a..687b06773 100644 --- a/xmtp_id/src/associations/mod.rs +++ b/xmtp_id/src/associations/mod.rs @@ -23,7 +23,7 @@ pub fn apply_update( initial_state: AssociationState, update: IdentityUpdate, ) -> Result { - update.update_state(Some(initial_state)) + update.update_state(Some(initial_state), update.server_timestamp_ns) } // Get the current state from an array of `IdentityUpdate`s. Entire operation fails if any operation fails @@ -32,7 +32,7 @@ pub fn get_state>( ) -> Result { let mut state = None; for update in updates.as_ref().iter() { - let res = update.update_state(state); + let res = update.update_state(state, update.server_timestamp_ns); state = Some(res?); } diff --git a/xmtp_id/src/associations/serialization.rs b/xmtp_id/src/associations/serialization.rs index 0e67e37e6..c4ac33050 100644 --- a/xmtp_id/src/associations/serialization.rs +++ b/xmtp_id/src/associations/serialization.rs @@ -318,6 +318,7 @@ impl From for MemberProto { MemberProto { identifier: Some(member.identifier.into()), added_by_entity: member.added_by_entity.map(Into::into), + server_timestamp_ns: member.created_at_ns, } } } @@ -332,6 +333,7 @@ impl TryFrom for Member { .ok_or(DeserializationError::MissingMemberIdentifier)? .try_into()?, added_by_entity: proto.added_by_entity.map(TryInto::try_into).transpose()?, + created_at_ns: proto.server_timestamp_ns, }) } } diff --git a/xmtp_id/src/associations/state.rs b/xmtp_id/src/associations/state.rs index 83cc8752e..2c8af5f7e 100644 --- a/xmtp_id/src/associations/state.rs +++ b/xmtp_id/src/associations/state.rs @@ -163,13 +163,9 @@ impl AssociationState { pub fn new(account_address: String, nonce: u64) -> Self { let inbox_id = generate_inbox_id(&account_address, &nonce); let identifier = MemberIdentifier::Address(account_address.clone()); - let new_member = Member::new(identifier.clone(), None); + let new_member = Member::new(identifier.clone(), None, None); Self { - members: { - let mut members = HashMap::new(); - members.insert(identifier, new_member); - members - }, + members: HashMap::from_iter([(identifier, new_member)]), seen_signatures: HashSet::new(), recovery_address: account_address.to_lowercase(), inbox_id, diff --git a/xmtp_proto/src/gen/xmtp.identity.associations.rs b/xmtp_proto/src/gen/xmtp.identity.associations.rs index 8ec6b99a5..98ce4f5c2 100644 --- a/xmtp_proto/src/gen/xmtp.identity.associations.rs +++ b/xmtp_proto/src/gen/xmtp.identity.associations.rs @@ -5,7 +5,7 @@ #[derive(Clone, PartialEq, ::prost::Message)] pub struct RecoverableEcdsaSignature { /// 65-bytes \[ R || S || V \], with recovery id as the last byte - #[prost(bytes="vec", tag="1")] + #[prost(bytes = "vec", tag = "1")] pub bytes: ::prost::alloc::vec::Vec, } /// EdDSA signature for 25519 @@ -13,10 +13,10 @@ pub struct RecoverableEcdsaSignature { #[derive(Clone, PartialEq, ::prost::Message)] pub struct RecoverableEd25519Signature { /// 64 bytes \[R(32 bytes) || S(32 bytes)\] - #[prost(bytes="vec", tag="1")] + #[prost(bytes = "vec", tag = "1")] pub bytes: ::prost::alloc::vec::Vec, /// 32 bytes - #[prost(bytes="vec", tag="2")] + #[prost(bytes = "vec", tag = "2")] pub public_key: ::prost::alloc::vec::Vec, } /// Smart Contract Wallet signature @@ -25,16 +25,16 @@ pub struct RecoverableEd25519Signature { pub struct SmartContractWalletSignature { /// CAIP-10 string /// - #[prost(string, tag="1")] + #[prost(string, tag = "1")] pub account_id: ::prost::alloc::string::String, /// Specify the block number to verify the signature against - #[prost(uint64, tag="2")] + #[prost(uint64, tag = "2")] pub block_number: u64, /// The actual signature bytes - #[prost(bytes="vec", tag="3")] + #[prost(bytes = "vec", tag = "3")] pub signature: ::prost::alloc::vec::Vec, /// The RPC URL specifies a chain to verify the signature against - #[prost(string, tag="4")] + #[prost(string, tag = "4")] pub chain_rpc_url: ::prost::alloc::string::String, } /// An existing address on xmtpv2 may have already signed a legacy identity key @@ -46,9 +46,9 @@ pub struct SmartContractWalletSignature { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct LegacyDelegatedSignature { - #[prost(message, optional, tag="1")] + #[prost(message, optional, tag = "1")] pub delegated_key: ::core::option::Option, - #[prost(message, optional, tag="2")] + #[prost(message, optional, tag = "2")] pub signature: ::core::option::Option, } /// A wrapper for all possible signature types @@ -60,7 +60,7 @@ pub struct Signature { /// recoverable, or specified as a field. /// 2. The signer certifies that the signing payload is correct. The payload /// must be inferred from the context in which the signature is provided. - #[prost(oneof="signature::Signature", tags="1, 2, 3, 4")] + #[prost(oneof = "signature::Signature", tags = "1, 2, 3, 4")] pub signature: ::core::option::Option, } /// Nested message and enum types in `Signature`. @@ -71,15 +71,15 @@ pub mod signature { /// 2. The signer certifies that the signing payload is correct. The payload /// must be inferred from the context in which the signature is provided. #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq, ::prost::Oneof)] pub enum Signature { - #[prost(message, tag="1")] + #[prost(message, tag = "1")] Erc191(super::RecoverableEcdsaSignature), - #[prost(message, tag="2")] + #[prost(message, tag = "2")] Erc6492(super::SmartContractWalletSignature), - #[prost(message, tag="3")] + #[prost(message, tag = "3")] InstallationKey(super::RecoverableEd25519Signature), - #[prost(message, tag="4")] + #[prost(message, tag = "4")] DelegatedErc191(super::LegacyDelegatedSignature), } } @@ -87,17 +87,17 @@ pub mod signature { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MemberIdentifier { - #[prost(oneof="member_identifier::Kind", tags="1, 2")] + #[prost(oneof = "member_identifier::Kind", tags = "1, 2")] pub kind: ::core::option::Option, } /// Nested message and enum types in `MemberIdentifier`. pub mod member_identifier { #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq, ::prost::Oneof)] pub enum Kind { - #[prost(string, tag="1")] + #[prost(string, tag = "1")] Address(::prost::alloc::string::String), - #[prost(bytes, tag="2")] + #[prost(bytes, tag = "2")] InstallationPublicKey(::prost::alloc::vec::Vec), } } @@ -105,10 +105,12 @@ pub mod member_identifier { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Member { - #[prost(message, optional, tag="1")] + #[prost(message, optional, tag = "1")] pub identifier: ::core::option::Option, - #[prost(message, optional, tag="2")] + #[prost(message, optional, tag = "2")] pub added_by_entity: ::core::option::Option, + #[prost(uint64, optional, tag = "3")] + pub server_timestamp_ns: ::core::option::Option, } /// The first entry of any XID log. The XID must be deterministically derivable /// from the address and nonce. @@ -117,12 +119,12 @@ pub struct Member { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CreateInbox { - #[prost(string, tag="1")] + #[prost(string, tag = "1")] pub initial_address: ::prost::alloc::string::String, - #[prost(uint64, tag="2")] + #[prost(uint64, tag = "2")] pub nonce: u64, /// Must be an addressable member - #[prost(message, optional, tag="3")] + #[prost(message, optional, tag = "3")] pub initial_address_signature: ::core::option::Option, } /// Adds a new member for an XID - either an addressable member such as a @@ -132,20 +134,20 @@ pub struct CreateInbox { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct AddAssociation { - #[prost(message, optional, tag="1")] + #[prost(message, optional, tag = "1")] pub new_member_identifier: ::core::option::Option, - #[prost(message, optional, tag="2")] + #[prost(message, optional, tag = "2")] pub existing_member_signature: ::core::option::Option, - #[prost(message, optional, tag="3")] + #[prost(message, optional, tag = "3")] pub new_member_signature: ::core::option::Option, } /// Revokes a member from an XID. The recovery address must sign the revocation. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct RevokeAssociation { - #[prost(message, optional, tag="1")] + #[prost(message, optional, tag = "1")] pub member_to_revoke: ::core::option::Option, - #[prost(message, optional, tag="2")] + #[prost(message, optional, tag = "2")] pub recovery_address_signature: ::core::option::Option, } /// Changes the recovery address for an XID. The recovery address is not required @@ -154,30 +156,30 @@ pub struct RevokeAssociation { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ChangeRecoveryAddress { - #[prost(string, tag="1")] + #[prost(string, tag = "1")] pub new_recovery_address: ::prost::alloc::string::String, - #[prost(message, optional, tag="2")] + #[prost(message, optional, tag = "2")] pub existing_recovery_address_signature: ::core::option::Option, } /// A single identity operation #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct IdentityAction { - #[prost(oneof="identity_action::Kind", tags="1, 2, 3, 4")] + #[prost(oneof = "identity_action::Kind", tags = "1, 2, 3, 4")] pub kind: ::core::option::Option, } /// Nested message and enum types in `IdentityAction`. pub mod identity_action { #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq, ::prost::Oneof)] pub enum Kind { - #[prost(message, tag="1")] + #[prost(message, tag = "1")] CreateInbox(super::CreateInbox), - #[prost(message, tag="2")] + #[prost(message, tag = "2")] Add(super::AddAssociation), - #[prost(message, tag="3")] + #[prost(message, tag = "3")] Revoke(super::RevokeAssociation), - #[prost(message, tag="4")] + #[prost(message, tag = "4")] ChangeRecoveryAddress(super::ChangeRecoveryAddress), } } @@ -191,42 +193,42 @@ pub mod identity_action { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct IdentityUpdate { - #[prost(message, repeated, tag="1")] + #[prost(message, repeated, tag = "1")] pub actions: ::prost::alloc::vec::Vec, - #[prost(uint64, tag="2")] + #[prost(uint64, tag = "2")] pub client_timestamp_ns: u64, - #[prost(string, tag="3")] + #[prost(string, tag = "3")] pub inbox_id: ::prost::alloc::string::String, } /// Map of members belonging to an inbox_id #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MemberMap { - #[prost(message, optional, tag="1")] + #[prost(message, optional, tag = "1")] pub key: ::core::option::Option, - #[prost(message, optional, tag="2")] + #[prost(message, optional, tag = "2")] pub value: ::core::option::Option, } /// A final association state resulting from multiple `IdentityUpdates` #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct AssociationState { - #[prost(string, tag="1")] + #[prost(string, tag = "1")] pub inbox_id: ::prost::alloc::string::String, - #[prost(message, repeated, tag="2")] + #[prost(message, repeated, tag = "2")] pub members: ::prost::alloc::vec::Vec, - #[prost(string, tag="3")] + #[prost(string, tag = "3")] pub recovery_address: ::prost::alloc::string::String, - #[prost(bytes="vec", repeated, tag="4")] + #[prost(bytes = "vec", repeated, tag = "4")] pub seen_signatures: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, } /// / state diff between two final AssociationStates #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct AssociationStateDiff { - #[prost(message, repeated, tag="1")] + #[prost(message, repeated, tag = "1")] pub new_members: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="2")] + #[prost(message, repeated, tag = "2")] pub removed_members: ::prost::alloc::vec::Vec, } /// Encoded file descriptor set for the `xmtp.identity.associations` package @@ -445,7 +447,7 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[ 0x33, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x03, 0x06, 0x12, 0x03, 0x3b, 0x04, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x03, 0x01, 0x12, 0x03, 0x3b, 0x1d, 0x2e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x03, 0x03, 0x12, 0x03, 0x3b, 0x31, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, 0x0a, 0x88, 0x31, 0x0a, 0x27, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x74, 0x6f, 0x33, 0x0a, 0x9a, 0x32, 0x0a, 0x27, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1a, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, @@ -459,7 +461,7 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[ 0x6f, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x15, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x06, 0x0a, 0x04, - 0x6b, 0x69, 0x6e, 0x64, 0x22, 0xc5, 0x01, 0x0a, 0x06, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, + 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x92, 0x02, 0x0a, 0x06, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x4c, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, @@ -470,374 +472,383 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[ 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x64, 0x64, 0x65, 0x64, 0x42, 0x79, 0x45, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x61, 0x64, 0x64, - 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x22, 0xaf, 0x01, 0x0a, - 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x12, 0x27, 0x0a, 0x0f, - 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x61, 0x0a, 0x19, 0x69, - 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x13, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x11, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, + 0x10, 0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x22, 0xaf, 0x01, 0x0a, 0x0b, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x61, 0x0a, 0x19, 0x69, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, + 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, + 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x52, 0x17, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xae, 0x02, 0x0a, 0x0e, + 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x60, + 0x0a, 0x15, 0x6e, 0x65, 0x77, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, + 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, + 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x13, 0x6e, 0x65, 0x77, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x12, 0x61, 0x0a, 0x19, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x17, 0x65, 0x78, 0x69, 0x73, + 0x74, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x12, 0x57, 0x0a, 0x14, 0x6e, 0x65, 0x77, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x12, 0x6e, 0x65, 0x77, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xd0, 0x01, 0x0a, + 0x11, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x56, 0x0a, 0x10, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x5f, + 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, + 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x6d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x54, 0x6f, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x63, 0x0a, 0x1a, 0x72, 0x65, + 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x17, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xae, - 0x02, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x60, 0x0a, 0x15, 0x6e, 0x65, 0x77, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, - 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x13, - 0x6e, 0x65, 0x77, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x12, 0x61, 0x0a, 0x19, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, - 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x17, 0x65, - 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x57, 0x0a, 0x14, 0x6e, 0x65, 0x77, 0x5f, 0x6d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x18, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, + 0xbf, 0x01, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, + 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x65, 0x77, + 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6e, 0x65, 0x77, 0x52, 0x65, 0x63, 0x6f, + 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x74, 0x0a, 0x23, 0x65, + 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, + 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, + 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x22, 0xdc, 0x02, 0x0a, 0x0e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4c, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x69, + 0x6e, 0x62, 0x6f, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, + 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, + 0x62, 0x6f, 0x78, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, + 0x6f, 0x78, 0x12, 0x3e, 0x0a, 0x03, 0x61, 0x64, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, + 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x64, 0x64, + 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x03, 0x61, + 0x64, 0x64, 0x12, 0x47, 0x0a, 0x06, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x6b, 0x0a, 0x17, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, + 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, + 0x00, 0x52, 0x15, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, + 0x22, 0xa1, 0x01, 0x0a, 0x0e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x12, 0x44, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x12, 0x6e, 0x65, 0x77, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, - 0xd0, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x56, 0x0a, 0x10, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, - 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x73, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x62, + 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x6e, 0x62, + 0x6f, 0x78, 0x49, 0x64, 0x22, 0x85, 0x01, 0x0a, 0x09, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4d, + 0x61, 0x70, 0x12, 0x3e, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x6d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x54, 0x6f, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x63, 0x0a, - 0x1a, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x18, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, - 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x22, 0xbf, 0x01, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, - 0x6f, 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x30, 0x0a, 0x14, - 0x6e, 0x65, 0x77, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6e, 0x65, 0x77, 0x52, - 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x74, - 0x0a, 0x23, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, - 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, - 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, - 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x52, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x63, 0x6f, - 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x22, 0xdc, 0x02, 0x0a, 0x0e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4c, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, + 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x38, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xc2, 0x01, 0x0a, + 0x10, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x12, 0x3f, 0x0a, 0x07, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, - 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x12, 0x3e, 0x0a, 0x03, 0x61, 0x64, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, - 0x52, 0x03, 0x61, 0x64, 0x64, 0x12, 0x47, 0x0a, 0x06, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x6b, - 0x0a, 0x17, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, - 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x31, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, - 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x48, 0x00, 0x52, 0x15, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x6f, - 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x6b, - 0x69, 0x6e, 0x64, 0x22, 0xa1, 0x01, 0x0a, 0x0e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x44, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x13, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x19, 0x0a, 0x08, - 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x22, 0x85, 0x01, 0x0a, 0x09, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x3e, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x38, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, - 0xc2, 0x01, 0x0a, 0x10, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x12, - 0x3f, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, - 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, - 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x73, - 0x65, 0x65, 0x6e, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x04, - 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0e, 0x73, 0x65, 0x65, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x73, 0x22, 0xbc, 0x01, 0x0a, 0x14, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x66, 0x66, 0x12, 0x4d, 0x0a, - 0x0b, 0x6e, 0x65, 0x77, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x4d, 0x61, 0x70, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x29, 0x0a, + 0x10, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x65, 0x65, 0x6e, + 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x0e, 0x73, 0x65, 0x65, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x22, 0xbc, 0x01, 0x0a, 0x14, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x66, 0x66, 0x12, 0x4d, 0x0a, 0x0b, 0x6e, 0x65, + 0x77, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, + 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0a, 0x6e, + 0x65, 0x77, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x0f, 0x72, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, - 0x52, 0x0a, 0x6e, 0x65, 0x77, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x0f, - 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x73, 0x42, 0xef, 0x01, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, - 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x10, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x2f, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xa2, 0x02, 0x03, - 0x58, 0x49, 0x41, 0xaa, 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x2e, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0xca, 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x5c, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xe2, 0x02, 0x26, + 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, + 0x42, 0xef, 0x01, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x42, 0x10, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, + 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x61, 0x73, + 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x49, 0x41, + 0xaa, 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x2e, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xca, 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5c, 0x41, 0x73, - 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1c, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x49, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x3a, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4a, 0xde, 0x1c, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x63, 0x01, - 0x0a, 0x3b, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x31, 0x20, 0x50, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, - 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, - 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x08, 0x0a, - 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, 0x23, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x05, - 0x00, 0x2f, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x07, 0x00, 0x48, 0x0a, 0x09, 0x0a, 0x02, - 0x08, 0x0b, 0x12, 0x03, 0x07, 0x00, 0x48, 0x0a, 0x33, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x0b, - 0x00, 0x10, 0x01, 0x1a, 0x27, 0x20, 0x54, 0x68, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, - 0x04, 0x00, 0x01, 0x12, 0x03, 0x0b, 0x08, 0x18, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x00, 0x08, 0x00, - 0x12, 0x04, 0x0c, 0x02, 0x0f, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x08, 0x00, 0x01, 0x12, - 0x03, 0x0c, 0x08, 0x0c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0d, 0x04, - 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x0d, 0x04, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0d, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0d, 0x15, 0x16, 0x0a, 0x0b, 0x0a, 0x04, 0x04, - 0x00, 0x02, 0x01, 0x12, 0x03, 0x0e, 0x04, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, - 0x05, 0x12, 0x03, 0x0e, 0x04, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, - 0x03, 0x0e, 0x0a, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x0e, - 0x24, 0x25, 0x0a, 0x50, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x13, 0x00, 0x16, 0x01, 0x1a, 0x44, - 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x74, - 0x68, 0x61, 0x74, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x69, - 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x74, - 0x68, 0x65, 0x6d, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x13, 0x08, 0x0e, - 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x14, 0x02, 0x22, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, 0x14, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x14, 0x13, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, - 0x00, 0x03, 0x12, 0x03, 0x14, 0x20, 0x21, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, 0x12, - 0x03, 0x15, 0x02, 0x30, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x04, 0x12, 0x03, 0x15, - 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x06, 0x12, 0x03, 0x15, 0x0b, 0x1b, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x15, 0x1c, 0x2b, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x15, 0x2e, 0x2f, 0x0a, 0xf8, 0x01, 0x0a, - 0x02, 0x04, 0x02, 0x12, 0x04, 0x1c, 0x00, 0x20, 0x01, 0x1a, 0xeb, 0x01, 0x20, 0x54, 0x68, 0x65, - 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x6f, 0x66, 0x20, - 0x61, 0x6e, 0x79, 0x20, 0x58, 0x49, 0x44, 0x20, 0x6c, 0x6f, 0x67, 0x2e, 0x20, 0x54, 0x68, 0x65, - 0x20, 0x58, 0x49, 0x44, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x64, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x64, - 0x65, 0x72, 0x69, 0x76, 0x61, 0x62, 0x6c, 0x65, 0x0a, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6e, - 0x6f, 0x6e, 0x63, 0x65, 0x2e, 0x0a, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x6f, 0x76, - 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x64, 0x65, 0x66, 0x61, - 0x75, 0x6c, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x69, 0x74, - 0x69, 0x61, 0x6c, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x75, 0x6e, 0x6c, 0x65, 0x73, 0x73, 0x0a, 0x20, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x73, 0x75, 0x62, 0x73, 0x65, 0x71, - 0x75, 0x65, 0x6e, 0x74, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x76, - 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x6c, 0x6f, 0x67, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, - 0x1c, 0x08, 0x13, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x1d, 0x02, 0x1d, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x05, 0x12, 0x03, 0x1d, 0x02, 0x08, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1d, 0x09, 0x18, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1d, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, - 0x02, 0x01, 0x12, 0x03, 0x1e, 0x02, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x05, - 0x12, 0x03, 0x1e, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x01, 0x12, 0x03, - 0x1e, 0x09, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, 0x03, 0x1e, 0x11, - 0x12, 0x0a, 0x2c, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x02, 0x12, 0x03, 0x1f, 0x02, 0x2a, 0x22, 0x1f, - 0x20, 0x4d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x06, 0x12, 0x03, 0x1f, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x02, 0x02, 0x02, 0x01, 0x12, 0x03, 0x1f, 0x0c, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x02, 0x02, 0x02, 0x03, 0x12, 0x03, 0x1f, 0x28, 0x29, 0x0a, 0x82, 0x02, 0x0a, 0x02, 0x04, 0x03, - 0x12, 0x04, 0x26, 0x00, 0x2a, 0x01, 0x1a, 0xf5, 0x01, 0x20, 0x41, 0x64, 0x64, 0x73, 0x20, 0x61, - 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, - 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x20, 0x2d, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, - 0x61, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x73, 0x75, 0x63, 0x68, 0x20, 0x61, 0x73, 0x20, 0x61, 0x0a, - 0x20, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x61, 0x6e, 0x20, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x63, 0x74, 0x69, - 0x6e, 0x67, 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x65, 0x68, 0x61, 0x6c, 0x66, 0x20, 0x6f, 0x66, 0x20, - 0x61, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x0a, 0x20, 0x41, 0x20, 0x6b, - 0x65, 0x79, 0x2d, 0x70, 0x61, 0x69, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x73, - 0x20, 0x62, 0x65, 0x65, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, - 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x20, 0x4d, - 0x55, 0x53, 0x54, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, - 0x74, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x0a, 0x20, 0x61, 0x73, 0x73, 0x6f, - 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, 0x64, 0x69, - 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x2e, 0x0a, 0x0a, 0x0a, - 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x03, 0x26, 0x08, 0x16, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, - 0x02, 0x00, 0x12, 0x03, 0x27, 0x02, 0x2d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x06, - 0x12, 0x03, 0x27, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x01, 0x12, 0x03, - 0x27, 0x13, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, 0x27, 0x2b, - 0x2c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x01, 0x12, 0x03, 0x28, 0x02, 0x2a, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x06, 0x12, 0x03, 0x28, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x03, 0x02, 0x01, 0x01, 0x12, 0x03, 0x28, 0x0c, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, - 0x02, 0x01, 0x03, 0x12, 0x03, 0x28, 0x28, 0x29, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x02, - 0x12, 0x03, 0x29, 0x02, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x06, 0x12, 0x03, - 0x29, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x01, 0x12, 0x03, 0x29, 0x0c, - 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x03, 0x12, 0x03, 0x29, 0x23, 0x24, 0x0a, - 0x5a, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x2d, 0x00, 0x30, 0x01, 0x1a, 0x4e, 0x20, 0x52, 0x65, - 0x76, 0x6f, 0x6b, 0x65, 0x73, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x66, - 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, - 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, - 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, - 0x04, 0x01, 0x12, 0x03, 0x2d, 0x08, 0x19, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, - 0x03, 0x2e, 0x02, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x06, 0x12, 0x03, 0x2e, - 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2e, 0x13, 0x23, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2e, 0x26, 0x27, 0x0a, 0x0b, - 0x0a, 0x04, 0x04, 0x04, 0x02, 0x01, 0x12, 0x03, 0x2f, 0x02, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x04, 0x02, 0x01, 0x06, 0x12, 0x03, 0x2f, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, - 0x01, 0x01, 0x12, 0x03, 0x2f, 0x0c, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x03, - 0x12, 0x03, 0x2f, 0x29, 0x2a, 0x0a, 0xd1, 0x01, 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04, 0x35, 0x00, - 0x38, 0x01, 0x1a, 0xc4, 0x01, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x74, 0x68, + 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xe2, 0x02, 0x26, 0x58, 0x6d, 0x74, + 0x70, 0x5c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5c, 0x41, 0x73, 0x73, 0x6f, 0x63, + 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0xea, 0x02, 0x1c, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x49, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x3a, 0x3a, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x4a, 0xa3, 0x1d, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x64, 0x01, 0x0a, 0x3b, 0x0a, + 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x31, 0x20, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x61, 0x73, 0x73, + 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, + 0x03, 0x03, 0x00, 0x23, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x05, 0x00, 0x2f, 0x0a, + 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x07, 0x00, 0x48, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, + 0x03, 0x07, 0x00, 0x48, 0x0a, 0x33, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x0b, 0x00, 0x10, 0x01, + 0x1a, 0x27, 0x20, 0x54, 0x68, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, + 0x66, 0x20, 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, + 0x12, 0x03, 0x0b, 0x08, 0x18, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x00, 0x08, 0x00, 0x12, 0x04, 0x0c, + 0x02, 0x0f, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x08, 0x00, 0x01, 0x12, 0x03, 0x0c, 0x08, + 0x0c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0d, 0x04, 0x17, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x0d, 0x04, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0d, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, + 0x02, 0x00, 0x03, 0x12, 0x03, 0x0d, 0x15, 0x16, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, + 0x12, 0x03, 0x0e, 0x04, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, + 0x0e, 0x04, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x0e, 0x0a, + 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x0e, 0x24, 0x25, 0x0a, + 0x50, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x13, 0x00, 0x17, 0x01, 0x1a, 0x44, 0x20, 0x73, 0x69, + 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, + 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x69, 0x6e, 0x64, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6d, + 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x13, 0x08, 0x0e, 0x0a, 0x0b, 0x0a, + 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x14, 0x02, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, + 0x02, 0x00, 0x06, 0x12, 0x03, 0x14, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, + 0x01, 0x12, 0x03, 0x14, 0x13, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, + 0x03, 0x14, 0x20, 0x21, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, 0x12, 0x03, 0x15, 0x02, + 0x30, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x04, 0x12, 0x03, 0x15, 0x02, 0x0a, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x06, 0x12, 0x03, 0x15, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x15, 0x1c, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x15, 0x2e, 0x2f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, + 0x02, 0x12, 0x03, 0x16, 0x02, 0x2a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x04, 0x12, + 0x03, 0x16, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x05, 0x12, 0x03, 0x16, + 0x0b, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x01, 0x12, 0x03, 0x16, 0x12, 0x25, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x03, 0x12, 0x03, 0x16, 0x28, 0x29, 0x0a, 0xf8, + 0x01, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x1d, 0x00, 0x21, 0x01, 0x1a, 0xeb, 0x01, 0x20, 0x54, + 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x6f, + 0x66, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x58, 0x49, 0x44, 0x20, 0x6c, 0x6f, 0x67, 0x2e, 0x20, 0x54, + 0x68, 0x65, 0x20, 0x58, 0x49, 0x44, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x64, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, + 0x20, 0x64, 0x65, 0x72, 0x69, 0x76, 0x61, 0x62, 0x6c, 0x65, 0x0a, 0x20, 0x66, 0x72, 0x6f, 0x6d, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x2e, 0x0a, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, + 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x75, 0x6e, 0x6c, 0x65, 0x73, 0x73, 0x0a, + 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x73, 0x75, 0x62, 0x73, + 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x69, 0x6e, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x6c, 0x6f, 0x67, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, + 0x12, 0x03, 0x1d, 0x08, 0x13, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x1e, + 0x02, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x05, 0x12, 0x03, 0x1e, 0x02, 0x08, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1e, 0x09, 0x18, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1e, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x02, 0x02, 0x01, 0x12, 0x03, 0x1f, 0x02, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, + 0x01, 0x05, 0x12, 0x03, 0x1f, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x01, + 0x12, 0x03, 0x1f, 0x09, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, 0x03, + 0x1f, 0x11, 0x12, 0x0a, 0x2c, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x02, 0x12, 0x03, 0x20, 0x02, 0x2a, + 0x22, 0x1f, 0x20, 0x4d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x06, 0x12, 0x03, 0x20, 0x02, 0x0b, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x01, 0x12, 0x03, 0x20, 0x0c, 0x25, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x02, 0x02, 0x02, 0x03, 0x12, 0x03, 0x20, 0x28, 0x29, 0x0a, 0x82, 0x02, 0x0a, 0x02, + 0x04, 0x03, 0x12, 0x04, 0x27, 0x00, 0x2b, 0x01, 0x1a, 0xf5, 0x01, 0x20, 0x41, 0x64, 0x64, 0x73, + 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x20, 0x2d, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, + 0x72, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x73, 0x75, 0x63, 0x68, 0x20, 0x61, 0x73, 0x20, + 0x61, 0x0a, 0x20, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x61, 0x6e, + 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x63, + 0x74, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x65, 0x68, 0x61, 0x6c, 0x66, 0x20, 0x6f, + 0x66, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x0a, 0x20, 0x41, + 0x20, 0x6b, 0x65, 0x79, 0x2d, 0x70, 0x61, 0x69, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, + 0x61, 0x73, 0x20, 0x62, 0x65, 0x65, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, + 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x72, 0x6f, 0x6c, 0x65, + 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x70, 0x65, 0x72, + 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x0a, 0x20, 0x61, 0x73, + 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, + 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x2e, 0x0a, + 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x03, 0x27, 0x08, 0x16, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x03, 0x02, 0x00, 0x12, 0x03, 0x28, 0x02, 0x2d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, + 0x00, 0x06, 0x12, 0x03, 0x28, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x01, + 0x12, 0x03, 0x28, 0x13, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, + 0x28, 0x2b, 0x2c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x01, 0x12, 0x03, 0x29, 0x02, 0x2a, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x06, 0x12, 0x03, 0x29, 0x02, 0x0b, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x12, 0x03, 0x29, 0x0c, 0x25, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x03, 0x02, 0x01, 0x03, 0x12, 0x03, 0x29, 0x28, 0x29, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, + 0x02, 0x02, 0x12, 0x03, 0x2a, 0x02, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x06, + 0x12, 0x03, 0x2a, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x01, 0x12, 0x03, + 0x2a, 0x0c, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x03, 0x12, 0x03, 0x2a, 0x23, + 0x24, 0x0a, 0x5a, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x2e, 0x00, 0x31, 0x01, 0x1a, 0x4e, 0x20, + 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x73, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x2e, 0x20, 0x54, - 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x64, 0x0a, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x58, 0x49, 0x44, 0x2e, 0x20, - 0x49, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x62, - 0x65, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x64, 0x64, - 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x72, - 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, - 0x63, 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x20, - 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x05, 0x01, - 0x12, 0x03, 0x35, 0x08, 0x1d, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, 0x12, 0x03, 0x36, - 0x02, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x05, 0x12, 0x03, 0x36, 0x02, 0x08, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x01, 0x12, 0x03, 0x36, 0x09, 0x1d, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x03, 0x12, 0x03, 0x36, 0x20, 0x21, 0x0a, 0x0b, 0x0a, 0x04, - 0x04, 0x05, 0x02, 0x01, 0x12, 0x03, 0x37, 0x02, 0x34, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, - 0x01, 0x06, 0x12, 0x03, 0x37, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x01, - 0x12, 0x03, 0x37, 0x0c, 0x2f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x03, 0x12, 0x03, - 0x37, 0x32, 0x33, 0x0a, 0x29, 0x0a, 0x02, 0x04, 0x06, 0x12, 0x04, 0x3b, 0x00, 0x42, 0x01, 0x1a, - 0x1d, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x0a, 0x0a, - 0x0a, 0x03, 0x04, 0x06, 0x01, 0x12, 0x03, 0x3b, 0x08, 0x16, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x06, - 0x08, 0x00, 0x12, 0x04, 0x3c, 0x02, 0x41, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x08, 0x00, - 0x01, 0x12, 0x03, 0x3c, 0x08, 0x0c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x00, 0x12, 0x03, - 0x3d, 0x04, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x06, 0x12, 0x03, 0x3d, 0x04, - 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x01, 0x12, 0x03, 0x3d, 0x10, 0x1c, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x03, 0x12, 0x03, 0x3d, 0x1f, 0x20, 0x0a, 0x0b, 0x0a, - 0x04, 0x04, 0x06, 0x02, 0x01, 0x12, 0x03, 0x3e, 0x04, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, - 0x02, 0x01, 0x06, 0x12, 0x03, 0x3e, 0x04, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x01, - 0x01, 0x12, 0x03, 0x3e, 0x13, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x01, 0x03, 0x12, - 0x03, 0x3e, 0x19, 0x1a, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x02, 0x12, 0x03, 0x3f, 0x04, - 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x02, 0x06, 0x12, 0x03, 0x3f, 0x04, 0x15, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x02, 0x01, 0x12, 0x03, 0x3f, 0x16, 0x1c, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x06, 0x02, 0x02, 0x03, 0x12, 0x03, 0x3f, 0x1f, 0x20, 0x0a, 0x0b, 0x0a, 0x04, 0x04, - 0x06, 0x02, 0x03, 0x12, 0x03, 0x40, 0x04, 0x36, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x03, - 0x06, 0x12, 0x03, 0x40, 0x04, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x03, 0x01, 0x12, - 0x03, 0x40, 0x1a, 0x31, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x03, 0x03, 0x12, 0x03, 0x40, - 0x34, 0x35, 0x0a, 0xd5, 0x03, 0x0a, 0x02, 0x04, 0x07, 0x12, 0x04, 0x4b, 0x00, 0x4f, 0x01, 0x1a, - 0xc8, 0x03, 0x20, 0x4f, 0x6e, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x69, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x65, 0x72, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, - 0x20, 0x74, 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x0a, 0x20, 0x45, 0x78, 0x61, 0x6d, - 0x70, 0x6c, 0x65, 0x3a, 0x20, 0x5b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x58, 0x69, 0x64, 0x2c, - 0x20, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, - 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5d, 0x0a, 0x20, 0x31, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, - 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x69, 0x73, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x64, 0x20, 0x62, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x6e, 0x61, 0x74, 0x69, - 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x20, 0x74, 0x65, 0x78, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x66, 0x20, 0x65, 0x61, 0x63, - 0x68, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, - 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, 0x73, 0x65, - 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x27, 0x5c, 0x6e, 0x5c, 0x6e, 0x5c, 0x6e, - 0x27, 0x2e, 0x0a, 0x20, 0x32, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, - 0x73, 0x69, 0x67, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, - 0x74, 0x65, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x0a, - 0x20, 0x33, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e, - 0x67, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, - 0x64, 0x64, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x61, 0x73, 0x73, - 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x20, 0x77, - 0x68, 0x65, 0x72, 0x65, 0x20, 0x72, 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x74, 0x2e, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, - 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x61, - 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x07, - 0x01, 0x12, 0x03, 0x4b, 0x08, 0x16, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x00, 0x12, 0x03, - 0x4c, 0x02, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x04, 0x12, 0x03, 0x4c, 0x02, - 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x06, 0x12, 0x03, 0x4c, 0x0b, 0x19, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x01, 0x12, 0x03, 0x4c, 0x1a, 0x21, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x07, 0x02, 0x00, 0x03, 0x12, 0x03, 0x4c, 0x24, 0x25, 0x0a, 0x0b, 0x0a, 0x04, 0x04, - 0x07, 0x02, 0x01, 0x12, 0x03, 0x4d, 0x02, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, - 0x05, 0x12, 0x03, 0x4d, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x01, 0x12, - 0x03, 0x4d, 0x09, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x03, 0x12, 0x03, 0x4d, - 0x1f, 0x20, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x02, 0x12, 0x03, 0x4e, 0x02, 0x16, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x02, 0x05, 0x12, 0x03, 0x4e, 0x02, 0x08, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x07, 0x02, 0x02, 0x01, 0x12, 0x03, 0x4e, 0x09, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x07, 0x02, 0x02, 0x03, 0x12, 0x03, 0x4e, 0x14, 0x15, 0x0a, 0x35, 0x0a, 0x02, 0x04, 0x08, 0x12, - 0x04, 0x52, 0x00, 0x55, 0x01, 0x1a, 0x29, 0x20, 0x4d, 0x61, 0x70, 0x20, 0x6f, 0x66, 0x20, 0x6d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, 0x62, 0x65, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x6e, 0x67, - 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x0a, - 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x08, 0x01, 0x12, 0x03, 0x52, 0x08, 0x11, 0x0a, 0x0b, 0x0a, 0x04, - 0x04, 0x08, 0x02, 0x00, 0x12, 0x03, 0x53, 0x02, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, - 0x00, 0x06, 0x12, 0x03, 0x53, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x01, - 0x12, 0x03, 0x53, 0x13, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x03, 0x12, 0x03, - 0x53, 0x19, 0x1a, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x08, 0x02, 0x01, 0x12, 0x03, 0x54, 0x02, 0x13, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x01, 0x06, 0x12, 0x03, 0x54, 0x02, 0x08, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x08, 0x02, 0x01, 0x01, 0x12, 0x03, 0x54, 0x09, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x08, 0x02, 0x01, 0x03, 0x12, 0x03, 0x54, 0x11, 0x12, 0x0a, 0x51, 0x0a, 0x02, 0x04, 0x09, - 0x12, 0x04, 0x58, 0x00, 0x5d, 0x01, 0x1a, 0x45, 0x20, 0x41, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, - 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x66, 0x72, 0x6f, - 0x6d, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x60, 0x49, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x60, 0x0a, 0x0a, 0x0a, 0x0a, - 0x03, 0x04, 0x09, 0x01, 0x12, 0x03, 0x58, 0x08, 0x18, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x09, 0x02, - 0x00, 0x12, 0x03, 0x59, 0x02, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x05, 0x12, - 0x03, 0x59, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x01, 0x12, 0x03, 0x59, - 0x09, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x03, 0x12, 0x03, 0x59, 0x14, 0x15, - 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x09, 0x02, 0x01, 0x12, 0x03, 0x5a, 0x02, 0x21, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x09, 0x02, 0x01, 0x04, 0x12, 0x03, 0x5a, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x09, 0x02, 0x01, 0x06, 0x12, 0x03, 0x5a, 0x0b, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, - 0x01, 0x01, 0x12, 0x03, 0x5a, 0x15, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x01, 0x03, - 0x12, 0x03, 0x5a, 0x1f, 0x20, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x09, 0x02, 0x02, 0x12, 0x03, 0x5b, - 0x02, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x02, 0x05, 0x12, 0x03, 0x5b, 0x02, 0x08, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x02, 0x01, 0x12, 0x03, 0x5b, 0x09, 0x19, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x09, 0x02, 0x02, 0x03, 0x12, 0x03, 0x5b, 0x1c, 0x1d, 0x0a, 0x0b, 0x0a, 0x04, - 0x04, 0x09, 0x02, 0x03, 0x12, 0x03, 0x5c, 0x02, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, - 0x03, 0x04, 0x12, 0x03, 0x5c, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x03, 0x05, - 0x12, 0x03, 0x5c, 0x0b, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x03, 0x01, 0x12, 0x03, - 0x5c, 0x11, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x03, 0x03, 0x12, 0x03, 0x5c, 0x23, - 0x24, 0x0a, 0x3d, 0x0a, 0x02, 0x04, 0x0a, 0x12, 0x04, 0x60, 0x00, 0x63, 0x01, 0x1a, 0x31, 0x2f, - 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x64, 0x69, 0x66, 0x66, 0x20, 0x62, 0x65, 0x74, 0x77, - 0x65, 0x65, 0x6e, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x41, 0x73, - 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x0a, - 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x0a, 0x01, 0x12, 0x03, 0x60, 0x08, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, - 0x04, 0x0a, 0x02, 0x00, 0x12, 0x03, 0x61, 0x02, 0x2c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, - 0x00, 0x04, 0x12, 0x03, 0x61, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x06, - 0x12, 0x03, 0x61, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x01, 0x12, 0x03, - 0x61, 0x1c, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x03, 0x12, 0x03, 0x61, 0x2a, - 0x2b, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0a, 0x02, 0x01, 0x12, 0x03, 0x62, 0x02, 0x30, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x01, 0x04, 0x12, 0x03, 0x62, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x0a, 0x02, 0x01, 0x06, 0x12, 0x03, 0x62, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, - 0x02, 0x01, 0x01, 0x12, 0x03, 0x62, 0x1c, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x01, - 0x03, 0x12, 0x03, 0x62, 0x2e, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x73, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x72, 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, + 0x03, 0x04, 0x04, 0x01, 0x12, 0x03, 0x2e, 0x08, 0x19, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, + 0x00, 0x12, 0x03, 0x2f, 0x02, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x06, 0x12, + 0x03, 0x2f, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2f, + 0x13, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2f, 0x26, 0x27, + 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x01, 0x12, 0x03, 0x30, 0x02, 0x2b, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x04, 0x02, 0x01, 0x06, 0x12, 0x03, 0x30, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x04, 0x02, 0x01, 0x01, 0x12, 0x03, 0x30, 0x0c, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, + 0x01, 0x03, 0x12, 0x03, 0x30, 0x29, 0x2a, 0x0a, 0xd1, 0x01, 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04, + 0x36, 0x00, 0x39, 0x01, 0x1a, 0xc4, 0x01, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x2e, + 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x6d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x58, 0x49, 0x44, + 0x2e, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, + 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, + 0x64, 0x64, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x0a, + 0x20, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x72, 0x65, 0x76, 0x6f, 0x6b, + 0x65, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, + 0x05, 0x01, 0x12, 0x03, 0x36, 0x08, 0x1d, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, 0x12, + 0x03, 0x37, 0x02, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x05, 0x12, 0x03, 0x37, + 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x01, 0x12, 0x03, 0x37, 0x09, 0x1d, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x03, 0x12, 0x03, 0x37, 0x20, 0x21, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x05, 0x02, 0x01, 0x12, 0x03, 0x38, 0x02, 0x34, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x05, 0x02, 0x01, 0x06, 0x12, 0x03, 0x38, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, + 0x01, 0x01, 0x12, 0x03, 0x38, 0x0c, 0x2f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x03, + 0x12, 0x03, 0x38, 0x32, 0x33, 0x0a, 0x29, 0x0a, 0x02, 0x04, 0x06, 0x12, 0x04, 0x3c, 0x00, 0x43, + 0x01, 0x1a, 0x1d, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, + 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x06, 0x01, 0x12, 0x03, 0x3c, 0x08, 0x16, 0x0a, 0x0c, 0x0a, 0x04, + 0x04, 0x06, 0x08, 0x00, 0x12, 0x04, 0x3d, 0x02, 0x42, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, + 0x08, 0x00, 0x01, 0x12, 0x03, 0x3d, 0x08, 0x0c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x00, + 0x12, 0x03, 0x3e, 0x04, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x06, 0x12, 0x03, + 0x3e, 0x04, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x01, 0x12, 0x03, 0x3e, 0x10, + 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x03, 0x12, 0x03, 0x3e, 0x1f, 0x20, 0x0a, + 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x01, 0x12, 0x03, 0x3f, 0x04, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x06, 0x02, 0x01, 0x06, 0x12, 0x03, 0x3f, 0x04, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, + 0x02, 0x01, 0x01, 0x12, 0x03, 0x3f, 0x13, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x01, + 0x03, 0x12, 0x03, 0x3f, 0x19, 0x1a, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x02, 0x12, 0x03, + 0x40, 0x04, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x02, 0x06, 0x12, 0x03, 0x40, 0x04, + 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x02, 0x01, 0x12, 0x03, 0x40, 0x16, 0x1c, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x02, 0x03, 0x12, 0x03, 0x40, 0x1f, 0x20, 0x0a, 0x0b, 0x0a, + 0x04, 0x04, 0x06, 0x02, 0x03, 0x12, 0x03, 0x41, 0x04, 0x36, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, + 0x02, 0x03, 0x06, 0x12, 0x03, 0x41, 0x04, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x03, + 0x01, 0x12, 0x03, 0x41, 0x1a, 0x31, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x03, 0x03, 0x12, + 0x03, 0x41, 0x34, 0x35, 0x0a, 0xd5, 0x03, 0x0a, 0x02, 0x04, 0x07, 0x12, 0x04, 0x4c, 0x00, 0x50, + 0x01, 0x1a, 0xc8, 0x03, 0x20, 0x4f, 0x6e, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, + 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x65, 0x72, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, + 0x65, 0x64, 0x20, 0x74, 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x0a, 0x20, 0x45, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x3a, 0x20, 0x5b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x58, 0x69, + 0x64, 0x2c, 0x20, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2c, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5d, 0x0a, 0x20, 0x31, 0x2e, 0x20, 0x54, 0x68, + 0x65, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x69, 0x73, 0x20, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x6e, 0x61, + 0x74, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x66, 0x20, 0x65, + 0x61, 0x63, 0x68, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x74, 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, + 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x27, 0x5c, 0x6e, 0x5c, 0x6e, + 0x5c, 0x6e, 0x27, 0x2e, 0x0a, 0x20, 0x32, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x75, 0x73, 0x65, + 0x72, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6e, + 0x63, 0x61, 0x74, 0x65, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x2e, 0x0a, 0x20, 0x33, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x69, 0x6e, 0x67, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x69, 0x73, + 0x20, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x61, + 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x72, 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x74, 0x2e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x75, + 0x73, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, + 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x69, 0x6e, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, + 0x04, 0x07, 0x01, 0x12, 0x03, 0x4c, 0x08, 0x16, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x00, + 0x12, 0x03, 0x4d, 0x02, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x04, 0x12, 0x03, + 0x4d, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x06, 0x12, 0x03, 0x4d, 0x0b, + 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x01, 0x12, 0x03, 0x4d, 0x1a, 0x21, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x03, 0x12, 0x03, 0x4d, 0x24, 0x25, 0x0a, 0x0b, 0x0a, + 0x04, 0x04, 0x07, 0x02, 0x01, 0x12, 0x03, 0x4e, 0x02, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, + 0x02, 0x01, 0x05, 0x12, 0x03, 0x4e, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, + 0x01, 0x12, 0x03, 0x4e, 0x09, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x03, 0x12, + 0x03, 0x4e, 0x1f, 0x20, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x02, 0x12, 0x03, 0x4f, 0x02, + 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x02, 0x05, 0x12, 0x03, 0x4f, 0x02, 0x08, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x02, 0x01, 0x12, 0x03, 0x4f, 0x09, 0x11, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x07, 0x02, 0x02, 0x03, 0x12, 0x03, 0x4f, 0x14, 0x15, 0x0a, 0x35, 0x0a, 0x02, 0x04, + 0x08, 0x12, 0x04, 0x53, 0x00, 0x56, 0x01, 0x1a, 0x29, 0x20, 0x4d, 0x61, 0x70, 0x20, 0x6f, 0x66, + 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, 0x62, 0x65, 0x6c, 0x6f, 0x6e, 0x67, 0x69, + 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, + 0x64, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x08, 0x01, 0x12, 0x03, 0x53, 0x08, 0x11, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x08, 0x02, 0x00, 0x12, 0x03, 0x54, 0x02, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x08, 0x02, 0x00, 0x06, 0x12, 0x03, 0x54, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, + 0x00, 0x01, 0x12, 0x03, 0x54, 0x13, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x03, + 0x12, 0x03, 0x54, 0x19, 0x1a, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x08, 0x02, 0x01, 0x12, 0x03, 0x55, + 0x02, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x01, 0x06, 0x12, 0x03, 0x55, 0x02, 0x08, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x01, 0x01, 0x12, 0x03, 0x55, 0x09, 0x0e, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x08, 0x02, 0x01, 0x03, 0x12, 0x03, 0x55, 0x11, 0x12, 0x0a, 0x51, 0x0a, 0x02, + 0x04, 0x09, 0x12, 0x04, 0x59, 0x00, 0x5e, 0x01, 0x1a, 0x45, 0x20, 0x41, 0x20, 0x66, 0x69, 0x6e, + 0x61, 0x6c, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x66, + 0x72, 0x6f, 0x6d, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x60, 0x49, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x60, 0x0a, 0x0a, + 0x0a, 0x0a, 0x03, 0x04, 0x09, 0x01, 0x12, 0x03, 0x59, 0x08, 0x18, 0x0a, 0x0b, 0x0a, 0x04, 0x04, + 0x09, 0x02, 0x00, 0x12, 0x03, 0x5a, 0x02, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, + 0x05, 0x12, 0x03, 0x5a, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x01, 0x12, + 0x03, 0x5a, 0x09, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x03, 0x12, 0x03, 0x5a, + 0x14, 0x15, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x09, 0x02, 0x01, 0x12, 0x03, 0x5b, 0x02, 0x21, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x01, 0x04, 0x12, 0x03, 0x5b, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x09, 0x02, 0x01, 0x06, 0x12, 0x03, 0x5b, 0x0b, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x09, 0x02, 0x01, 0x01, 0x12, 0x03, 0x5b, 0x15, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, + 0x01, 0x03, 0x12, 0x03, 0x5b, 0x1f, 0x20, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x09, 0x02, 0x02, 0x12, + 0x03, 0x5c, 0x02, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x02, 0x05, 0x12, 0x03, 0x5c, + 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x02, 0x01, 0x12, 0x03, 0x5c, 0x09, 0x19, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x02, 0x03, 0x12, 0x03, 0x5c, 0x1c, 0x1d, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x09, 0x02, 0x03, 0x12, 0x03, 0x5d, 0x02, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x09, 0x02, 0x03, 0x04, 0x12, 0x03, 0x5d, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, + 0x03, 0x05, 0x12, 0x03, 0x5d, 0x0b, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x03, 0x01, + 0x12, 0x03, 0x5d, 0x11, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x03, 0x03, 0x12, 0x03, + 0x5d, 0x23, 0x24, 0x0a, 0x3d, 0x0a, 0x02, 0x04, 0x0a, 0x12, 0x04, 0x61, 0x00, 0x64, 0x01, 0x1a, + 0x31, 0x2f, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x64, 0x69, 0x66, 0x66, 0x20, 0x62, 0x65, + 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x20, + 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x0a, 0x01, 0x12, 0x03, 0x61, 0x08, 0x1c, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x0a, 0x02, 0x00, 0x12, 0x03, 0x62, 0x02, 0x2c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x0a, 0x02, 0x00, 0x04, 0x12, 0x03, 0x62, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, + 0x00, 0x06, 0x12, 0x03, 0x62, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x01, + 0x12, 0x03, 0x62, 0x1c, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x03, 0x12, 0x03, + 0x62, 0x2a, 0x2b, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0a, 0x02, 0x01, 0x12, 0x03, 0x63, 0x02, 0x30, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x01, 0x04, 0x12, 0x03, 0x63, 0x02, 0x0a, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x01, 0x06, 0x12, 0x03, 0x63, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x0a, 0x02, 0x01, 0x01, 0x12, 0x03, 0x63, 0x1c, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, + 0x02, 0x01, 0x03, 0x12, 0x03, 0x63, 0x2e, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, ]; include!("xmtp.identity.associations.serde.rs"); -// @@protoc_insertion_point(module) \ No newline at end of file +// @@protoc_insertion_point(module) diff --git a/xmtp_proto/src/gen/xmtp.identity.associations.serde.rs b/xmtp_proto/src/gen/xmtp.identity.associations.serde.rs index 89ceafd87..ee29fad7c 100644 --- a/xmtp_proto/src/gen/xmtp.identity.associations.serde.rs +++ b/xmtp_proto/src/gen/xmtp.identity.associations.serde.rs @@ -1017,6 +1017,9 @@ impl serde::Serialize for Member { if self.added_by_entity.is_some() { len += 1; } + if self.server_timestamp_ns.is_some() { + len += 1; + } let mut struct_ser = serializer.serialize_struct("xmtp.identity.associations.Member", len)?; if let Some(v) = self.identifier.as_ref() { struct_ser.serialize_field("identifier", v)?; @@ -1024,6 +1027,11 @@ impl serde::Serialize for Member { if let Some(v) = self.added_by_entity.as_ref() { struct_ser.serialize_field("addedByEntity", v)?; } + if let Some(v) = self.server_timestamp_ns.as_ref() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("serverTimestampNs", ToString::to_string(&v).as_str())?; + } struct_ser.end() } } @@ -1037,12 +1045,15 @@ impl<'de> serde::Deserialize<'de> for Member { "identifier", "added_by_entity", "addedByEntity", + "server_timestamp_ns", + "serverTimestampNs", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { Identifier, AddedByEntity, + ServerTimestampNs, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -1066,6 +1077,7 @@ impl<'de> serde::Deserialize<'de> for Member { match value { "identifier" => Ok(GeneratedField::Identifier), "addedByEntity" | "added_by_entity" => Ok(GeneratedField::AddedByEntity), + "serverTimestampNs" | "server_timestamp_ns" => Ok(GeneratedField::ServerTimestampNs), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -1087,6 +1099,7 @@ impl<'de> serde::Deserialize<'de> for Member { { let mut identifier__ = None; let mut added_by_entity__ = None; + let mut server_timestamp_ns__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Identifier => { @@ -1101,11 +1114,20 @@ impl<'de> serde::Deserialize<'de> for Member { } added_by_entity__ = map_.next_value()?; } + GeneratedField::ServerTimestampNs => { + if server_timestamp_ns__.is_some() { + return Err(serde::de::Error::duplicate_field("serverTimestampNs")); + } + server_timestamp_ns__ = + map_.next_value::<::std::option::Option<::pbjson::private::NumberDeserialize<_>>>()?.map(|x| x.0) + ; + } } } Ok(Member { identifier: identifier__, added_by_entity: added_by_entity__, + server_timestamp_ns: server_timestamp_ns__, }) } } From dba453cbae452247a709513b8f3fd80ffa39f8bb Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Wed, 18 Sep 2024 10:31:02 -0400 Subject: [PATCH 02/17] gracefully handle cache deserialization issues --- xmtp_mls/src/identity_updates.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/xmtp_mls/src/identity_updates.rs b/xmtp_mls/src/identity_updates.rs index 74da80b4a..e9bb1b810 100644 --- a/xmtp_mls/src/identity_updates.rs +++ b/xmtp_mls/src/identity_updates.rs @@ -117,10 +117,18 @@ where return Err(AssociationError::MissingIdentityUpdate.into()); } - if let Some(association_state) = - StoredAssociationState::read_from_cache(conn, inbox_id.to_string(), last_sequence_id)? + match StoredAssociationState::read_from_cache(conn, inbox_id.to_string(), last_sequence_id) { - return Ok(association_state); + Ok(Some(state)) => { + return Ok(state); + } + // If the cache fails to deserialize, it's because the structure of the cache + // has changed, and we need to reload. + Err(err) => { + log::warn!("{err:?}"); + } + // No cache + _ => {} } let unverified_updates = updates From 45f14ec14d5dee5207886cff07701f1d6854af88 Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Wed, 18 Sep 2024 10:45:33 -0400 Subject: [PATCH 03/17] test --- xmtp_mls/src/identity_updates.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/xmtp_mls/src/identity_updates.rs b/xmtp_mls/src/identity_updates.rs index e9bb1b810..39fec44ba 100644 --- a/xmtp_mls/src/identity_updates.rs +++ b/xmtp_mls/src/identity_updates.rs @@ -493,6 +493,7 @@ pub(crate) mod tests { use xmtp_id::{ associations::{ builder::SignatureRequest, test_utils::add_wallet_signature, AssociationState, + MemberIdentifier, }, InboxOwner, }; @@ -582,6 +583,13 @@ pub(crate) mod tests { let association_state = get_association_state(&client, client.inbox_id()).await; + let members = + association_state.members_by_parent(&MemberIdentifier::Address(wallet_address.clone())); + // Those members should have timestamps + for member in members { + assert!(member.created_at_ns.is_some()); + } + assert_eq!(association_state.members().len(), 3); assert_eq!(association_state.recovery_address(), &wallet_address); assert!(association_state.get(&wallet_2_address.into()).is_some()); From bc07fd436537787963a013fd7d54c30a838aa169 Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Wed, 18 Sep 2024 14:09:48 -0400 Subject: [PATCH 04/17] uncross timestamps --- xmtp_id/src/associations/association_log.rs | 32 ++--- xmtp_id/src/associations/member.rs | 8 +- xmtp_id/src/associations/mod.rs | 4 +- xmtp_id/src/associations/serialization.rs | 4 +- xmtp_mls/src/identity_updates.rs | 2 +- .../src/gen/xmtp.identity.associations.rs | 110 +++++++++--------- .../gen/xmtp.identity.associations.serde.rs | 26 ++--- 7 files changed, 93 insertions(+), 93 deletions(-) diff --git a/xmtp_id/src/associations/association_log.rs b/xmtp_id/src/associations/association_log.rs index 31f81ee0d..67240418b 100644 --- a/xmtp_id/src/associations/association_log.rs +++ b/xmtp_id/src/associations/association_log.rs @@ -40,7 +40,7 @@ pub trait IdentityAction: Send + 'static { fn update_state( &self, existing_state: Option, - server_timestamp_ns: u64, + client_timestamp_ns: u64, ) -> Result; fn signatures(&self) -> Vec>; fn replay_check(&self, state: &AssociationState) -> Result<(), AssociationError> { @@ -67,7 +67,7 @@ impl IdentityAction for CreateInbox { fn update_state( &self, existing_state: Option, - _server_timestamp_ns: u64, + _client_timestamp_ns: u64, ) -> Result { if existing_state.is_some() { return Err(AssociationError::MultipleCreate); @@ -108,7 +108,7 @@ impl IdentityAction for AddAssociation { fn update_state( &self, maybe_existing_state: Option, - server_timestamp_ns: u64, + client_timestamp_ns: u64, ) -> Result { let existing_state = maybe_existing_state.ok_or(AssociationError::NotCreated)?; self.replay_check(&existing_state)?; @@ -182,7 +182,7 @@ impl IdentityAction for AddAssociation { let new_member = Member::new( new_member_address.clone(), Some(existing_entity_id), - Some(server_timestamp_ns), + Some(client_timestamp_ns), ); Ok(existing_state.add(new_member)) @@ -207,7 +207,7 @@ impl IdentityAction for RevokeAssociation { fn update_state( &self, maybe_existing_state: Option, - _server_timestamp_ns: u64, + _client_timestamp_ns: u64, ) -> Result { let existing_state = maybe_existing_state.ok_or(AssociationError::NotCreated)?; self.replay_check(&existing_state)?; @@ -263,7 +263,7 @@ impl IdentityAction for ChangeRecoveryAddress { fn update_state( &self, existing_state: Option, - _server_timestamp_ns: u64, + _client_timestamp_ns: u64, ) -> Result { let existing_state = existing_state.ok_or(AssociationError::NotCreated)?; self.replay_check(&existing_state)?; @@ -301,18 +301,18 @@ impl IdentityAction for Action { fn update_state( &self, existing_state: Option, - server_timestamp_ns: u64, + client_timestamp_ns: u64, ) -> Result { match self { - Action::CreateInbox(event) => event.update_state(existing_state, server_timestamp_ns), + Action::CreateInbox(event) => event.update_state(existing_state, client_timestamp_ns), Action::AddAssociation(event) => { - event.update_state(existing_state, server_timestamp_ns) + event.update_state(existing_state, client_timestamp_ns) } Action::RevokeAssociation(event) => { - event.update_state(existing_state, server_timestamp_ns) + event.update_state(existing_state, client_timestamp_ns) } Action::ChangeRecoveryAddress(event) => { - event.update_state(existing_state, server_timestamp_ns) + event.update_state(existing_state, client_timestamp_ns) } } } @@ -331,16 +331,16 @@ impl IdentityAction for Action { #[derive(Debug, Clone)] pub struct IdentityUpdate { pub inbox_id: String, - pub server_timestamp_ns: u64, + pub client_timestamp_ns: u64, pub actions: Vec, } impl IdentityUpdate { - pub fn new(actions: Vec, inbox_id: String, server_timestamp_ns: u64) -> Self { + pub fn new(actions: Vec, inbox_id: String, client_timestamp_ns: u64) -> Self { Self { inbox_id, actions, - server_timestamp_ns, + client_timestamp_ns, } } } @@ -349,11 +349,11 @@ impl IdentityAction for IdentityUpdate { fn update_state( &self, existing_state: Option, - server_timestamp_ns: u64, + client_timestamp_ns: u64, ) -> Result { let mut state = existing_state.clone(); for action in &self.actions { - state = Some(action.update_state(state, server_timestamp_ns)?); + state = Some(action.update_state(state, client_timestamp_ns)?); } let new_state = state.ok_or(AssociationError::NotCreated)?; diff --git a/xmtp_id/src/associations/member.rs b/xmtp_id/src/associations/member.rs index 62088e4fd..3159a6110 100644 --- a/xmtp_id/src/associations/member.rs +++ b/xmtp_id/src/associations/member.rs @@ -77,19 +77,19 @@ impl From> for MemberIdentifier { pub struct Member { pub identifier: MemberIdentifier, pub added_by_entity: Option, - pub created_at_ns: Option, + pub client_timestamp_ns: Option, } impl Member { pub fn new( identifier: MemberIdentifier, added_by_entity: Option, - created_at_ns: Option, + client_timestamp_ns: Option, ) -> Self { Self { identifier, added_by_entity, - created_at_ns, + client_timestamp_ns, } } @@ -124,7 +124,7 @@ mod tests { Self { identifier: MemberIdentifier::default(), added_by_entity: None, - created_at_ns: None, + client_timestamp_ns: None, } } } diff --git a/xmtp_id/src/associations/mod.rs b/xmtp_id/src/associations/mod.rs index 687b06773..022a948bf 100644 --- a/xmtp_id/src/associations/mod.rs +++ b/xmtp_id/src/associations/mod.rs @@ -23,7 +23,7 @@ pub fn apply_update( initial_state: AssociationState, update: IdentityUpdate, ) -> Result { - update.update_state(Some(initial_state), update.server_timestamp_ns) + update.update_state(Some(initial_state), update.client_timestamp_ns) } // Get the current state from an array of `IdentityUpdate`s. Entire operation fails if any operation fails @@ -32,7 +32,7 @@ pub fn get_state>( ) -> Result { let mut state = None; for update in updates.as_ref().iter() { - let res = update.update_state(state, update.server_timestamp_ns); + let res = update.update_state(state, update.client_timestamp_ns); state = Some(res?); } diff --git a/xmtp_id/src/associations/serialization.rs b/xmtp_id/src/associations/serialization.rs index c4ac33050..2f34f33e0 100644 --- a/xmtp_id/src/associations/serialization.rs +++ b/xmtp_id/src/associations/serialization.rs @@ -318,7 +318,7 @@ impl From for MemberProto { MemberProto { identifier: Some(member.identifier.into()), added_by_entity: member.added_by_entity.map(Into::into), - server_timestamp_ns: member.created_at_ns, + client_timestamp_ns: member.client_timestamp_ns, } } } @@ -333,7 +333,7 @@ impl TryFrom for Member { .ok_or(DeserializationError::MissingMemberIdentifier)? .try_into()?, added_by_entity: proto.added_by_entity.map(TryInto::try_into).transpose()?, - created_at_ns: proto.server_timestamp_ns, + client_timestamp_ns: proto.client_timestamp_ns, }) } } diff --git a/xmtp_mls/src/identity_updates.rs b/xmtp_mls/src/identity_updates.rs index 39fec44ba..cd5213746 100644 --- a/xmtp_mls/src/identity_updates.rs +++ b/xmtp_mls/src/identity_updates.rs @@ -587,7 +587,7 @@ pub(crate) mod tests { association_state.members_by_parent(&MemberIdentifier::Address(wallet_address.clone())); // Those members should have timestamps for member in members { - assert!(member.created_at_ns.is_some()); + assert!(member.client_timestamp_ns.is_some()); } assert_eq!(association_state.members().len(), 3); diff --git a/xmtp_proto/src/gen/xmtp.identity.associations.rs b/xmtp_proto/src/gen/xmtp.identity.associations.rs index 98ce4f5c2..f01f90fd9 100644 --- a/xmtp_proto/src/gen/xmtp.identity.associations.rs +++ b/xmtp_proto/src/gen/xmtp.identity.associations.rs @@ -5,7 +5,7 @@ #[derive(Clone, PartialEq, ::prost::Message)] pub struct RecoverableEcdsaSignature { /// 65-bytes \[ R || S || V \], with recovery id as the last byte - #[prost(bytes = "vec", tag = "1")] + #[prost(bytes="vec", tag="1")] pub bytes: ::prost::alloc::vec::Vec, } /// EdDSA signature for 25519 @@ -13,10 +13,10 @@ pub struct RecoverableEcdsaSignature { #[derive(Clone, PartialEq, ::prost::Message)] pub struct RecoverableEd25519Signature { /// 64 bytes \[R(32 bytes) || S(32 bytes)\] - #[prost(bytes = "vec", tag = "1")] + #[prost(bytes="vec", tag="1")] pub bytes: ::prost::alloc::vec::Vec, /// 32 bytes - #[prost(bytes = "vec", tag = "2")] + #[prost(bytes="vec", tag="2")] pub public_key: ::prost::alloc::vec::Vec, } /// Smart Contract Wallet signature @@ -25,16 +25,16 @@ pub struct RecoverableEd25519Signature { pub struct SmartContractWalletSignature { /// CAIP-10 string /// - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub account_id: ::prost::alloc::string::String, /// Specify the block number to verify the signature against - #[prost(uint64, tag = "2")] + #[prost(uint64, tag="2")] pub block_number: u64, /// The actual signature bytes - #[prost(bytes = "vec", tag = "3")] + #[prost(bytes="vec", tag="3")] pub signature: ::prost::alloc::vec::Vec, /// The RPC URL specifies a chain to verify the signature against - #[prost(string, tag = "4")] + #[prost(string, tag="4")] pub chain_rpc_url: ::prost::alloc::string::String, } /// An existing address on xmtpv2 may have already signed a legacy identity key @@ -46,9 +46,9 @@ pub struct SmartContractWalletSignature { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct LegacyDelegatedSignature { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub delegated_key: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub signature: ::core::option::Option, } /// A wrapper for all possible signature types @@ -60,7 +60,7 @@ pub struct Signature { /// recoverable, or specified as a field. /// 2. The signer certifies that the signing payload is correct. The payload /// must be inferred from the context in which the signature is provided. - #[prost(oneof = "signature::Signature", tags = "1, 2, 3, 4")] + #[prost(oneof="signature::Signature", tags="1, 2, 3, 4")] pub signature: ::core::option::Option, } /// Nested message and enum types in `Signature`. @@ -71,15 +71,15 @@ pub mod signature { /// 2. The signer certifies that the signing payload is correct. The payload /// must be inferred from the context in which the signature is provided. #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] +#[derive(Clone, PartialEq, ::prost::Oneof)] pub enum Signature { - #[prost(message, tag = "1")] + #[prost(message, tag="1")] Erc191(super::RecoverableEcdsaSignature), - #[prost(message, tag = "2")] + #[prost(message, tag="2")] Erc6492(super::SmartContractWalletSignature), - #[prost(message, tag = "3")] + #[prost(message, tag="3")] InstallationKey(super::RecoverableEd25519Signature), - #[prost(message, tag = "4")] + #[prost(message, tag="4")] DelegatedErc191(super::LegacyDelegatedSignature), } } @@ -87,17 +87,17 @@ pub mod signature { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MemberIdentifier { - #[prost(oneof = "member_identifier::Kind", tags = "1, 2")] + #[prost(oneof="member_identifier::Kind", tags="1, 2")] pub kind: ::core::option::Option, } /// Nested message and enum types in `MemberIdentifier`. pub mod member_identifier { #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] +#[derive(Clone, PartialEq, ::prost::Oneof)] pub enum Kind { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] Address(::prost::alloc::string::String), - #[prost(bytes, tag = "2")] + #[prost(bytes, tag="2")] InstallationPublicKey(::prost::alloc::vec::Vec), } } @@ -105,12 +105,12 @@ pub mod member_identifier { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Member { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub identifier: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub added_by_entity: ::core::option::Option, - #[prost(uint64, optional, tag = "3")] - pub server_timestamp_ns: ::core::option::Option, + #[prost(uint64, optional, tag="3")] + pub client_timestamp_ns: ::core::option::Option, } /// The first entry of any XID log. The XID must be deterministically derivable /// from the address and nonce. @@ -119,12 +119,12 @@ pub struct Member { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CreateInbox { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub initial_address: ::prost::alloc::string::String, - #[prost(uint64, tag = "2")] + #[prost(uint64, tag="2")] pub nonce: u64, /// Must be an addressable member - #[prost(message, optional, tag = "3")] + #[prost(message, optional, tag="3")] pub initial_address_signature: ::core::option::Option, } /// Adds a new member for an XID - either an addressable member such as a @@ -134,20 +134,20 @@ pub struct CreateInbox { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct AddAssociation { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub new_member_identifier: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub existing_member_signature: ::core::option::Option, - #[prost(message, optional, tag = "3")] + #[prost(message, optional, tag="3")] pub new_member_signature: ::core::option::Option, } /// Revokes a member from an XID. The recovery address must sign the revocation. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct RevokeAssociation { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub member_to_revoke: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub recovery_address_signature: ::core::option::Option, } /// Changes the recovery address for an XID. The recovery address is not required @@ -156,30 +156,30 @@ pub struct RevokeAssociation { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ChangeRecoveryAddress { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub new_recovery_address: ::prost::alloc::string::String, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub existing_recovery_address_signature: ::core::option::Option, } /// A single identity operation #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct IdentityAction { - #[prost(oneof = "identity_action::Kind", tags = "1, 2, 3, 4")] + #[prost(oneof="identity_action::Kind", tags="1, 2, 3, 4")] pub kind: ::core::option::Option, } /// Nested message and enum types in `IdentityAction`. pub mod identity_action { #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] +#[derive(Clone, PartialEq, ::prost::Oneof)] pub enum Kind { - #[prost(message, tag = "1")] + #[prost(message, tag="1")] CreateInbox(super::CreateInbox), - #[prost(message, tag = "2")] + #[prost(message, tag="2")] Add(super::AddAssociation), - #[prost(message, tag = "3")] + #[prost(message, tag="3")] Revoke(super::RevokeAssociation), - #[prost(message, tag = "4")] + #[prost(message, tag="4")] ChangeRecoveryAddress(super::ChangeRecoveryAddress), } } @@ -193,42 +193,42 @@ pub mod identity_action { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct IdentityUpdate { - #[prost(message, repeated, tag = "1")] + #[prost(message, repeated, tag="1")] pub actions: ::prost::alloc::vec::Vec, - #[prost(uint64, tag = "2")] + #[prost(uint64, tag="2")] pub client_timestamp_ns: u64, - #[prost(string, tag = "3")] + #[prost(string, tag="3")] pub inbox_id: ::prost::alloc::string::String, } /// Map of members belonging to an inbox_id #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MemberMap { - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag="1")] pub key: ::core::option::Option, - #[prost(message, optional, tag = "2")] + #[prost(message, optional, tag="2")] pub value: ::core::option::Option, } /// A final association state resulting from multiple `IdentityUpdates` #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct AssociationState { - #[prost(string, tag = "1")] + #[prost(string, tag="1")] pub inbox_id: ::prost::alloc::string::String, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub members: ::prost::alloc::vec::Vec, - #[prost(string, tag = "3")] + #[prost(string, tag="3")] pub recovery_address: ::prost::alloc::string::String, - #[prost(bytes = "vec", repeated, tag = "4")] + #[prost(bytes="vec", repeated, tag="4")] pub seen_signatures: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, } /// / state diff between two final AssociationStates #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct AssociationStateDiff { - #[prost(message, repeated, tag = "1")] + #[prost(message, repeated, tag="1")] pub new_members: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag="2")] pub removed_members: ::prost::alloc::vec::Vec, } /// Encoded file descriptor set for the `xmtp.identity.associations` package @@ -472,12 +472,12 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[ 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x64, 0x64, 0x65, 0x64, 0x42, 0x79, 0x45, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x13, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x11, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x13, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x11, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, + 0x79, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x22, 0xaf, 0x01, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, @@ -851,4 +851,4 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[ 0x02, 0x01, 0x03, 0x12, 0x03, 0x63, 0x2e, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, ]; include!("xmtp.identity.associations.serde.rs"); -// @@protoc_insertion_point(module) +// @@protoc_insertion_point(module) \ No newline at end of file diff --git a/xmtp_proto/src/gen/xmtp.identity.associations.serde.rs b/xmtp_proto/src/gen/xmtp.identity.associations.serde.rs index ee29fad7c..696f4a122 100644 --- a/xmtp_proto/src/gen/xmtp.identity.associations.serde.rs +++ b/xmtp_proto/src/gen/xmtp.identity.associations.serde.rs @@ -1017,7 +1017,7 @@ impl serde::Serialize for Member { if self.added_by_entity.is_some() { len += 1; } - if self.server_timestamp_ns.is_some() { + if self.client_timestamp_ns.is_some() { len += 1; } let mut struct_ser = serializer.serialize_struct("xmtp.identity.associations.Member", len)?; @@ -1027,10 +1027,10 @@ impl serde::Serialize for Member { if let Some(v) = self.added_by_entity.as_ref() { struct_ser.serialize_field("addedByEntity", v)?; } - if let Some(v) = self.server_timestamp_ns.as_ref() { + if let Some(v) = self.client_timestamp_ns.as_ref() { #[allow(clippy::needless_borrow)] #[allow(clippy::needless_borrows_for_generic_args)] - struct_ser.serialize_field("serverTimestampNs", ToString::to_string(&v).as_str())?; + struct_ser.serialize_field("clientTimestampNs", ToString::to_string(&v).as_str())?; } struct_ser.end() } @@ -1045,15 +1045,15 @@ impl<'de> serde::Deserialize<'de> for Member { "identifier", "added_by_entity", "addedByEntity", - "server_timestamp_ns", - "serverTimestampNs", + "client_timestamp_ns", + "clientTimestampNs", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { Identifier, AddedByEntity, - ServerTimestampNs, + ClientTimestampNs, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -1077,7 +1077,7 @@ impl<'de> serde::Deserialize<'de> for Member { match value { "identifier" => Ok(GeneratedField::Identifier), "addedByEntity" | "added_by_entity" => Ok(GeneratedField::AddedByEntity), - "serverTimestampNs" | "server_timestamp_ns" => Ok(GeneratedField::ServerTimestampNs), + "clientTimestampNs" | "client_timestamp_ns" => Ok(GeneratedField::ClientTimestampNs), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -1099,7 +1099,7 @@ impl<'de> serde::Deserialize<'de> for Member { { let mut identifier__ = None; let mut added_by_entity__ = None; - let mut server_timestamp_ns__ = None; + let mut client_timestamp_ns__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Identifier => { @@ -1114,11 +1114,11 @@ impl<'de> serde::Deserialize<'de> for Member { } added_by_entity__ = map_.next_value()?; } - GeneratedField::ServerTimestampNs => { - if server_timestamp_ns__.is_some() { - return Err(serde::de::Error::duplicate_field("serverTimestampNs")); + GeneratedField::ClientTimestampNs => { + if client_timestamp_ns__.is_some() { + return Err(serde::de::Error::duplicate_field("clientTimestampNs")); } - server_timestamp_ns__ = + client_timestamp_ns__ = map_.next_value::<::std::option::Option<::pbjson::private::NumberDeserialize<_>>>()?.map(|x| x.0) ; } @@ -1127,7 +1127,7 @@ impl<'de> serde::Deserialize<'de> for Member { Ok(Member { identifier: identifier__, added_by_entity: added_by_entity__, - server_timestamp_ns: server_timestamp_ns__, + client_timestamp_ns: client_timestamp_ns__, }) } } From 39511fae66cb7f2744b0b389550ee815bffc4910 Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Wed, 18 Sep 2024 14:34:23 -0400 Subject: [PATCH 05/17] source timestamp --- xmtp_id/src/associations/association_log.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xmtp_id/src/associations/association_log.rs b/xmtp_id/src/associations/association_log.rs index 67240418b..681dfc807 100644 --- a/xmtp_id/src/associations/association_log.rs +++ b/xmtp_id/src/associations/association_log.rs @@ -349,11 +349,11 @@ impl IdentityAction for IdentityUpdate { fn update_state( &self, existing_state: Option, - client_timestamp_ns: u64, + _client_timestamp_ns: u64, ) -> Result { let mut state = existing_state.clone(); for action in &self.actions { - state = Some(action.update_state(state, client_timestamp_ns)?); + state = Some(action.update_state(state, self.client_timestamp_ns)?); } let new_state = state.ok_or(AssociationError::NotCreated)?; From 16f74b303d33ac88d7e46dd9d4a62a370c6221af Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Wed, 18 Sep 2024 14:56:20 -0400 Subject: [PATCH 06/17] migration to clear out the identity_updates cache --- .../2024-09-18-185314_clear_identity_updates_cache/down.sql | 0 .../2024-09-18-185314_clear_identity_updates_cache/up.sql | 1 + 2 files changed, 1 insertion(+) create mode 100644 xmtp_mls/migrations/2024-09-18-185314_clear_identity_updates_cache/down.sql create mode 100644 xmtp_mls/migrations/2024-09-18-185314_clear_identity_updates_cache/up.sql diff --git a/xmtp_mls/migrations/2024-09-18-185314_clear_identity_updates_cache/down.sql b/xmtp_mls/migrations/2024-09-18-185314_clear_identity_updates_cache/down.sql new file mode 100644 index 000000000..e69de29bb diff --git a/xmtp_mls/migrations/2024-09-18-185314_clear_identity_updates_cache/up.sql b/xmtp_mls/migrations/2024-09-18-185314_clear_identity_updates_cache/up.sql new file mode 100644 index 000000000..ebabc499d --- /dev/null +++ b/xmtp_mls/migrations/2024-09-18-185314_clear_identity_updates_cache/up.sql @@ -0,0 +1 @@ +DELETE FROM identity_updates; From 38a34d127280d676fcad8ce8859da4b21e259ac2 Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Wed, 18 Sep 2024 15:07:54 -0400 Subject: [PATCH 07/17] revert to hard failing on deserialization issues --- xmtp_mls/src/identity_updates.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/xmtp_mls/src/identity_updates.rs b/xmtp_mls/src/identity_updates.rs index cd5213746..34689e833 100644 --- a/xmtp_mls/src/identity_updates.rs +++ b/xmtp_mls/src/identity_updates.rs @@ -117,18 +117,10 @@ where return Err(AssociationError::MissingIdentityUpdate.into()); } - match StoredAssociationState::read_from_cache(conn, inbox_id.to_string(), last_sequence_id) + if let Some(association_state) = + StoredAssociationState::read_from_cache(conn, inbox_id.to_string(), last_sequence_id)? { - Ok(Some(state)) => { - return Ok(state); - } - // If the cache fails to deserialize, it's because the structure of the cache - // has changed, and we need to reload. - Err(err) => { - log::warn!("{err:?}"); - } - // No cache - _ => {} + return Ok(association_state); } let unverified_updates = updates From a7350fa76019fa8fd65e8b1cc4a1397901a3d6d6 Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Wed, 18 Sep 2024 15:09:21 -0400 Subject: [PATCH 08/17] clear association states --- .../down.sql | 0 .../2024-09-18-185314_clear_association_states_cache/up.sql | 1 + .../2024-09-18-185314_clear_identity_updates_cache/up.sql | 1 - 3 files changed, 1 insertion(+), 1 deletion(-) rename xmtp_mls/migrations/{2024-09-18-185314_clear_identity_updates_cache => 2024-09-18-185314_clear_association_states_cache}/down.sql (100%) create mode 100644 xmtp_mls/migrations/2024-09-18-185314_clear_association_states_cache/up.sql delete mode 100644 xmtp_mls/migrations/2024-09-18-185314_clear_identity_updates_cache/up.sql diff --git a/xmtp_mls/migrations/2024-09-18-185314_clear_identity_updates_cache/down.sql b/xmtp_mls/migrations/2024-09-18-185314_clear_association_states_cache/down.sql similarity index 100% rename from xmtp_mls/migrations/2024-09-18-185314_clear_identity_updates_cache/down.sql rename to xmtp_mls/migrations/2024-09-18-185314_clear_association_states_cache/down.sql diff --git a/xmtp_mls/migrations/2024-09-18-185314_clear_association_states_cache/up.sql b/xmtp_mls/migrations/2024-09-18-185314_clear_association_states_cache/up.sql new file mode 100644 index 000000000..7a6ba7017 --- /dev/null +++ b/xmtp_mls/migrations/2024-09-18-185314_clear_association_states_cache/up.sql @@ -0,0 +1 @@ +DELETE FROM association_state; diff --git a/xmtp_mls/migrations/2024-09-18-185314_clear_identity_updates_cache/up.sql b/xmtp_mls/migrations/2024-09-18-185314_clear_identity_updates_cache/up.sql deleted file mode 100644 index ebabc499d..000000000 --- a/xmtp_mls/migrations/2024-09-18-185314_clear_identity_updates_cache/up.sql +++ /dev/null @@ -1 +0,0 @@ -DELETE FROM identity_updates; From edf49f23e08cd481f877e0b7d61a17d006024c41 Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Thu, 19 Sep 2024 12:47:27 -0400 Subject: [PATCH 09/17] rebuild association state on cache miss --- bindings_ffi/src/mls.rs | 30 +++++++------- bindings_node/src/groups.rs | 5 ++- xmtp_mls/src/client.rs | 4 +- xmtp_mls/src/groups/members.rs | 40 +++++++++++++------ xmtp_mls/src/groups/mod.rs | 30 +++++++------- xmtp_mls/src/identity_updates.rs | 26 ++++++++++-- .../encrypted_store/association_state.rs | 7 +++- 7 files changed, 92 insertions(+), 50 deletions(-) diff --git a/bindings_ffi/src/mls.rs b/bindings_ffi/src/mls.rs index e6a3c043e..c1d372d47 100644 --- a/bindings_ffi/src/mls.rs +++ b/bindings_ffi/src/mls.rs @@ -1047,7 +1047,7 @@ impl FfiGroup { Ok(ffi_message) } - pub fn list_members(&self) -> Result, GenericError> { + pub async fn list_members(&self) -> Result, GenericError> { let group = MlsGroup::new( self.inner_client.context().clone(), self.group_id.clone(), @@ -1055,7 +1055,8 @@ impl FfiGroup { ); let members: Vec = group - .members()? + .members(&self.inner_client) + .await? .into_iter() .map(|member| FfiGroupMember { inbox_id: member.inbox_id, @@ -2275,7 +2276,7 @@ mod tests { .await .unwrap(); - let members = group.list_members().unwrap(); + let members = group.list_members().await.unwrap(); assert_eq!(members.len(), 2); } @@ -2300,7 +2301,7 @@ mod tests { .await .unwrap(); - let members = group.list_members().unwrap(); + let members = group.list_members().await.unwrap(); assert_eq!(members.len(), 2); assert_eq!(group.group_name().unwrap(), "Group Name"); assert_eq!(group.group_image_url_square().unwrap(), "url"); @@ -2564,10 +2565,10 @@ mod tests { client2_group.sync().await.unwrap(); // Assert both clients see 2 members - let client1_members = client1_group.list_members().unwrap(); + let client1_members = client1_group.list_members().await.unwrap(); assert_eq!(client1_members.len(), 2); - let client2_members = client2_group.list_members().unwrap(); + let client2_members = client2_group.list_members().await.unwrap(); assert_eq!(client2_members.len(), 2); // Drop and delete local database for client2 @@ -2585,12 +2586,12 @@ mod tests { .unwrap(); // Assert client1 still sees 2 members - let client1_members = client1_group.list_members().unwrap(); + let client1_members = client1_group.list_members().await.unwrap(); assert_eq!(client1_members.len(), 2); client2.conversations().sync().await.unwrap(); let client2_group = client2.group(group.id()).unwrap(); - let client2_members = client2_group.list_members().unwrap(); + let client2_members = client2_group.list_members().await.unwrap(); assert_eq!(client2_members.len(), 2); } @@ -2838,11 +2839,11 @@ mod tests { .unwrap(); bo_group.sync().await.unwrap(); - let bo_members = bo_group.list_members().unwrap(); + let bo_members = bo_group.list_members().await.unwrap(); assert_eq!(bo_members.len(), 4); alix_group.sync().await.unwrap(); - let alix_members = alix_group.list_members().unwrap(); + let alix_members = alix_group.list_members().await.unwrap(); assert_eq!(alix_members.len(), 4); } @@ -2864,11 +2865,11 @@ mod tests { let bo_group = bo.group(alix_group.id()).unwrap(); alix_group.sync().await.unwrap(); - let alix_members = alix_group.list_members().unwrap(); + let alix_members = alix_group.list_members().await.unwrap(); assert_eq!(alix_members.len(), 2); bo_group.sync().await.unwrap(); - let bo_members = bo_group.list_members().unwrap(); + let bo_members = bo_group.list_members().await.unwrap(); assert_eq!(bo_members.len(), 2); let bo_messages = bo_group @@ -2892,11 +2893,11 @@ mod tests { assert!(bo_messages.first().unwrap().kind == FfiGroupMessageKind::MembershipChange); assert_eq!(bo_messages.len(), 1); - let bo_members = bo_group.list_members().unwrap(); + let bo_members = bo_group.list_members().await.unwrap(); assert_eq!(bo_members.len(), 1); alix_group.sync().await.unwrap(); - let alix_members = alix_group.list_members().unwrap(); + let alix_members = alix_group.list_members().await.unwrap(); assert_eq!(alix_members.len(), 1); } @@ -3736,6 +3737,7 @@ mod tests { if let Some(member) = alix_group .list_members() + .await .unwrap() .iter() .find(|&m| m.inbox_id == bo.inbox_id()) diff --git a/bindings_node/src/groups.rs b/bindings_node/src/groups.rs index a652ed2eb..0cf62e04a 100644 --- a/bindings_node/src/groups.rs +++ b/bindings_node/src/groups.rs @@ -209,7 +209,7 @@ impl NapiGroup { } #[napi] - pub fn list_members(&self) -> Result> { + pub async fn list_members(&self) -> Result> { let group = MlsGroup::new( self.inner_client.context().clone(), self.group_id.clone(), @@ -217,7 +217,8 @@ impl NapiGroup { ); let members: Vec = group - .members() + .members(&self.inner_client) + .await .map_err(ErrorWrapper::from)? .into_iter() .map(|member| NapiGroupMember { diff --git a/xmtp_mls/src/client.rs b/xmtp_mls/src/client.rs index b3a5dede3..0332f64b4 100644 --- a/xmtp_mls/src/client.rs +++ b/xmtp_mls/src/client.rs @@ -1045,14 +1045,14 @@ mod tests { .add_members_by_inbox_id(&amal, vec![bola.inbox_id()]) .await .unwrap(); - assert_eq!(amal_group.members().unwrap().len(), 2); + assert_eq!(amal_group.members(&amal).await.unwrap().len(), 2); // Now remove bola amal_group .remove_members_by_inbox_id(&amal, vec![bola.inbox_id()]) .await .unwrap(); - assert_eq!(amal_group.members().unwrap().len(), 1); + assert_eq!(amal_group.members(&amal).await.unwrap().len(), 1); log::info!("Syncing bolas welcomes"); // See if Bola can see that they were added to the group bola.sync_welcomes().await.unwrap(); diff --git a/xmtp_mls/src/groups/members.rs b/xmtp_mls/src/groups/members.rs index 1eb6df5ee..45d8da969 100644 --- a/xmtp_mls/src/groups/members.rs +++ b/xmtp_mls/src/groups/members.rs @@ -8,6 +8,7 @@ use crate::{ consent_record::{ConsentState, ConsentType}, }, xmtp_openmls_provider::XmtpOpenMlsProvider, + Client, XmtpApi, }; #[derive(Debug, Clone)] @@ -28,13 +29,17 @@ pub enum PermissionLevel { impl MlsGroup { // Load the member list for the group from the DB, merging together multiple installations into a single entry - pub fn members(&self) -> Result, GroupError> { + pub async fn members( + &self, + client: &Client, + ) -> Result, GroupError> { let provider = self.mls_provider()?; - self.members_with_provider(&provider) + self.members_with_provider(client, &provider).await } - pub fn members_with_provider( + pub async fn members_with_provider( &self, + client: &Client, provider: &XmtpOpenMlsProvider, ) -> Result, GroupError> { let openmls_group = self.load_mls_group(provider)?; @@ -48,19 +53,28 @@ impl MlsGroup { .collect::>(); let conn = provider.conn_ref(); - let association_states = + let mut association_states = StoredAssociationState::batch_read_from_cache(conn, requests.clone())?; let mutable_metadata = self.mutable_metadata(provider)?; if association_states.len() != requests.len() { - // Cache miss - not expected to happen because: - // 1. We don't allow updates to the group metadata unless we have already validated the association state - // 2. When validating the association state, we must have written it to the cache - log::error!( - "Failed to load all members for group - metadata: {:?}, computed members: {:?}", - requests, - association_states - ); - return Err(GroupError::InvalidGroupMembership); + // Attempt to rebuild the cache before erroring out due to a cache miss. + let requests = requests + .into_iter() + .map(|(id, sequence)| (id, Some(sequence))) + .collect(); + association_states = client.batch_get_association_state(conn, &requests).await?; + + if association_states.len() != requests.len() { + // Cache miss - not expected to happen because: + // 1. We don't allow updates to the group metadata unless we have already validated the association state + // 2. When validating the association state, we must have written it to the cache + log::error!( + "Failed to load all members for group - metadata: {:?}, computed members: {:?}", + requests, + association_states + ); + return Err(GroupError::InvalidGroupMembership); + } } let members = association_states .into_iter() diff --git a/xmtp_mls/src/groups/mod.rs b/xmtp_mls/src/groups/mod.rs index 44dde5ecb..29b22d8a4 100644 --- a/xmtp_mls/src/groups/mod.rs +++ b/xmtp_mls/src/groups/mod.rs @@ -617,7 +617,7 @@ impl MlsGroup { .get_inbox_ids(account_addresses.clone()) .await?; // get current number of users in group - let member_count = self.members()?.len(); + let member_count = self.members(client).await?.len(); if member_count + inbox_id_map.len() > MAX_GROUP_SIZE as usize { return Err(GroupError::UserLimitExceeded); } @@ -1507,8 +1507,8 @@ mod tests { assert_eq!(bola_group_name, ""); // Check if both clients can see the members correctly - let amal_members: Vec = amal_group.members().unwrap(); - let bola_members: Vec = bola_group.members().unwrap(); + let amal_members: Vec = amal_group.members(&amal).await.unwrap(); + let bola_members: Vec = bola_group.members(&bola).await.unwrap(); assert_eq!(amal_members.len(), 2); assert_eq!(bola_members.len(), 2); @@ -1835,7 +1835,7 @@ mod tests { .await .unwrap(); log::info!("created the group with 2 additional members"); - assert_eq!(group.members().unwrap().len(), 3); + assert_eq!(group.members(&bola).await.unwrap().len(), 3); let messages = group.find_messages(None, None, None, None, None).unwrap(); assert_eq!(messages.len(), 1); assert_eq!(messages[0].kind, GroupMessageKind::MembershipChange); @@ -1849,7 +1849,7 @@ mod tests { .remove_members(&amal, vec![bola_wallet.get_address()]) .await .unwrap(); - assert_eq!(group.members().unwrap().len(), 2); + assert_eq!(group.members(&bola).await.unwrap().len(), 2); log::info!("removed bola"); let messages = group.find_messages(None, None, None, None, None).unwrap(); assert_eq!(messages.len(), 2); @@ -1885,20 +1885,22 @@ mod tests { ) .await .unwrap(); - assert_eq!(group.members().unwrap().len(), 3); + assert_eq!(group.members(&bola).await.unwrap().len(), 3); group .remove_members(&amal, vec![bola_wallet.get_address()]) .await .unwrap(); - assert_eq!(group.members().unwrap().len(), 2); + assert_eq!(group.members(&bola).await.unwrap().len(), 2); assert!(group - .members() + .members(&bola) + .await .unwrap() .iter() .all(|m| m.inbox_id != bola.inbox_id())); assert!(group - .members() + .members(&bola) + .await .unwrap() .iter() .any(|m| m.inbox_id == charlie.inbox_id())); @@ -1944,7 +1946,7 @@ mod tests { .await .unwrap(); - assert_eq!(group.members().unwrap().len(), 2); + assert_eq!(group.members(&amal).await.unwrap().len(), 2); let provider: XmtpOpenMlsProvider = amal.context.store.conn().unwrap().into(); // Finished with setup @@ -2591,7 +2593,7 @@ mod tests { amal_group.sync(&amal).await.unwrap(); // Initial checks for group members - let initial_members = amal_group.members().unwrap(); + let initial_members = amal_group.members(&amal).await.unwrap(); let mut count_member = 0; let mut count_admin = 0; let mut count_super_admin = 0; @@ -2619,7 +2621,7 @@ mod tests { amal_group.sync(&amal).await.unwrap(); // Check after adding Bola as an admin - let members = amal_group.members().unwrap(); + let members = amal_group.members(&amal).await.unwrap(); let mut count_member = 0; let mut count_admin = 0; let mut count_super_admin = 0; @@ -2647,7 +2649,7 @@ mod tests { amal_group.sync(&amal).await.unwrap(); // Check after adding Caro as a super admin - let members = amal_group.members().unwrap(); + let members = amal_group.members(&amal).await.unwrap(); let mut count_member = 0; let mut count_admin = 0; let mut count_super_admin = 0; @@ -2862,7 +2864,7 @@ mod tests { .await .unwrap(); bola_group.sync(&bola).await.unwrap(); - let members = bola_group.members().unwrap(); + let members = bola_group.members(&bola).await.unwrap(); assert_eq!(members.len(), 3); } diff --git a/xmtp_mls/src/identity_updates.rs b/xmtp_mls/src/identity_updates.rs index 34689e833..187975b91 100644 --- a/xmtp_mls/src/identity_updates.rs +++ b/xmtp_mls/src/identity_updates.rs @@ -90,6 +90,24 @@ where Ok(needs_update) } + pub async fn batch_get_association_state>( + &self, + conn: &DbConnection, + identifiers: &Vec<(InboxId, Option)>, + ) -> Result, ClientError> { + let association_states = try_join_all( + identifiers + .iter() + .map(|(inbox_id, to_sequence_id)| { + self.get_association_state(conn, inbox_id, *to_sequence_id) + }) + .collect::>(), + ) + .await?; + + Ok(association_states) + } + pub async fn get_latest_association_state>( &self, conn: &DbConnection, @@ -113,8 +131,10 @@ where .last() .ok_or::(AssociationError::MissingIdentityUpdate.into())? .sequence_id; - if to_sequence_id.is_some() && to_sequence_id != Some(last_sequence_id) { - return Err(AssociationError::MissingIdentityUpdate.into()); + if let Some(to_sequence_id) = to_sequence_id { + if to_sequence_id != last_sequence_id { + return Err(AssociationError::MissingIdentityUpdate.into()); + } } if let Some(association_state) = @@ -473,7 +493,7 @@ async fn verify_updates( try_join_all( updates .iter() - .map(|update| async { update.to_verified(scw_verifier).await }), + .map(|update| update.to_verified(scw_verifier)), ) .await } diff --git a/xmtp_mls/src/storage/encrypted_store/association_state.rs b/xmtp_mls/src/storage/encrypted_store/association_state.rs index 079c6ec47..3ca603d1d 100644 --- a/xmtp_mls/src/storage/encrypted_store/association_state.rs +++ b/xmtp_mls/src/storage/encrypted_store/association_state.rs @@ -1,7 +1,7 @@ use diesel::prelude::*; use prost::Message; use xmtp_id::{ - associations::{AssociationState, DeserializationError}, + associations::{AssociationError, AssociationState, DeserializationError}, InboxId, }; use xmtp_proto::xmtp::identity::associations::AssociationState as AssociationStateProto; @@ -10,7 +10,10 @@ use super::{ schema::association_state::{self, dsl}, DbConnection, }; -use crate::{impl_fetch, impl_store_or_ignore, storage::StorageError, Fetch, StoreOrIgnore}; +use crate::{ + client::ClientError, impl_fetch, impl_store_or_ignore, storage::StorageError, Fetch, + StoreOrIgnore, +}; /// StoredIdentityUpdate holds a serialized IdentityUpdate record #[derive(Insertable, Identifiable, Queryable, Debug, Clone, PartialEq, Eq)] From eedff513b2a348cf644431a4141e1ca2305900c3 Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Thu, 19 Sep 2024 12:52:55 -0400 Subject: [PATCH 10/17] lint --- xmtp_mls/src/groups/members.rs | 2 +- xmtp_mls/src/identity_updates.rs | 2 +- xmtp_mls/src/storage/encrypted_store/association_state.rs | 7 ++----- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/xmtp_mls/src/groups/members.rs b/xmtp_mls/src/groups/members.rs index 45d8da969..1136074da 100644 --- a/xmtp_mls/src/groups/members.rs +++ b/xmtp_mls/src/groups/members.rs @@ -58,7 +58,7 @@ impl MlsGroup { let mutable_metadata = self.mutable_metadata(provider)?; if association_states.len() != requests.len() { // Attempt to rebuild the cache before erroring out due to a cache miss. - let requests = requests + let requests: Vec<_> = requests .into_iter() .map(|(id, sequence)| (id, Some(sequence))) .collect(); diff --git a/xmtp_mls/src/identity_updates.rs b/xmtp_mls/src/identity_updates.rs index 187975b91..8397b64f2 100644 --- a/xmtp_mls/src/identity_updates.rs +++ b/xmtp_mls/src/identity_updates.rs @@ -93,7 +93,7 @@ where pub async fn batch_get_association_state>( &self, conn: &DbConnection, - identifiers: &Vec<(InboxId, Option)>, + identifiers: &[(InboxId, Option)], ) -> Result, ClientError> { let association_states = try_join_all( identifiers diff --git a/xmtp_mls/src/storage/encrypted_store/association_state.rs b/xmtp_mls/src/storage/encrypted_store/association_state.rs index 3ca603d1d..079c6ec47 100644 --- a/xmtp_mls/src/storage/encrypted_store/association_state.rs +++ b/xmtp_mls/src/storage/encrypted_store/association_state.rs @@ -1,7 +1,7 @@ use diesel::prelude::*; use prost::Message; use xmtp_id::{ - associations::{AssociationError, AssociationState, DeserializationError}, + associations::{AssociationState, DeserializationError}, InboxId, }; use xmtp_proto::xmtp::identity::associations::AssociationState as AssociationStateProto; @@ -10,10 +10,7 @@ use super::{ schema::association_state::{self, dsl}, DbConnection, }; -use crate::{ - client::ClientError, impl_fetch, impl_store_or_ignore, storage::StorageError, Fetch, - StoreOrIgnore, -}; +use crate::{impl_fetch, impl_store_or_ignore, storage::StorageError, Fetch, StoreOrIgnore}; /// StoredIdentityUpdate holds a serialized IdentityUpdate record #[derive(Insertable, Identifiable, Queryable, Debug, Clone, PartialEq, Eq)] From a82fa4fa3b9f98889e5e5530ff5c2cf35d0f01bc Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Thu, 19 Sep 2024 15:43:55 -0400 Subject: [PATCH 11/17] fix example --- Cargo.lock | 1 + examples/cli/Cargo.toml | 1 + examples/cli/cli-client.rs | 9 ++++++--- examples/cli/serializable.rs | 11 ++++++++--- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 085faa1ad..2127a8feb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6224,6 +6224,7 @@ dependencies = [ "clap", "ethers", "femme", + "futures", "hex", "kv-log-macro", "log", diff --git a/examples/cli/Cargo.toml b/examples/cli/Cargo.toml index 58e6f22b0..e9b2e4aa7 100644 --- a/examples/cli/Cargo.toml +++ b/examples/cli/Cargo.toml @@ -16,6 +16,7 @@ path = "cli-client.rs" clap = { version = "4.4.6", features = ["derive"] } ethers = "2.0.4" femme = "2.2.1" +futures.workspace = true hex = "0.4.3" kv-log-macro = "1.0.7" log = { workspace = true, features = [ diff --git a/examples/cli/cli-client.rs b/examples/cli/cli-client.rs index 063cc450e..6ede48fa3 100755 --- a/examples/cli/cli-client.rs +++ b/examples/cli/cli-client.rs @@ -15,6 +15,7 @@ use std::{fs, path::PathBuf, time::Duration}; use clap::{Parser, Subcommand, ValueEnum}; use ethers::signers::{coins_bip39::English, LocalWallet, MnemonicBuilder}; +use futures::future::join_all; use kv_log_macro::{error, info}; use prost::Message; use xmtp_id::associations::unverified::{UnverifiedRecoverableEcdsaSignature, UnverifiedSignature}; @@ -213,10 +214,12 @@ async fn main() { for group in group_list.iter() { group.sync(&client).await.expect("error syncing group"); } + let serializable_group_list = group_list .iter() - .map(Into::into) - .collect::>(); + .map(|g| SerializableGroup::from(g, &client)) + .collect::>(); + let serializable_group_list = join_all(serializable_group_list).await; info!( "group members", @@ -337,7 +340,7 @@ async fn main() { .group(hex::decode(group_id).expect("bad group id")) .expect("group not found"); group.sync(&client).await.unwrap(); - let serializable: SerializableGroup = group.into(); + let serializable = SerializableGroup::from(group, &client).await; info!("Group {}", group_id, { command_output: true, group_id: group_id, group_info: make_value(&serializable) }) } Commands::RequestHistorySync {} => { diff --git a/examples/cli/serializable.rs b/examples/cli/serializable.rs index 34f4d11c8..9153e0345 100644 --- a/examples/cli/serializable.rs +++ b/examples/cli/serializable.rs @@ -4,6 +4,7 @@ use xmtp_mls::{ codecs::{text::TextCodec, ContentCodec}, groups::MlsGroup, storage::group_message::StoredGroupMessage, + XmtpApi, }; use xmtp_proto::xmtp::mls::message_contents::EncodedContent; @@ -20,11 +21,15 @@ pub struct SerializableGroup { pub metadata: SerializableGroupMetadata, } -impl<'a> From<&'a MlsGroup> for SerializableGroup { - fn from(group: &'a MlsGroup) -> Self { +impl SerializableGroup { + pub async fn from( + group: &MlsGroup, + client: &xmtp_mls::Client, + ) -> Self { let group_id = hex::encode(group.group_id.clone()); let members = group - .members() + .members(client) + .await .expect("could not load members") .into_iter() .map(|m| m.inbox_id) From c63a7ab5919025376c80244ef41eca240530c626 Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Thu, 19 Sep 2024 15:55:07 -0400 Subject: [PATCH 12/17] reduce number of requests on cache rebuild --- xmtp_mls/src/groups/members.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/xmtp_mls/src/groups/members.rs b/xmtp_mls/src/groups/members.rs index 1136074da..dd21eb7bc 100644 --- a/xmtp_mls/src/groups/members.rs +++ b/xmtp_mls/src/groups/members.rs @@ -57,12 +57,24 @@ impl MlsGroup { StoredAssociationState::batch_read_from_cache(conn, requests.clone())?; let mutable_metadata = self.mutable_metadata(provider)?; if association_states.len() != requests.len() { - // Attempt to rebuild the cache before erroring out due to a cache miss. + // Attempt to rebuild the cache. let requests: Vec<_> = requests .into_iter() - .map(|(id, sequence)| (id, Some(sequence))) + .filter_map(|(id, sequence)| { + // Filter out association states we already have to avoid redundant requests. + if association_states + .iter() + .find(|state| *state.inbox_id() == id) + .is_some() + { + return None; + } + Some((id, Some(sequence))) + }) .collect(); - association_states = client.batch_get_association_state(conn, &requests).await?; + + let mut new_states = client.batch_get_association_state(conn, &requests).await?; + association_states.append(&mut new_states); if association_states.len() != requests.len() { // Cache miss - not expected to happen because: From fc6f648e54605e88be1f7373eae0b6fb5e75d68f Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Thu, 19 Sep 2024 15:59:09 -0400 Subject: [PATCH 13/17] lint --- xmtp_mls/src/groups/members.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/xmtp_mls/src/groups/members.rs b/xmtp_mls/src/groups/members.rs index dd21eb7bc..42ca497fa 100644 --- a/xmtp_mls/src/groups/members.rs +++ b/xmtp_mls/src/groups/members.rs @@ -64,8 +64,7 @@ impl MlsGroup { // Filter out association states we already have to avoid redundant requests. if association_states .iter() - .find(|state| *state.inbox_id() == id) - .is_some() + .any(|state| *state.inbox_id() == id) { return None; } From f5e0e8ac64b2bf511899292030bc203487ce7483 Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Thu, 19 Sep 2024 16:15:55 -0400 Subject: [PATCH 14/17] fix js test --- bindings_node/test/Conversations.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings_node/test/Conversations.test.ts b/bindings_node/test/Conversations.test.ts index 8ddc1adec..a631ad19c 100644 --- a/bindings_node/test/Conversations.test.ts +++ b/bindings_node/test/Conversations.test.ts @@ -43,7 +43,7 @@ describe('Conversations', () => { }) expect(group.addedByInboxId()).toBe(client1.inboxId()) expect(group.findMessages().length).toBe(1) - const members = group.listMembers() + const members = await group.listMembers() expect(members.length).toBe(2) const memberInboxIds = members.map((member) => member.inboxId) expect(memberInboxIds).toContain(client1.inboxId()) From 7ead43c7e4ecb27c29b6f826554934c043e19c56 Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Thu, 19 Sep 2024 16:17:31 -0400 Subject: [PATCH 15/17] comment --- xmtp_mls/src/groups/members.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xmtp_mls/src/groups/members.rs b/xmtp_mls/src/groups/members.rs index 42ca497fa..9f57f9727 100644 --- a/xmtp_mls/src/groups/members.rs +++ b/xmtp_mls/src/groups/members.rs @@ -61,7 +61,7 @@ impl MlsGroup { let requests: Vec<_> = requests .into_iter() .filter_map(|(id, sequence)| { - // Filter out association states we already have to avoid redundant requests. + // Filter out association states we already have to avoid unnecessary requests. if association_states .iter() .any(|state| *state.inbox_id() == id) From d98b9acd6472974cb5a05b98cb5f8351a50c9587 Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Thu, 19 Sep 2024 16:28:41 -0400 Subject: [PATCH 16/17] Assign a new variable --- xmtp_mls/src/groups/members.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/xmtp_mls/src/groups/members.rs b/xmtp_mls/src/groups/members.rs index 9f57f9727..dba0a6cb6 100644 --- a/xmtp_mls/src/groups/members.rs +++ b/xmtp_mls/src/groups/members.rs @@ -58,21 +58,23 @@ impl MlsGroup { let mutable_metadata = self.mutable_metadata(provider)?; if association_states.len() != requests.len() { // Attempt to rebuild the cache. - let requests: Vec<_> = requests - .into_iter() + let missing_requests: Vec<_> = requests + .iter() .filter_map(|(id, sequence)| { // Filter out association states we already have to avoid unnecessary requests. if association_states .iter() - .any(|state| *state.inbox_id() == id) + .any(|state| state.inbox_id() == id) { return None; } - Some((id, Some(sequence))) + Some((id.clone(), Some(*sequence))) }) .collect(); - let mut new_states = client.batch_get_association_state(conn, &requests).await?; + let mut new_states = client + .batch_get_association_state(conn, &missing_requests) + .await?; association_states.append(&mut new_states); if association_states.len() != requests.len() { From 0f3d1b9fac9494d510edb1bc6253fb932104a56c Mon Sep 17 00:00:00 2001 From: Dakota Brink Date: Thu, 19 Sep 2024 16:43:19 -0400 Subject: [PATCH 17/17] add test --- xmtp_mls/src/client.rs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/xmtp_mls/src/client.rs b/xmtp_mls/src/client.rs index 0332f64b4..c7d9f3eab 100644 --- a/xmtp_mls/src/client.rs +++ b/xmtp_mls/src/client.rs @@ -812,6 +812,7 @@ pub fn deserialize_welcome(welcome_bytes: &Vec) -> Result