diff --git a/crates/xmtp_proto/proto_version b/crates/xmtp_proto/proto_version index 50fff82353..f1a671844e 100644 --- a/crates/xmtp_proto/proto_version +++ b/crates/xmtp_proto/proto_version @@ -1 +1 @@ -845ad62c2285c531dcd775a769ebb0e7fe611eea +f04e01a9f97af67a3b6fe8419b534c237a2ea590 diff --git a/crates/xmtp_proto/src/gen/proto_descriptor.bin b/crates/xmtp_proto/src/gen/proto_descriptor.bin index 48ee48c56a..c7ecf8ba5d 100644 Binary files a/crates/xmtp_proto/src/gen/proto_descriptor.bin and b/crates/xmtp_proto/src/gen/proto_descriptor.bin differ diff --git a/crates/xmtp_proto/src/gen/xmtp.message_api.v1.rs b/crates/xmtp_proto/src/gen/xmtp.message_api.v1.rs index 6882242c90..d3fb9f6be9 100644 --- a/crates/xmtp_proto/src/gen/xmtp.message_api.v1.rs +++ b/crates/xmtp_proto/src/gen/xmtp.message_api.v1.rs @@ -1,4 +1,54 @@ // This file is @generated by prost-build. +/// Token is used by clients to prove to the nodes +/// that they are serving a specific wallet. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Token { + /// identity key signed by a wallet + #[prost(message, optional, tag = "1")] + pub identity_key: ::core::option::Option, + /// encoded bytes of AuthData + #[prost(bytes = "vec", tag = "2")] + pub auth_data_bytes: ::prost::alloc::vec::Vec, + /// identity key signature of AuthData bytes + #[prost(message, optional, tag = "3")] + pub auth_data_signature: ::core::option::Option< + super::super::message_contents::Signature, + >, +} +impl ::prost::Name for Token { + const NAME: &'static str = "Token"; + const PACKAGE: &'static str = "xmtp.message_api.v1"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_api.v1.Token".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_api.v1.Token".into() + } +} +/// AuthData carries token parameters that are authenticated +/// by the identity key signature. +/// It is embedded in the Token structure as bytes +/// so that the bytes don't need to be reconstructed +/// to verify the token signature. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct AuthData { + /// address of the wallet + #[prost(string, tag = "1")] + pub wallet_addr: ::prost::alloc::string::String, + /// time when the token was generated/signed + #[prost(uint64, tag = "2")] + pub created_ns: u64, +} +impl ::prost::Name for AuthData { + const NAME: &'static str = "AuthData"; + const PACKAGE: &'static str = "xmtp.message_api.v1"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_api.v1.AuthData".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_api.v1.AuthData".into() + } +} /// This is based off of the go-waku Index type, but with the /// receiverTime and pubsubTopic removed for simplicity. /// Both removed fields are optional @@ -718,53 +768,3 @@ pub mod message_api_server { const NAME: &'static str = SERVICE_NAME; } } -/// Token is used by clients to prove to the nodes -/// that they are serving a specific wallet. -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct Token { - /// identity key signed by a wallet - #[prost(message, optional, tag = "1")] - pub identity_key: ::core::option::Option, - /// encoded bytes of AuthData - #[prost(bytes = "vec", tag = "2")] - pub auth_data_bytes: ::prost::alloc::vec::Vec, - /// identity key signature of AuthData bytes - #[prost(message, optional, tag = "3")] - pub auth_data_signature: ::core::option::Option< - super::super::message_contents::Signature, - >, -} -impl ::prost::Name for Token { - const NAME: &'static str = "Token"; - const PACKAGE: &'static str = "xmtp.message_api.v1"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_api.v1.Token".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_api.v1.Token".into() - } -} -/// AuthData carries token parameters that are authenticated -/// by the identity key signature. -/// It is embedded in the Token structure as bytes -/// so that the bytes don't need to be reconstructed -/// to verify the token signature. -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct AuthData { - /// address of the wallet - #[prost(string, tag = "1")] - pub wallet_addr: ::prost::alloc::string::String, - /// time when the token was generated/signed - #[prost(uint64, tag = "2")] - pub created_ns: u64, -} -impl ::prost::Name for AuthData { - const NAME: &'static str = "AuthData"; - const PACKAGE: &'static str = "xmtp.message_api.v1"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_api.v1.AuthData".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_api.v1.AuthData".into() - } -} diff --git a/crates/xmtp_proto/src/gen/xmtp.message_contents.rs b/crates/xmtp_proto/src/gen/xmtp.message_contents.rs index 5e4f142c79..866bf7c0f7 100644 --- a/crates/xmtp_proto/src/gen/xmtp.message_contents.rs +++ b/crates/xmtp_proto/src/gen/xmtp.message_contents.rs @@ -220,6 +220,105 @@ impl ::prost::Name for PublicKeyBundle { "/xmtp.message_contents.PublicKeyBundle".into() } } +/// The message that will be signed by the Client and returned inside the +/// `action_body` field of the FrameAction message +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct FrameActionBody { + /// The URL of the frame that was clicked + /// May be different from `post_url` + #[prost(string, tag = "1")] + pub frame_url: ::prost::alloc::string::String, + /// The 1-indexed button that was clicked + #[prost(int32, tag = "2")] + pub button_index: i32, + /// Timestamp of the click in milliseconds since the epoch + #[deprecated] + #[prost(uint64, tag = "3")] + pub timestamp: u64, + /// A unique identifier for the conversation, not tied to anything on the + /// network. Will not match the topic or conversation_id + #[prost(string, tag = "4")] + pub opaque_conversation_identifier: ::prost::alloc::string::String, + /// Unix timestamp + #[prost(uint32, tag = "5")] + pub unix_timestamp: u32, + /// Input text from a text input field + #[prost(string, tag = "6")] + pub input_text: ::prost::alloc::string::String, + /// A state serialized to a string (for example via JSON.stringify()). Maximum 4096 bytes. + #[prost(string, tag = "7")] + pub state: ::prost::alloc::string::String, + /// A 0x wallet address + #[prost(string, tag = "8")] + pub address: ::prost::alloc::string::String, + /// A hash from a transaction + #[prost(string, tag = "9")] + pub transaction_id: ::prost::alloc::string::String, +} +impl ::prost::Name for FrameActionBody { + const NAME: &'static str = "FrameActionBody"; + const PACKAGE: &'static str = "xmtp.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_contents.FrameActionBody".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_contents.FrameActionBody".into() + } +} +/// The outer payload that will be sent as the `messageBytes` in the +/// `trusted_data` part of the Frames message +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct FrameAction { + #[deprecated] + #[prost(message, optional, tag = "1")] + pub signature: ::core::option::Option, + /// The SignedPublicKeyBundle of the signer, used to link the XMTP signature + /// with a blockchain account through a chain of signatures. + #[deprecated] + #[prost(message, optional, tag = "2")] + pub signed_public_key_bundle: ::core::option::Option, + /// Serialized FrameActionBody message, so that the signature verification can + /// happen on a byte-perfect representation of the message + #[prost(bytes = "vec", tag = "3")] + pub action_body: ::prost::alloc::vec::Vec, + /// The installation signature + #[prost(bytes = "vec", tag = "4")] + pub installation_signature: ::prost::alloc::vec::Vec, + /// The public installation id used to sign. + #[prost(bytes = "vec", tag = "5")] + pub installation_id: ::prost::alloc::vec::Vec, + /// The inbox id of the installation used to sign. + #[prost(string, tag = "6")] + pub inbox_id: ::prost::alloc::string::String, +} +impl ::prost::Name for FrameAction { + const NAME: &'static str = "FrameAction"; + const PACKAGE: &'static str = "xmtp.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_contents.FrameAction".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_contents.FrameAction".into() + } +} +/// SignedPayload is a wrapper for a signature and a payload +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct SignedPayload { + #[prost(bytes = "vec", tag = "1")] + pub payload: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "2")] + pub signature: ::core::option::Option, +} +impl ::prost::Name for SignedPayload { + const NAME: &'static str = "SignedPayload"; + const PACKAGE: &'static str = "xmtp.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_contents.SignedPayload".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_contents.SignedPayload".into() + } +} /// Ciphertext represents encrypted payload. /// It is definited as a union to support cryptographic algorithm agility. /// The payload is accompanied by the cryptographic parameters @@ -320,6 +419,219 @@ impl ::prost::Name for SignedEciesCiphertext { "/xmtp.message_contents.SignedEciesCiphertext".into() } } +/// PrivateKey generalized to support different key types +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct SignedPrivateKey { + /// time the key was created + #[prost(uint64, tag = "1")] + pub created_ns: u64, + /// public key for this private key + #[prost(message, optional, tag = "3")] + pub public_key: ::core::option::Option, + /// private key + #[prost(oneof = "signed_private_key::Union", tags = "2")] + pub union: ::core::option::Option, +} +/// Nested message and enum types in `SignedPrivateKey`. +pub mod signed_private_key { + /// EC: SECP256k1 + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] + pub struct Secp256k1 { + /// D big-endian, 32 bytes + #[prost(bytes = "vec", tag = "1")] + pub bytes: ::prost::alloc::vec::Vec, + } + impl ::prost::Name for Secp256k1 { + const NAME: &'static str = "Secp256k1"; + const PACKAGE: &'static str = "xmtp.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_contents.SignedPrivateKey.Secp256k1".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_contents.SignedPrivateKey.Secp256k1".into() + } + } + /// private key + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] + pub enum Union { + #[prost(message, tag = "2")] + Secp256k1(Secp256k1), + } +} +impl ::prost::Name for SignedPrivateKey { + const NAME: &'static str = "SignedPrivateKey"; + const PACKAGE: &'static str = "xmtp.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_contents.SignedPrivateKey".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_contents.SignedPrivateKey".into() + } +} +/// PrivateKeyBundle wraps the identityKey and the preKeys, +/// enforces usage of signed keys. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PrivateKeyBundleV2 { + #[prost(message, optional, tag = "1")] + pub identity_key: ::core::option::Option, + /// all the known pre-keys, newer keys first, + #[prost(message, repeated, tag = "2")] + pub pre_keys: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for PrivateKeyBundleV2 { + const NAME: &'static str = "PrivateKeyBundleV2"; + const PACKAGE: &'static str = "xmtp.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_contents.PrivateKeyBundleV2".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_contents.PrivateKeyBundleV2".into() + } +} +/// LEGACY: PrivateKey generalized to support different key types +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct PrivateKey { + /// time the key was created + #[prost(uint64, tag = "1")] + pub timestamp: u64, + /// public key for this private key + #[prost(message, optional, tag = "3")] + pub public_key: ::core::option::Option, + /// private key + #[prost(oneof = "private_key::Union", tags = "2")] + pub union: ::core::option::Option, +} +/// Nested message and enum types in `PrivateKey`. +pub mod private_key { + /// EC: SECP256k1 + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] + pub struct Secp256k1 { + /// D big-endian, 32 bytes + #[prost(bytes = "vec", tag = "1")] + pub bytes: ::prost::alloc::vec::Vec, + } + impl ::prost::Name for Secp256k1 { + const NAME: &'static str = "Secp256k1"; + const PACKAGE: &'static str = "xmtp.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_contents.PrivateKey.Secp256k1".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_contents.PrivateKey.Secp256k1".into() + } + } + /// private key + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] + pub enum Union { + #[prost(message, tag = "2")] + Secp256k1(Secp256k1), + } +} +impl ::prost::Name for PrivateKey { + const NAME: &'static str = "PrivateKey"; + const PACKAGE: &'static str = "xmtp.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_contents.PrivateKey".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_contents.PrivateKey".into() + } +} +/// LEGACY: PrivateKeyBundleV1 wraps the identityKey and the preKeys +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PrivateKeyBundleV1 { + #[prost(message, optional, tag = "1")] + pub identity_key: ::core::option::Option, + /// all the known pre-keys, newer keys first, + #[prost(message, repeated, tag = "2")] + pub pre_keys: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for PrivateKeyBundleV1 { + const NAME: &'static str = "PrivateKeyBundleV1"; + const PACKAGE: &'static str = "xmtp.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_contents.PrivateKeyBundleV1".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_contents.PrivateKeyBundleV1".into() + } +} +/// Versioned PrivateKeyBundle +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PrivateKeyBundle { + #[prost(oneof = "private_key_bundle::Version", tags = "1, 2")] + pub version: ::core::option::Option, +} +/// Nested message and enum types in `PrivateKeyBundle`. +pub mod private_key_bundle { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Version { + #[prost(message, tag = "1")] + V1(super::PrivateKeyBundleV1), + #[prost(message, tag = "2")] + V2(super::PrivateKeyBundleV2), + } +} +impl ::prost::Name for PrivateKeyBundle { + const NAME: &'static str = "PrivateKeyBundle"; + const PACKAGE: &'static str = "xmtp.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_contents.PrivateKeyBundle".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_contents.PrivateKeyBundle".into() + } +} +/// PrivateKeyBundle encrypted with key material generated by +/// signing a randomly generated "pre-key" with the user's wallet, +/// i.e. EIP-191 signature of a "storage signature" message with +/// the pre-key embedded in it. +/// (see xmtp-js::PrivateKeyBundle.toEncryptedBytes for details) +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct EncryptedPrivateKeyBundleV1 { + /// randomly generated pre-key + /// + /// 32 bytes + #[prost(bytes = "vec", tag = "1")] + pub wallet_pre_key: ::prost::alloc::vec::Vec, + /// MUST contain encrypted PrivateKeyBundle + #[prost(message, optional, tag = "2")] + pub ciphertext: ::core::option::Option, +} +impl ::prost::Name for EncryptedPrivateKeyBundleV1 { + const NAME: &'static str = "EncryptedPrivateKeyBundleV1"; + const PACKAGE: &'static str = "xmtp.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_contents.EncryptedPrivateKeyBundleV1".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_contents.EncryptedPrivateKeyBundleV1".into() + } +} +/// Versioned encrypted PrivateKeyBundle +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct EncryptedPrivateKeyBundle { + #[prost(oneof = "encrypted_private_key_bundle::Version", tags = "1")] + pub version: ::core::option::Option, +} +/// Nested message and enum types in `EncryptedPrivateKeyBundle`. +pub mod encrypted_private_key_bundle { + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] + pub enum Version { + #[prost(message, tag = "1")] + V1(super::EncryptedPrivateKeyBundleV1), + } +} +impl ::prost::Name for EncryptedPrivateKeyBundle { + const NAME: &'static str = "EncryptedPrivateKeyBundle"; + const PACKAGE: &'static str = "xmtp.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_contents.EncryptedPrivateKeyBundle".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_contents.EncryptedPrivateKeyBundle".into() + } +} /// Unsealed invitation V1 #[derive(Clone, PartialEq, ::prost::Message)] pub struct InvitationV1 { @@ -545,217 +857,180 @@ impl ::prost::Name for ConversationReference { "/xmtp.message_contents.ConversationReference".into() } } -/// PrivateKey generalized to support different key types -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct SignedPrivateKey { - /// time the key was created - #[prost(uint64, tag = "1")] - pub created_ns: u64, - /// public key for this private key - #[prost(message, optional, tag = "3")] - pub public_key: ::core::option::Option, - /// private key - #[prost(oneof = "signed_private_key::Union", tags = "2")] - pub union: ::core::option::Option, -} -/// Nested message and enum types in `SignedPrivateKey`. -pub mod signed_private_key { - /// EC: SECP256k1 - #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] - pub struct Secp256k1 { - /// D big-endian, 32 bytes - #[prost(bytes = "vec", tag = "1")] - pub bytes: ::prost::alloc::vec::Vec, - } - impl ::prost::Name for Secp256k1 { - const NAME: &'static str = "Secp256k1"; - const PACKAGE: &'static str = "xmtp.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.SignedPrivateKey.Secp256k1".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.SignedPrivateKey.Secp256k1".into() - } - } - /// private key - #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] - pub enum Union { - #[prost(message, tag = "2")] - Secp256k1(Secp256k1), - } -} -impl ::prost::Name for SignedPrivateKey { - const NAME: &'static str = "SignedPrivateKey"; - const PACKAGE: &'static str = "xmtp.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.SignedPrivateKey".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.SignedPrivateKey".into() - } -} -/// PrivateKeyBundle wraps the identityKey and the preKeys, -/// enforces usage of signed keys. -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PrivateKeyBundleV2 { - #[prost(message, optional, tag = "1")] - pub identity_key: ::core::option::Option, - /// all the known pre-keys, newer keys first, - #[prost(message, repeated, tag = "2")] - pub pre_keys: ::prost::alloc::vec::Vec, -} -impl ::prost::Name for PrivateKeyBundleV2 { - const NAME: &'static str = "PrivateKeyBundleV2"; - const PACKAGE: &'static str = "xmtp.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.PrivateKeyBundleV2".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.PrivateKeyBundleV2".into() - } -} -/// LEGACY: PrivateKey generalized to support different key types +/// EciesMessage is a wrapper for ECIES encrypted payloads #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct PrivateKey { - /// time the key was created - #[prost(uint64, tag = "1")] - pub timestamp: u64, - /// public key for this private key - #[prost(message, optional, tag = "3")] - pub public_key: ::core::option::Option, - /// private key - #[prost(oneof = "private_key::Union", tags = "2")] - pub union: ::core::option::Option, +pub struct EciesMessage { + #[prost(oneof = "ecies_message::Version", tags = "1")] + pub version: ::core::option::Option, } -/// Nested message and enum types in `PrivateKey`. -pub mod private_key { - /// EC: SECP256k1 - #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] - pub struct Secp256k1 { - /// D big-endian, 32 bytes - #[prost(bytes = "vec", tag = "1")] - pub bytes: ::prost::alloc::vec::Vec, - } - impl ::prost::Name for Secp256k1 { - const NAME: &'static str = "Secp256k1"; - const PACKAGE: &'static str = "xmtp.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.PrivateKey.Secp256k1".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.PrivateKey.Secp256k1".into() - } - } - /// private key +/// Nested message and enum types in `EciesMessage`. +pub mod ecies_message { #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] - pub enum Union { - #[prost(message, tag = "2")] - Secp256k1(Secp256k1), + pub enum Version { + /// Expected to be an ECIES encrypted SignedPayload + #[prost(bytes, tag = "1")] + V1(::prost::alloc::vec::Vec), } } -impl ::prost::Name for PrivateKey { - const NAME: &'static str = "PrivateKey"; +impl ::prost::Name for EciesMessage { + const NAME: &'static str = "EciesMessage"; const PACKAGE: &'static str = "xmtp.message_contents"; fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.PrivateKey".into() + "xmtp.message_contents.EciesMessage".into() } fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.PrivateKey".into() + "/xmtp.message_contents.EciesMessage".into() } } -/// LEGACY: PrivateKeyBundleV1 wraps the identityKey and the preKeys -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PrivateKeyBundleV1 { +/// Message header is encoded separately as the bytes are also used +/// as associated data for authenticated encryption +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct MessageHeaderV1 { #[prost(message, optional, tag = "1")] - pub identity_key: ::core::option::Option, - /// all the known pre-keys, newer keys first, - #[prost(message, repeated, tag = "2")] - pub pre_keys: ::prost::alloc::vec::Vec, + pub sender: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub recipient: ::core::option::Option, + #[prost(uint64, tag = "3")] + pub timestamp: u64, } -impl ::prost::Name for PrivateKeyBundleV1 { - const NAME: &'static str = "PrivateKeyBundleV1"; +impl ::prost::Name for MessageHeaderV1 { + const NAME: &'static str = "MessageHeaderV1"; const PACKAGE: &'static str = "xmtp.message_contents"; fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.PrivateKeyBundleV1".into() + "xmtp.message_contents.MessageHeaderV1".into() } fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.PrivateKeyBundleV1".into() + "/xmtp.message_contents.MessageHeaderV1".into() } } -/// Versioned PrivateKeyBundle -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PrivateKeyBundle { - #[prost(oneof = "private_key_bundle::Version", tags = "1, 2")] - pub version: ::core::option::Option, +/// Message is the top level protocol element +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct MessageV1 { + /// encapsulates encoded MessageHeaderV1 + #[prost(bytes = "vec", tag = "1")] + pub header_bytes: ::prost::alloc::vec::Vec, + /// Ciphertext.payload MUST contain encrypted EncodedContent + #[prost(message, optional, tag = "2")] + pub ciphertext: ::core::option::Option, } -/// Nested message and enum types in `PrivateKeyBundle`. -pub mod private_key_bundle { - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Version { - #[prost(message, tag = "1")] - V1(super::PrivateKeyBundleV1), - #[prost(message, tag = "2")] - V2(super::PrivateKeyBundleV2), +impl ::prost::Name for MessageV1 { + const NAME: &'static str = "MessageV1"; + const PACKAGE: &'static str = "xmtp.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_contents.MessageV1".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_contents.MessageV1".into() } } -impl ::prost::Name for PrivateKeyBundle { - const NAME: &'static str = "PrivateKeyBundle"; +/// Message header carries information that is not encrypted, and is therefore +/// observable by the network. It is however authenticated as associated data +/// of the AEAD encryption used to protect the message, +/// thus providing tamper evidence. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct MessageHeaderV2 { + /// sender specified message creation time + #[prost(uint64, tag = "1")] + pub created_ns: u64, + /// the topic the message belongs to + #[prost(string, tag = "2")] + pub topic: ::prost::alloc::string::String, +} +impl ::prost::Name for MessageHeaderV2 { + const NAME: &'static str = "MessageHeaderV2"; const PACKAGE: &'static str = "xmtp.message_contents"; fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.PrivateKeyBundle".into() + "xmtp.message_contents.MessageHeaderV2".into() } fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.PrivateKeyBundle".into() + "/xmtp.message_contents.MessageHeaderV2".into() } } -/// PrivateKeyBundle encrypted with key material generated by -/// signing a randomly generated "pre-key" with the user's wallet, -/// i.e. EIP-191 signature of a "storage signature" message with -/// the pre-key embedded in it. -/// (see xmtp-js::PrivateKeyBundle.toEncryptedBytes for details) +/// Message combines the encoded header with the encrypted payload. #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct EncryptedPrivateKeyBundleV1 { - /// randomly generated pre-key - /// - /// 32 bytes +pub struct MessageV2 { + /// encapsulates encoded MessageHeaderV2 #[prost(bytes = "vec", tag = "1")] - pub wallet_pre_key: ::prost::alloc::vec::Vec, - /// MUST contain encrypted PrivateKeyBundle + pub header_bytes: ::prost::alloc::vec::Vec, + /// Ciphertext.payload MUST contain encrypted SignedContent #[prost(message, optional, tag = "2")] pub ciphertext: ::core::option::Option, + /// HMAC of the message ciphertext, with the HMAC key derived from the topic + /// key + #[prost(bytes = "vec", optional, tag = "3")] + pub sender_hmac: ::core::option::Option<::prost::alloc::vec::Vec>, + /// Flag indicating whether the message should be pushed from a notification + /// server + #[prost(bool, optional, tag = "4")] + pub should_push: ::core::option::Option, } -impl ::prost::Name for EncryptedPrivateKeyBundleV1 { - const NAME: &'static str = "EncryptedPrivateKeyBundleV1"; +impl ::prost::Name for MessageV2 { + const NAME: &'static str = "MessageV2"; const PACKAGE: &'static str = "xmtp.message_contents"; fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.EncryptedPrivateKeyBundleV1".into() + "xmtp.message_contents.MessageV2".into() } fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.EncryptedPrivateKeyBundleV1".into() + "/xmtp.message_contents.MessageV2".into() } } -/// Versioned encrypted PrivateKeyBundle +/// Versioned Message #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct EncryptedPrivateKeyBundle { - #[prost(oneof = "encrypted_private_key_bundle::Version", tags = "1")] - pub version: ::core::option::Option, +pub struct Message { + #[prost(oneof = "message::Version", tags = "1, 2")] + pub version: ::core::option::Option, } -/// Nested message and enum types in `EncryptedPrivateKeyBundle`. -pub mod encrypted_private_key_bundle { +/// Nested message and enum types in `Message`. +pub mod message { #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] pub enum Version { #[prost(message, tag = "1")] - V1(super::EncryptedPrivateKeyBundleV1), + V1(super::MessageV1), + #[prost(message, tag = "2")] + V2(super::MessageV2), } } -impl ::prost::Name for EncryptedPrivateKeyBundle { - const NAME: &'static str = "EncryptedPrivateKeyBundle"; +impl ::prost::Name for Message { + const NAME: &'static str = "Message"; const PACKAGE: &'static str = "xmtp.message_contents"; fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.EncryptedPrivateKeyBundle".into() + "xmtp.message_contents.Message".into() } fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.EncryptedPrivateKeyBundle".into() + "/xmtp.message_contents.Message".into() + } +} +/// DecodedMessage represents the decrypted message contents. +/// DecodedMessage instances are not stored on the network, but +/// may be serialized and stored by clients +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DecodedMessage { + #[prost(string, tag = "1")] + pub id: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub message_version: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub sender_address: ::prost::alloc::string::String, + #[prost(string, optional, tag = "4")] + pub recipient_address: ::core::option::Option<::prost::alloc::string::String>, + #[prost(uint64, tag = "5")] + pub sent_ns: u64, + #[prost(string, tag = "6")] + pub content_topic: ::prost::alloc::string::String, + #[prost(message, optional, tag = "7")] + pub conversation: ::core::option::Option, + /// encapsulates EncodedContent + #[prost(bytes = "vec", tag = "8")] + pub content_bytes: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for DecodedMessage { + const NAME: &'static str = "DecodedMessage"; + const PACKAGE: &'static str = "xmtp.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.message_contents.DecodedMessage".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.message_contents.DecodedMessage".into() } } /// PrivatePreferencesAction is a message used to update the client's preference @@ -912,95 +1187,14 @@ pub mod private_preferences_payload { V1(super::Ciphertext), } } -impl ::prost::Name for PrivatePreferencesPayload { - const NAME: &'static str = "PrivatePreferencesPayload"; - const PACKAGE: &'static str = "xmtp.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.PrivatePreferencesPayload".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.PrivatePreferencesPayload".into() - } -} -/// The message that will be signed by the Client and returned inside the -/// `action_body` field of the FrameAction message -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct FrameActionBody { - /// The URL of the frame that was clicked - /// May be different from `post_url` - #[prost(string, tag = "1")] - pub frame_url: ::prost::alloc::string::String, - /// The 1-indexed button that was clicked - #[prost(int32, tag = "2")] - pub button_index: i32, - /// Timestamp of the click in milliseconds since the epoch - #[deprecated] - #[prost(uint64, tag = "3")] - pub timestamp: u64, - /// A unique identifier for the conversation, not tied to anything on the - /// network. Will not match the topic or conversation_id - #[prost(string, tag = "4")] - pub opaque_conversation_identifier: ::prost::alloc::string::String, - /// Unix timestamp - #[prost(uint32, tag = "5")] - pub unix_timestamp: u32, - /// Input text from a text input field - #[prost(string, tag = "6")] - pub input_text: ::prost::alloc::string::String, - /// A state serialized to a string (for example via JSON.stringify()). Maximum 4096 bytes. - #[prost(string, tag = "7")] - pub state: ::prost::alloc::string::String, - /// A 0x wallet address - #[prost(string, tag = "8")] - pub address: ::prost::alloc::string::String, - /// A hash from a transaction - #[prost(string, tag = "9")] - pub transaction_id: ::prost::alloc::string::String, -} -impl ::prost::Name for FrameActionBody { - const NAME: &'static str = "FrameActionBody"; - const PACKAGE: &'static str = "xmtp.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.FrameActionBody".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.FrameActionBody".into() - } -} -/// The outer payload that will be sent as the `messageBytes` in the -/// `trusted_data` part of the Frames message -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct FrameAction { - #[deprecated] - #[prost(message, optional, tag = "1")] - pub signature: ::core::option::Option, - /// The SignedPublicKeyBundle of the signer, used to link the XMTP signature - /// with a blockchain account through a chain of signatures. - #[deprecated] - #[prost(message, optional, tag = "2")] - pub signed_public_key_bundle: ::core::option::Option, - /// Serialized FrameActionBody message, so that the signature verification can - /// happen on a byte-perfect representation of the message - #[prost(bytes = "vec", tag = "3")] - pub action_body: ::prost::alloc::vec::Vec, - /// The installation signature - #[prost(bytes = "vec", tag = "4")] - pub installation_signature: ::prost::alloc::vec::Vec, - /// The public installation id used to sign. - #[prost(bytes = "vec", tag = "5")] - pub installation_id: ::prost::alloc::vec::Vec, - /// The inbox id of the installation used to sign. - #[prost(string, tag = "6")] - pub inbox_id: ::prost::alloc::string::String, -} -impl ::prost::Name for FrameAction { - const NAME: &'static str = "FrameAction"; +impl ::prost::Name for PrivatePreferencesPayload { + const NAME: &'static str = "PrivatePreferencesPayload"; const PACKAGE: &'static str = "xmtp.message_contents"; fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.FrameAction".into() + "xmtp.message_contents.PrivatePreferencesPayload".into() } fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.FrameAction".into() + "/xmtp.message_contents.PrivatePreferencesPayload".into() } } /// ContentTypeId is used to identify the type of content stored in a Message. @@ -1117,83 +1311,6 @@ impl Compression { } } } -/// LEGACY: User key bundle V1 using PublicKeys. -/// The PublicKeys MUST be signed. -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct ContactBundleV1 { - #[prost(message, optional, tag = "1")] - pub key_bundle: ::core::option::Option, -} -impl ::prost::Name for ContactBundleV1 { - const NAME: &'static str = "ContactBundleV1"; - const PACKAGE: &'static str = "xmtp.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.ContactBundleV1".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.ContactBundleV1".into() - } -} -/// User key bundle V2 using SignedPublicKeys. -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct ContactBundleV2 { - #[prost(message, optional, tag = "1")] - pub key_bundle: ::core::option::Option, -} -impl ::prost::Name for ContactBundleV2 { - const NAME: &'static str = "ContactBundleV2"; - const PACKAGE: &'static str = "xmtp.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.ContactBundleV2".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.ContactBundleV2".into() - } -} -/// Versioned ContactBundle -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct ContactBundle { - #[prost(oneof = "contact_bundle::Version", tags = "1, 2")] - pub version: ::core::option::Option, -} -/// Nested message and enum types in `ContactBundle`. -pub mod contact_bundle { - #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] - pub enum Version { - #[prost(message, tag = "1")] - V1(super::ContactBundleV1), - #[prost(message, tag = "2")] - V2(super::ContactBundleV2), - } -} -impl ::prost::Name for ContactBundle { - const NAME: &'static str = "ContactBundle"; - const PACKAGE: &'static str = "xmtp.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.ContactBundle".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.ContactBundle".into() - } -} -/// SignedPayload is a wrapper for a signature and a payload -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct SignedPayload { - #[prost(bytes = "vec", tag = "1")] - pub payload: ::prost::alloc::vec::Vec, - #[prost(message, optional, tag = "2")] - pub signature: ::core::option::Option, -} -impl ::prost::Name for SignedPayload { - const NAME: &'static str = "SignedPayload"; - const PACKAGE: &'static str = "xmtp.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.SignedPayload".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.SignedPayload".into() - } -} /// Composite is used to implement xmtp.org/composite content type #[derive(Clone, PartialEq, ::prost::Message)] pub struct Composite { @@ -1239,179 +1356,62 @@ impl ::prost::Name for Composite { "/xmtp.message_contents.Composite".into() } } -/// Message header is encoded separately as the bytes are also used -/// as associated data for authenticated encryption +/// LEGACY: User key bundle V1 using PublicKeys. +/// The PublicKeys MUST be signed. #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct MessageHeaderV1 { +pub struct ContactBundleV1 { #[prost(message, optional, tag = "1")] - pub sender: ::core::option::Option, - #[prost(message, optional, tag = "2")] - pub recipient: ::core::option::Option, - #[prost(uint64, tag = "3")] - pub timestamp: u64, -} -impl ::prost::Name for MessageHeaderV1 { - const NAME: &'static str = "MessageHeaderV1"; - const PACKAGE: &'static str = "xmtp.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.MessageHeaderV1".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.MessageHeaderV1".into() - } -} -/// Message is the top level protocol element -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct MessageV1 { - /// encapsulates encoded MessageHeaderV1 - #[prost(bytes = "vec", tag = "1")] - pub header_bytes: ::prost::alloc::vec::Vec, - /// Ciphertext.payload MUST contain encrypted EncodedContent - #[prost(message, optional, tag = "2")] - pub ciphertext: ::core::option::Option, -} -impl ::prost::Name for MessageV1 { - const NAME: &'static str = "MessageV1"; - const PACKAGE: &'static str = "xmtp.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.MessageV1".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.MessageV1".into() - } -} -/// Message header carries information that is not encrypted, and is therefore -/// observable by the network. It is however authenticated as associated data -/// of the AEAD encryption used to protect the message, -/// thus providing tamper evidence. -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct MessageHeaderV2 { - /// sender specified message creation time - #[prost(uint64, tag = "1")] - pub created_ns: u64, - /// the topic the message belongs to - #[prost(string, tag = "2")] - pub topic: ::prost::alloc::string::String, + pub key_bundle: ::core::option::Option, } -impl ::prost::Name for MessageHeaderV2 { - const NAME: &'static str = "MessageHeaderV2"; +impl ::prost::Name for ContactBundleV1 { + const NAME: &'static str = "ContactBundleV1"; const PACKAGE: &'static str = "xmtp.message_contents"; fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.MessageHeaderV2".into() + "xmtp.message_contents.ContactBundleV1".into() } fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.MessageHeaderV2".into() + "/xmtp.message_contents.ContactBundleV1".into() } } -/// Message combines the encoded header with the encrypted payload. +/// User key bundle V2 using SignedPublicKeys. #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct MessageV2 { - /// encapsulates encoded MessageHeaderV2 - #[prost(bytes = "vec", tag = "1")] - pub header_bytes: ::prost::alloc::vec::Vec, - /// Ciphertext.payload MUST contain encrypted SignedContent - #[prost(message, optional, tag = "2")] - pub ciphertext: ::core::option::Option, - /// HMAC of the message ciphertext, with the HMAC key derived from the topic - /// key - #[prost(bytes = "vec", optional, tag = "3")] - pub sender_hmac: ::core::option::Option<::prost::alloc::vec::Vec>, - /// Flag indicating whether the message should be pushed from a notification - /// server - #[prost(bool, optional, tag = "4")] - pub should_push: ::core::option::Option, +pub struct ContactBundleV2 { + #[prost(message, optional, tag = "1")] + pub key_bundle: ::core::option::Option, } -impl ::prost::Name for MessageV2 { - const NAME: &'static str = "MessageV2"; +impl ::prost::Name for ContactBundleV2 { + const NAME: &'static str = "ContactBundleV2"; const PACKAGE: &'static str = "xmtp.message_contents"; fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.MessageV2".into() + "xmtp.message_contents.ContactBundleV2".into() } fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.MessageV2".into() + "/xmtp.message_contents.ContactBundleV2".into() } } -/// Versioned Message +/// Versioned ContactBundle #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct Message { - #[prost(oneof = "message::Version", tags = "1, 2")] - pub version: ::core::option::Option, +pub struct ContactBundle { + #[prost(oneof = "contact_bundle::Version", tags = "1, 2")] + pub version: ::core::option::Option, } -/// Nested message and enum types in `Message`. -pub mod message { +/// Nested message and enum types in `ContactBundle`. +pub mod contact_bundle { #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] pub enum Version { #[prost(message, tag = "1")] - V1(super::MessageV1), + V1(super::ContactBundleV1), #[prost(message, tag = "2")] - V2(super::MessageV2), - } -} -impl ::prost::Name for Message { - const NAME: &'static str = "Message"; - const PACKAGE: &'static str = "xmtp.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.Message".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.Message".into() - } -} -/// DecodedMessage represents the decrypted message contents. -/// DecodedMessage instances are not stored on the network, but -/// may be serialized and stored by clients -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct DecodedMessage { - #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub message_version: ::prost::alloc::string::String, - #[prost(string, tag = "3")] - pub sender_address: ::prost::alloc::string::String, - #[prost(string, optional, tag = "4")] - pub recipient_address: ::core::option::Option<::prost::alloc::string::String>, - #[prost(uint64, tag = "5")] - pub sent_ns: u64, - #[prost(string, tag = "6")] - pub content_topic: ::prost::alloc::string::String, - #[prost(message, optional, tag = "7")] - pub conversation: ::core::option::Option, - /// encapsulates EncodedContent - #[prost(bytes = "vec", tag = "8")] - pub content_bytes: ::prost::alloc::vec::Vec, -} -impl ::prost::Name for DecodedMessage { - const NAME: &'static str = "DecodedMessage"; - const PACKAGE: &'static str = "xmtp.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.DecodedMessage".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.DecodedMessage".into() - } -} -/// EciesMessage is a wrapper for ECIES encrypted payloads -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct EciesMessage { - #[prost(oneof = "ecies_message::Version", tags = "1")] - pub version: ::core::option::Option, -} -/// Nested message and enum types in `EciesMessage`. -pub mod ecies_message { - #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] - pub enum Version { - /// Expected to be an ECIES encrypted SignedPayload - #[prost(bytes, tag = "1")] - V1(::prost::alloc::vec::Vec), + V2(super::ContactBundleV2), } } -impl ::prost::Name for EciesMessage { - const NAME: &'static str = "EciesMessage"; +impl ::prost::Name for ContactBundle { + const NAME: &'static str = "ContactBundle"; const PACKAGE: &'static str = "xmtp.message_contents"; fn full_name() -> ::prost::alloc::string::String { - "xmtp.message_contents.EciesMessage".into() + "xmtp.message_contents.ContactBundle".into() } fn type_url() -> ::prost::alloc::string::String { - "/xmtp.message_contents.EciesMessage".into() + "/xmtp.message_contents.ContactBundle".into() } } diff --git a/crates/xmtp_proto/src/gen/xmtp.mls.api.v1.rs b/crates/xmtp_proto/src/gen/xmtp.mls.api.v1.rs index 8205bd5643..84d1d01754 100644 --- a/crates/xmtp_proto/src/gen/xmtp.mls.api.v1.rs +++ b/crates/xmtp_proto/src/gen/xmtp.mls.api.v1.rs @@ -762,6 +762,345 @@ impl ::prost::Name for SubscribeWelcomeMessagesRequest { "/xmtp.mls.api.v1.SubscribeWelcomeMessagesRequest".into() } } +/// Client -> server. Sent one or more times over the life of the stream. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SubscribeRequest { + #[prost(oneof = "subscribe_request::Version", tags = "1")] + pub version: ::core::option::Option, +} +/// Nested message and enum types in `SubscribeRequest`. +pub mod subscribe_request { + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct V1 { + /// Each frame is exactly one of: a mutation, a Ping, or a Pong. + #[prost(oneof = "v1::Request", tags = "1, 2, 3")] + pub request: ::core::option::Option, + } + /// Nested message and enum types in `V1`. + pub mod v1 { + /// Add and/or remove subscriptions in place (applied atomically per frame). + /// Topics use the kind-prefixed binary representation shared with the + /// decentralized backend (XIP-49 §3.3.2): the first byte is the topic kind, + /// the remainder is the identifier. This RPC initially serves + /// TOPIC_KIND_GROUP_MESSAGES_V1 (0x00, identifier = group_id) and + /// TOPIC_KIND_WELCOME_MESSAGES_V1 (0x01, identifier = installation_key); + /// a topic whose kind the node does not serve fails the stream with + /// INVALID_ARGUMENT. Future kinds (key packages, identity updates) are + /// adopted via the capabilities advertised on Started. + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Mutate { + /// begin delivering these topics + #[prost(message, repeated, tag = "1")] + pub adds: ::prost::alloc::vec::Vec, + /// topics to stop delivering + #[prost(bytes = "vec", repeated, tag = "2")] + pub removes: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + /// Catch this Mutate's adds up to the live edge — history, TopicsLive + /// markers, and the wave's CatchupComplete — but do NOT register them + /// for live delivery. The markers then mean "you have everything as of + /// now". Combined with half-closing the request stream, this is the + /// bounded catch-up ("sync") mode: the server finishes the wave and then + /// closes the stream itself. Removals in the Mutate are unaffected. + #[prost(bool, tag = "3")] + pub history_only: bool, + /// Client-chosen correlation id, echoed on this wave's CatchupComplete + /// so completions are attributable when waves overlap. SHOULD be unique + /// per stream; 0 = no correlation requested (still echoed as 0). + #[prost(uint64, tag = "4")] + pub mutate_id: u64, + } + /// Nested message and enum types in `Mutate`. + pub mod mutate { + /// A topic to subscribe, with the cursor to resume from. + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] + pub struct Subscription { + #[prost(bytes = "vec", tag = "1")] + pub topic: ::prost::alloc::vec::Vec, + /// Deliver ids greater than this; 0 = from the beginning. For a newly + /// joined group, a client SHOULD seed this from the welcome's encrypted + /// WelcomeMetadata.message_cursor so a new membership does not refetch + /// pre-join history it cannot decrypt; for a new installation's welcome + /// topic, 0 is how pending welcomes are collected. + #[prost(uint64, tag = "2")] + pub id_cursor: u64, + } + impl ::prost::Name for Subscription { + const NAME: &'static str = "Subscription"; + const PACKAGE: &'static str = "xmtp.mls.api.v1"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.api.v1.SubscribeRequest.V1.Mutate.Subscription".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.api.v1.SubscribeRequest.V1.Mutate.Subscription".into() + } + } + } + impl ::prost::Name for Mutate { + const NAME: &'static str = "Mutate"; + const PACKAGE: &'static str = "xmtp.mls.api.v1"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.api.v1.SubscribeRequest.V1.Mutate".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.api.v1.SubscribeRequest.V1.Mutate".into() + } + } + /// Each frame is exactly one of: a mutation, a Ping, or a Pong. + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Request { + #[prost(message, tag = "1")] + Mutate(Mutate), + /// liveness challenge (e.g. probe the link after resuming) + #[prost(message, tag = "2")] + Ping(super::super::Ping), + /// answer to a server Ping + #[prost(message, tag = "3")] + Pong(super::super::Pong), + } + } + impl ::prost::Name for V1 { + const NAME: &'static str = "V1"; + const PACKAGE: &'static str = "xmtp.mls.api.v1"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.api.v1.SubscribeRequest.V1".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.api.v1.SubscribeRequest.V1".into() + } + } + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Version { + #[prost(message, tag = "1")] + V1(V1), + } +} +impl ::prost::Name for SubscribeRequest { + const NAME: &'static str = "SubscribeRequest"; + const PACKAGE: &'static str = "xmtp.mls.api.v1"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.api.v1.SubscribeRequest".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.api.v1.SubscribeRequest".into() + } +} +/// Server -> client. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SubscribeResponse { + #[prost(oneof = "subscribe_response::Version", tags = "1")] + pub version: ::core::option::Option, +} +/// Nested message and enum types in `SubscribeResponse`. +pub mod subscribe_response { + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct V1 { + #[prost(oneof = "v1::Response", tags = "1, 2, 3, 4, 5, 6")] + pub response: ::core::option::Option, + } + /// Nested message and enum types in `V1`. + pub mod v1 { + /// A batch of new messages; group and welcome messages share the stream, + /// depending on which subscriptions are active. + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Messages { + #[prost(message, repeated, tag = "1")] + pub group_messages: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub welcome_messages: ::prost::alloc::vec::Vec, + } + impl ::prost::Name for Messages { + const NAME: &'static str = "Messages"; + const PACKAGE: &'static str = "xmtp.mls.api.v1"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.api.v1.SubscribeResponse.V1.Messages".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.api.v1.SubscribeResponse.V1.Messages".into() + } + } + /// The first frame on every stream. + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] + pub struct Started { + /// The server's ping cadence (ms): the basis for the client's staleness + /// threshold and the server's reap deadline. + #[prost(uint32, tag = "1")] + pub keepalive_interval_ms: u32, + /// Optional protocol features the node supports on this stream. The node + /// silently ignores request types it does not understand, so a client + /// MUST NOT send an optional request type whose capability the node did + /// not advertise (it would hang waiting on a response that never comes). + #[prost(enumeration = "Capability", repeated, tag = "2")] + pub capabilities: ::prost::alloc::vec::Vec, + } + impl ::prost::Name for Started { + const NAME: &'static str = "Started"; + const PACKAGE: &'static str = "xmtp.mls.api.v1"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.api.v1.SubscribeResponse.V1.Started".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.api.v1.SubscribeResponse.V1.Started".into() + } + } + /// Sent once per Mutate that adds subscriptions (a catch-up "wave"), after + /// the wave's last TopicsLive: everything the Mutate asked for is delivered. + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] + pub struct CatchupComplete { + /// echoes the Mutate that started this wave (0 if none given) + #[prost(uint64, tag = "1")] + pub mutate_id: u64, + } + impl ::prost::Name for CatchupComplete { + const NAME: &'static str = "CatchupComplete"; + const PACKAGE: &'static str = "xmtp.mls.api.v1"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.api.v1.SubscribeResponse.V1.CatchupComplete".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.api.v1.SubscribeResponse.V1.CatchupComplete".into() + } + } + /// Emitted when topics finish catch-up, AFTER the last history frame for + /// them — including any live messages that queued up behind the catch-up, + /// which were equally historical from the client's perspective — so every + /// later frame for a listed topic is live tail. Informational only: delivery + /// correctness (no duplicates, no gaps) never depends on it. Re-adding a + /// topic re-runs catch-up and re-emits it; receivers treat it idempotently. + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] + pub struct TopicsLive { + /// kind-prefixed topics now tailing live + #[prost(bytes = "vec", repeated, tag = "1")] + pub topics: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + } + impl ::prost::Name for TopicsLive { + const NAME: &'static str = "TopicsLive"; + const PACKAGE: &'static str = "xmtp.mls.api.v1"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.api.v1.SubscribeResponse.V1.TopicsLive".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.api.v1.SubscribeResponse.V1.TopicsLive".into() + } + } + /// Optional per-stream protocol features (none defined yet; future + /// revisions add values, e.g. fetch-over-stream lookups answered with the + /// same read view that feeds the stream, or new streamable topic kinds). + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum Capability { + Unspecified = 0, + } + impl Capability { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "CAPABILITY_UNSPECIFIED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "CAPABILITY_UNSPECIFIED" => Some(Self::Unspecified), + _ => None, + } + } + } + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Response { + #[prost(message, tag = "1")] + Messages(Messages), + /// sent once, immediately on open, before any catch-up + #[prost(message, tag = "2")] + Started(Started), + /// idle liveness challenge; receiver MUST answer with Pong + #[prost(message, tag = "3")] + Ping(super::super::Ping), + /// answer to a client Ping + #[prost(message, tag = "4")] + Pong(super::super::Pong), + /// these topics just crossed from catch-up to live + #[prost(message, tag = "5")] + TopicsLive(TopicsLive), + /// a Mutate's adds are fully delivered + #[prost(message, tag = "6")] + CatchupComplete(CatchupComplete), + } + } + impl ::prost::Name for V1 { + const NAME: &'static str = "V1"; + const PACKAGE: &'static str = "xmtp.mls.api.v1"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.api.v1.SubscribeResponse.V1".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.api.v1.SubscribeResponse.V1".into() + } + } + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Version { + #[prost(message, tag = "1")] + V1(V1), + } +} +impl ::prost::Name for SubscribeResponse { + const NAME: &'static str = "SubscribeResponse"; + const PACKAGE: &'static str = "xmtp.mls.api.v1"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.api.v1.SubscribeResponse".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.api.v1.SubscribeResponse".into() + } +} +/// Liveness challenge/response, shared across versions. Either peer MAY send a +/// Ping; the receiver MUST reply with a Pong echoing the nonce. The sender closes +/// the stream if no Pong arrives within its deadline — how a node reaps a vanished +/// peer (e.g. a mobile client the OS suspended behind a proxy that still ACKs the +/// transport). +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Ping { + #[prost(uint64, tag = "1")] + pub nonce: u64, +} +impl ::prost::Name for Ping { + const NAME: &'static str = "Ping"; + const PACKAGE: &'static str = "xmtp.mls.api.v1"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.api.v1.Ping".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.api.v1.Ping".into() + } +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Pong { + /// echoes the nonce of the Ping it answers + #[prost(uint64, tag = "1")] + pub nonce: u64, +} +impl ::prost::Name for Pong { + const NAME: &'static str = "Pong"; + const PACKAGE: &'static str = "xmtp.mls.api.v1"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.api.v1.Pong".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.api.v1.Pong".into() + } +} #[derive(Clone, PartialEq, ::prost::Message)] pub struct BatchPublishCommitLogRequest { #[prost(message, repeated, tag = "1")] @@ -1057,6 +1396,20 @@ pub mod mls_api_server { tonic::Response, tonic::Status, >; + /// Server streaming response type for the Subscribe method. + type SubscribeStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result, + > + + std::marker::Send + + 'static; + /// Bidirectional subscription (XIP-83). One long-lived stream the client mutates + /// in place via add/remove topic deltas, with WebSocket-style liveness ping/pong. + /// A single stream MAY carry both group-message and welcome topics. + /// gRPC-only: bidirectional streaming has no HTTP/grpc-gateway mapping. + async fn subscribe( + &self, + request: tonic::Request>, + ) -> std::result::Result, tonic::Status>; async fn batch_publish_commit_log( &self, request: tonic::Request, @@ -1656,6 +2009,54 @@ pub mod mls_api_server { }; Box::pin(fut) } + "/xmtp.mls.api.v1.MlsApi/Subscribe" => { + #[allow(non_camel_case_types)] + struct SubscribeSvc(pub Arc); + impl< + T: MlsApi, + > tonic::server::StreamingService + for SubscribeSvc { + type Response = super::SubscribeResponse; + type ResponseStream = T::SubscribeStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + tonic::Streaming, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::subscribe(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = SubscribeSvc(inner); + let codec = tonic_prost::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } "/xmtp.mls.api.v1.MlsApi/BatchPublishCommitLog" => { #[allow(non_camel_case_types)] struct BatchPublishCommitLogSvc(pub Arc); diff --git a/crates/xmtp_proto/src/gen/xmtp.mls.api.v1.serde.rs b/crates/xmtp_proto/src/gen/xmtp.mls.api.v1.serde.rs index 86ca7f1ab0..6526357791 100644 --- a/crates/xmtp_proto/src/gen/xmtp.mls.api.v1.serde.rs +++ b/crates/xmtp_proto/src/gen/xmtp.mls.api.v1.serde.rs @@ -2352,6 +2352,204 @@ impl<'de> serde::Deserialize<'de> for PagingInfo { deserializer.deserialize_struct("xmtp.mls.api.v1.PagingInfo", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for Ping { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.nonce != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.Ping", len)?; + if self.nonce != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("nonce", ToString::to_string(&self.nonce).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for Ping { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "nonce", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Nonce, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "nonce" => Ok(GeneratedField::Nonce), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = Ping; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.api.v1.Ping") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut nonce__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Nonce => { + if nonce__.is_some() { + return Err(serde::de::Error::duplicate_field("nonce")); + } + nonce__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(Ping { + nonce: nonce__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.mls.api.v1.Ping", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for Pong { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.nonce != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.Pong", len)?; + if self.nonce != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("nonce", ToString::to_string(&self.nonce).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for Pong { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "nonce", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Nonce, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "nonce" => Ok(GeneratedField::Nonce), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = Pong; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.api.v1.Pong") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut nonce__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Nonce => { + if nonce__.is_some() { + return Err(serde::de::Error::duplicate_field("nonce")); + } + nonce__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(Pong { + nonce: nonce__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.mls.api.v1.Pong", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for PublishCommitLogRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -4020,6 +4218,1274 @@ impl<'de> serde::Deserialize<'de> for subscribe_group_messages_request::Filter { deserializer.deserialize_struct("xmtp.mls.api.v1.SubscribeGroupMessagesRequest.Filter", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for SubscribeRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.version.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.SubscribeRequest", len)?; + if let Some(v) = self.version.as_ref() { + match v { + subscribe_request::Version::V1(v) => { + struct_ser.serialize_field("v1", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for SubscribeRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "v1", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + V1, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "v1" => Ok(GeneratedField::V1), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = SubscribeRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.api.v1.SubscribeRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut version__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::V1 => { + if version__.is_some() { + return Err(serde::de::Error::duplicate_field("v1")); + } + version__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_request::Version::V1) +; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(SubscribeRequest { + version: version__, + }) + } + } + deserializer.deserialize_struct("xmtp.mls.api.v1.SubscribeRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_request::V1 { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.request.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.SubscribeRequest.V1", len)?; + if let Some(v) = self.request.as_ref() { + match v { + subscribe_request::v1::Request::Mutate(v) => { + struct_ser.serialize_field("mutate", v)?; + } + subscribe_request::v1::Request::Ping(v) => { + struct_ser.serialize_field("ping", v)?; + } + subscribe_request::v1::Request::Pong(v) => { + struct_ser.serialize_field("pong", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_request::V1 { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "mutate", + "ping", + "pong", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Mutate, + Ping, + Pong, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "mutate" => Ok(GeneratedField::Mutate), + "ping" => Ok(GeneratedField::Ping), + "pong" => Ok(GeneratedField::Pong), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_request::V1; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.api.v1.SubscribeRequest.V1") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut request__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Mutate => { + if request__.is_some() { + return Err(serde::de::Error::duplicate_field("mutate")); + } + request__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_request::v1::Request::Mutate) +; + } + GeneratedField::Ping => { + if request__.is_some() { + return Err(serde::de::Error::duplicate_field("ping")); + } + request__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_request::v1::Request::Ping) +; + } + GeneratedField::Pong => { + if request__.is_some() { + return Err(serde::de::Error::duplicate_field("pong")); + } + request__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_request::v1::Request::Pong) +; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_request::V1 { + request: request__, + }) + } + } + deserializer.deserialize_struct("xmtp.mls.api.v1.SubscribeRequest.V1", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_request::v1::Mutate { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.adds.is_empty() { + len += 1; + } + if !self.removes.is_empty() { + len += 1; + } + if self.history_only { + len += 1; + } + if self.mutate_id != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.SubscribeRequest.V1.Mutate", len)?; + if !self.adds.is_empty() { + struct_ser.serialize_field("adds", &self.adds)?; + } + if !self.removes.is_empty() { + struct_ser.serialize_field("removes", &self.removes.iter().map(pbjson::private::base64::encode).collect::>())?; + } + if self.history_only { + struct_ser.serialize_field("history_only", &self.history_only)?; + } + if self.mutate_id != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("mutate_id", ToString::to_string(&self.mutate_id).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_request::v1::Mutate { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "adds", + "removes", + "history_only", + "historyOnly", + "mutate_id", + "mutateId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Adds, + Removes, + HistoryOnly, + MutateId, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "adds" => Ok(GeneratedField::Adds), + "removes" => Ok(GeneratedField::Removes), + "historyOnly" | "history_only" => Ok(GeneratedField::HistoryOnly), + "mutateId" | "mutate_id" => Ok(GeneratedField::MutateId), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_request::v1::Mutate; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.api.v1.SubscribeRequest.V1.Mutate") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut adds__ = None; + let mut removes__ = None; + let mut history_only__ = None; + let mut mutate_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Adds => { + if adds__.is_some() { + return Err(serde::de::Error::duplicate_field("adds")); + } + adds__ = Some(map_.next_value()?); + } + GeneratedField::Removes => { + if removes__.is_some() { + return Err(serde::de::Error::duplicate_field("removes")); + } + removes__ = + Some(map_.next_value::>>()? + .into_iter().map(|x| x.0).collect()) + ; + } + GeneratedField::HistoryOnly => { + if history_only__.is_some() { + return Err(serde::de::Error::duplicate_field("historyOnly")); + } + history_only__ = Some(map_.next_value()?); + } + GeneratedField::MutateId => { + if mutate_id__.is_some() { + return Err(serde::de::Error::duplicate_field("mutateId")); + } + mutate_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_request::v1::Mutate { + adds: adds__.unwrap_or_default(), + removes: removes__.unwrap_or_default(), + history_only: history_only__.unwrap_or_default(), + mutate_id: mutate_id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.mls.api.v1.SubscribeRequest.V1.Mutate", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_request::v1::mutate::Subscription { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.topic.is_empty() { + len += 1; + } + if self.id_cursor != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.SubscribeRequest.V1.Mutate.Subscription", len)?; + if !self.topic.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("topic", pbjson::private::base64::encode(&self.topic).as_str())?; + } + if self.id_cursor != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("id_cursor", ToString::to_string(&self.id_cursor).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_request::v1::mutate::Subscription { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "topic", + "id_cursor", + "idCursor", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Topic, + IdCursor, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "topic" => Ok(GeneratedField::Topic), + "idCursor" | "id_cursor" => Ok(GeneratedField::IdCursor), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_request::v1::mutate::Subscription; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.api.v1.SubscribeRequest.V1.Mutate.Subscription") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut topic__ = None; + let mut id_cursor__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Topic => { + if topic__.is_some() { + return Err(serde::de::Error::duplicate_field("topic")); + } + topic__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::IdCursor => { + if id_cursor__.is_some() { + return Err(serde::de::Error::duplicate_field("idCursor")); + } + id_cursor__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_request::v1::mutate::Subscription { + topic: topic__.unwrap_or_default(), + id_cursor: id_cursor__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.mls.api.v1.SubscribeRequest.V1.Mutate.Subscription", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for SubscribeResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.version.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.SubscribeResponse", len)?; + if let Some(v) = self.version.as_ref() { + match v { + subscribe_response::Version::V1(v) => { + struct_ser.serialize_field("v1", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for SubscribeResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "v1", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + V1, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "v1" => Ok(GeneratedField::V1), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = SubscribeResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.api.v1.SubscribeResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut version__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::V1 => { + if version__.is_some() { + return Err(serde::de::Error::duplicate_field("v1")); + } + version__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_response::Version::V1) +; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(SubscribeResponse { + version: version__, + }) + } + } + deserializer.deserialize_struct("xmtp.mls.api.v1.SubscribeResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_response::V1 { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.response.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.SubscribeResponse.V1", len)?; + if let Some(v) = self.response.as_ref() { + match v { + subscribe_response::v1::Response::Messages(v) => { + struct_ser.serialize_field("messages", v)?; + } + subscribe_response::v1::Response::Started(v) => { + struct_ser.serialize_field("started", v)?; + } + subscribe_response::v1::Response::Ping(v) => { + struct_ser.serialize_field("ping", v)?; + } + subscribe_response::v1::Response::Pong(v) => { + struct_ser.serialize_field("pong", v)?; + } + subscribe_response::v1::Response::TopicsLive(v) => { + struct_ser.serialize_field("topics_live", v)?; + } + subscribe_response::v1::Response::CatchupComplete(v) => { + struct_ser.serialize_field("catchup_complete", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_response::V1 { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "messages", + "started", + "ping", + "pong", + "topics_live", + "topicsLive", + "catchup_complete", + "catchupComplete", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Messages, + Started, + Ping, + Pong, + TopicsLive, + CatchupComplete, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "messages" => Ok(GeneratedField::Messages), + "started" => Ok(GeneratedField::Started), + "ping" => Ok(GeneratedField::Ping), + "pong" => Ok(GeneratedField::Pong), + "topicsLive" | "topics_live" => Ok(GeneratedField::TopicsLive), + "catchupComplete" | "catchup_complete" => Ok(GeneratedField::CatchupComplete), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_response::V1; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.api.v1.SubscribeResponse.V1") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut response__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Messages => { + if response__.is_some() { + return Err(serde::de::Error::duplicate_field("messages")); + } + response__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_response::v1::Response::Messages) +; + } + GeneratedField::Started => { + if response__.is_some() { + return Err(serde::de::Error::duplicate_field("started")); + } + response__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_response::v1::Response::Started) +; + } + GeneratedField::Ping => { + if response__.is_some() { + return Err(serde::de::Error::duplicate_field("ping")); + } + response__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_response::v1::Response::Ping) +; + } + GeneratedField::Pong => { + if response__.is_some() { + return Err(serde::de::Error::duplicate_field("pong")); + } + response__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_response::v1::Response::Pong) +; + } + GeneratedField::TopicsLive => { + if response__.is_some() { + return Err(serde::de::Error::duplicate_field("topicsLive")); + } + response__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_response::v1::Response::TopicsLive) +; + } + GeneratedField::CatchupComplete => { + if response__.is_some() { + return Err(serde::de::Error::duplicate_field("catchupComplete")); + } + response__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_response::v1::Response::CatchupComplete) +; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_response::V1 { + response: response__, + }) + } + } + deserializer.deserialize_struct("xmtp.mls.api.v1.SubscribeResponse.V1", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_response::v1::Capability { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + let variant = match self { + Self::Unspecified => "CAPABILITY_UNSPECIFIED", + }; + serializer.serialize_str(variant) + } +} +impl<'de> serde::Deserialize<'de> for subscribe_response::v1::Capability { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "CAPABILITY_UNSPECIFIED", + ]; + + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = subscribe_response::v1::Capability; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + fn visit_i64(self, v: i64) -> std::result::Result + where + E: serde::de::Error, + { + i32::try_from(v) + .ok() + .and_then(|x| x.try_into().ok()) + .ok_or_else(|| { + serde::de::Error::invalid_value(serde::de::Unexpected::Signed(v), &self) + }) + } + + fn visit_u64(self, v: u64) -> std::result::Result + where + E: serde::de::Error, + { + i32::try_from(v) + .ok() + .and_then(|x| x.try_into().ok()) + .ok_or_else(|| { + serde::de::Error::invalid_value(serde::de::Unexpected::Unsigned(v), &self) + }) + } + + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "CAPABILITY_UNSPECIFIED" => Ok(subscribe_response::v1::Capability::Unspecified), + _ => Err(serde::de::Error::unknown_variant(value, FIELDS)), + } + } + } + deserializer.deserialize_any(GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_response::v1::CatchupComplete { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.mutate_id != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.SubscribeResponse.V1.CatchupComplete", len)?; + if self.mutate_id != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("mutate_id", ToString::to_string(&self.mutate_id).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_response::v1::CatchupComplete { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "mutate_id", + "mutateId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + MutateId, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "mutateId" | "mutate_id" => Ok(GeneratedField::MutateId), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_response::v1::CatchupComplete; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.api.v1.SubscribeResponse.V1.CatchupComplete") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut mutate_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::MutateId => { + if mutate_id__.is_some() { + return Err(serde::de::Error::duplicate_field("mutateId")); + } + mutate_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_response::v1::CatchupComplete { + mutate_id: mutate_id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.mls.api.v1.SubscribeResponse.V1.CatchupComplete", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_response::v1::Messages { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.group_messages.is_empty() { + len += 1; + } + if !self.welcome_messages.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.SubscribeResponse.V1.Messages", len)?; + if !self.group_messages.is_empty() { + struct_ser.serialize_field("group_messages", &self.group_messages)?; + } + if !self.welcome_messages.is_empty() { + struct_ser.serialize_field("welcome_messages", &self.welcome_messages)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_response::v1::Messages { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "group_messages", + "groupMessages", + "welcome_messages", + "welcomeMessages", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + GroupMessages, + WelcomeMessages, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "groupMessages" | "group_messages" => Ok(GeneratedField::GroupMessages), + "welcomeMessages" | "welcome_messages" => Ok(GeneratedField::WelcomeMessages), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_response::v1::Messages; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.api.v1.SubscribeResponse.V1.Messages") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut group_messages__ = None; + let mut welcome_messages__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::GroupMessages => { + if group_messages__.is_some() { + return Err(serde::de::Error::duplicate_field("groupMessages")); + } + group_messages__ = Some(map_.next_value()?); + } + GeneratedField::WelcomeMessages => { + if welcome_messages__.is_some() { + return Err(serde::de::Error::duplicate_field("welcomeMessages")); + } + welcome_messages__ = Some(map_.next_value()?); + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_response::v1::Messages { + group_messages: group_messages__.unwrap_or_default(), + welcome_messages: welcome_messages__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.mls.api.v1.SubscribeResponse.V1.Messages", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_response::v1::Started { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.keepalive_interval_ms != 0 { + len += 1; + } + if !self.capabilities.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.SubscribeResponse.V1.Started", len)?; + if self.keepalive_interval_ms != 0 { + struct_ser.serialize_field("keepalive_interval_ms", &self.keepalive_interval_ms)?; + } + if !self.capabilities.is_empty() { + let v = self.capabilities.iter().cloned().map(|v| { + subscribe_response::v1::Capability::try_from(v) + .map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", v))) + }).collect::, _>>()?; + struct_ser.serialize_field("capabilities", &v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_response::v1::Started { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "keepalive_interval_ms", + "keepaliveIntervalMs", + "capabilities", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + KeepaliveIntervalMs, + Capabilities, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "keepaliveIntervalMs" | "keepalive_interval_ms" => Ok(GeneratedField::KeepaliveIntervalMs), + "capabilities" => Ok(GeneratedField::Capabilities), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_response::v1::Started; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.api.v1.SubscribeResponse.V1.Started") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut keepalive_interval_ms__ = None; + let mut capabilities__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::KeepaliveIntervalMs => { + if keepalive_interval_ms__.is_some() { + return Err(serde::de::Error::duplicate_field("keepaliveIntervalMs")); + } + keepalive_interval_ms__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::Capabilities => { + if capabilities__.is_some() { + return Err(serde::de::Error::duplicate_field("capabilities")); + } + capabilities__ = Some(map_.next_value::>()?.into_iter().map(|x| x as i32).collect()); + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_response::v1::Started { + keepalive_interval_ms: keepalive_interval_ms__.unwrap_or_default(), + capabilities: capabilities__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.mls.api.v1.SubscribeResponse.V1.Started", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_response::v1::TopicsLive { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.topics.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.SubscribeResponse.V1.TopicsLive", len)?; + if !self.topics.is_empty() { + struct_ser.serialize_field("topics", &self.topics.iter().map(pbjson::private::base64::encode).collect::>())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_response::v1::TopicsLive { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "topics", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Topics, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "topics" => Ok(GeneratedField::Topics), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_response::v1::TopicsLive; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.api.v1.SubscribeResponse.V1.TopicsLive") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut topics__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Topics => { + if topics__.is_some() { + return Err(serde::de::Error::duplicate_field("topics")); + } + topics__ = + Some(map_.next_value::>>()? + .into_iter().map(|x| x.0).collect()) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_response::v1::TopicsLive { + topics: topics__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.mls.api.v1.SubscribeResponse.V1.TopicsLive", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for SubscribeWelcomeMessagesRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result diff --git a/crates/xmtp_proto/src/gen/xmtp.mls.database.rs b/crates/xmtp_proto/src/gen/xmtp.mls.database.rs index f1efd395c3..7b3795c241 100644 --- a/crates/xmtp_proto/src/gen/xmtp.mls.database.rs +++ b/crates/xmtp_proto/src/gen/xmtp.mls.database.rs @@ -1,50 +1,4 @@ // This file is @generated by prost-build. -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct Task { - #[prost(oneof = "task::Task", tags = "1, 2")] - pub task: ::core::option::Option, -} -/// Nested message and enum types in `Task`. -pub mod task { - #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] - pub enum Task { - #[prost(message, tag = "1")] - ProcessWelcomePointer(super::super::message_contents::WelcomePointer), - #[prost(message, tag = "2")] - SendSyncArchive(super::SendSyncArchive), - } -} -impl ::prost::Name for Task { - const NAME: &'static str = "Task"; - const PACKAGE: &'static str = "xmtp.mls.database"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.database.Task".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.database.Task".into() - } -} -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct SendSyncArchive { - #[prost(message, optional, tag = "1")] - pub options: ::core::option::Option, - #[prost(bytes = "vec", tag = "2")] - pub sync_group_id: ::prost::alloc::vec::Vec, - #[prost(string, optional, tag = "3")] - pub pin: ::core::option::Option<::prost::alloc::string::String>, - #[prost(string, tag = "4")] - pub server_url: ::prost::alloc::string::String, -} -impl ::prost::Name for SendSyncArchive { - const NAME: &'static str = "SendSyncArchive"; - const PACKAGE: &'static str = "xmtp.mls.database"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.database.SendSyncArchive".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.database.SendSyncArchive".into() - } -} /// The data required to publish a message #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct SendMessageData { @@ -457,6 +411,79 @@ impl ::prost::Name for UpdatePermissionData { "/xmtp.mls.database.UpdatePermissionData".into() } } +/// Generic AppData write intent — local-only serialization for the +/// `group_intents` table. Replaces the proliferation of per-component +/// IntentKind/IntentData pairs with a single shape that carries the +/// target component_id and the same payload bytes that will be written +/// to the on-wire `AppDataUpdate` proposal. Interpretation of `payload` +/// is determined by the target component's `ComponentType` in the +/// registry: +/// +/// * `Bytes` / `String` typed components — payload is the new value +/// written verbatim (last-writer-wins). +/// * `TlsMap` typed components — payload is a TLS-encoded +/// `TlsMapDelta` carrying `Insert` / `Update` / `Delete` +/// mutations. Apply semantics on the receive side are total +/// (Insert is no-op-if-present, Update is upsert, Delete is +/// idempotent); the strict variants of the in-process TlsMap API +/// are not exposed on the wire. No "conflict" failure path. +/// * `TlsSet` typed components — payload is a TLS-encoded +/// `TlsSetDelta` (`Add` is no-op-if-present, `Remove` is +/// idempotent). +/// +/// Never appears on the MLS wire; lives only in the local intents +/// table. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct AppDataUpdateData { + /// Versioned envelope. New variants are added as new oneof entries; + /// readers that don't recognize a variant fail closed. + #[prost(oneof = "app_data_update_data::Version", tags = "1")] + pub version: ::core::option::Option, +} +/// Nested message and enum types in `AppDataUpdateData`. +pub mod app_data_update_data { + /// v1 payload shape. + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] + pub struct V1 { + /// u16 component_id widened to u32 (proto has no u16). The + /// dispatcher narrows back to u16 at decode time. + #[prost(uint32, tag = "1")] + pub component_id: u32, + /// The bytes that will be written verbatim as the on-wire + /// AppDataUpdate proposal payload. Interpretation determined by + /// the component's registered ComponentType (see message-level + /// comment above). + #[prost(bytes = "vec", tag = "2")] + pub payload: ::prost::alloc::vec::Vec, + } + impl ::prost::Name for V1 { + const NAME: &'static str = "V1"; + const PACKAGE: &'static str = "xmtp.mls.database"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.database.AppDataUpdateData.V1".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.database.AppDataUpdateData.V1".into() + } + } + /// Versioned envelope. New variants are added as new oneof entries; + /// readers that don't recognize a variant fail closed. + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] + pub enum Version { + #[prost(message, tag = "1")] + V1(V1), + } +} +impl ::prost::Name for AppDataUpdateData { + const NAME: &'static str = "AppDataUpdateData"; + const PACKAGE: &'static str = "xmtp.mls.database"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.database.AppDataUpdateData".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.database.AppDataUpdateData".into() + } +} /// Generic data-type for all post-commit actions #[derive(Clone, PartialEq, ::prost::Message)] pub struct PostCommitAction { @@ -754,3 +781,71 @@ impl PermissionPolicyOption { } } } +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Task { + #[prost(oneof = "task::Task", tags = "1, 2, 3")] + pub task: ::core::option::Option, +} +/// Nested message and enum types in `Task`. +pub mod task { + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] + pub enum Task { + #[prost(message, tag = "1")] + ProcessWelcomePointer(super::super::message_contents::WelcomePointer), + #[prost(message, tag = "2")] + SendSyncArchive(super::SendSyncArchive), + #[prost(message, tag = "3")] + ProcessPendingSelfRemove(super::ProcessPendingSelfRemove), + } +} +impl ::prost::Name for Task { + const NAME: &'static str = "Task"; + const PACKAGE: &'static str = "xmtp.mls.database"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.database.Task".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.database.Task".into() + } +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct SendSyncArchive { + #[prost(message, optional, tag = "1")] + pub options: ::core::option::Option, + #[prost(bytes = "vec", tag = "2")] + pub sync_group_id: ::prost::alloc::vec::Vec, + #[prost(string, optional, tag = "3")] + pub pin: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, tag = "4")] + pub server_url: ::prost::alloc::string::String, +} +impl ::prost::Name for SendSyncArchive { + const NAME: &'static str = "SendSyncArchive"; + const PACKAGE: &'static str = "xmtp.mls.database"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.database.SendSyncArchive".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.database.SendSyncArchive".into() + } +} +/// Durable TaskRunner intent: process a group's pending self-remove requests +/// (build the MLS RemoveProposal/Commit to evict members who sent a LeaveRequest, +/// then clean up the pending-remove list). Enqueued in the same DB transaction as +/// the pending_remove row so it survives restart; runs on the TaskRunner with +/// retry/backoff. group_id is the target conversation. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ProcessPendingSelfRemove { + #[prost(bytes = "vec", tag = "1")] + pub group_id: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for ProcessPendingSelfRemove { + const NAME: &'static str = "ProcessPendingSelfRemove"; + const PACKAGE: &'static str = "xmtp.mls.database"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.database.ProcessPendingSelfRemove".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.database.ProcessPendingSelfRemove".into() + } +} diff --git a/crates/xmtp_proto/src/gen/xmtp.mls.database.serde.rs b/crates/xmtp_proto/src/gen/xmtp.mls.database.serde.rs index 13d8f74f99..54bff52738 100644 --- a/crates/xmtp_proto/src/gen/xmtp.mls.database.serde.rs +++ b/crates/xmtp_proto/src/gen/xmtp.mls.database.serde.rs @@ -485,6 +485,225 @@ impl<'de> serde::Deserialize<'de> for AdminListUpdateType { deserializer.deserialize_any(GeneratedVisitor) } } +impl serde::Serialize for AppDataUpdateData { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.version.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.database.AppDataUpdateData", len)?; + if let Some(v) = self.version.as_ref() { + match v { + app_data_update_data::Version::V1(v) => { + struct_ser.serialize_field("v1", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for AppDataUpdateData { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "v1", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + V1, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "v1" => Ok(GeneratedField::V1), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = AppDataUpdateData; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.database.AppDataUpdateData") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut version__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::V1 => { + if version__.is_some() { + return Err(serde::de::Error::duplicate_field("v1")); + } + version__ = map_.next_value::<::std::option::Option<_>>()?.map(app_data_update_data::Version::V1) +; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(AppDataUpdateData { + version: version__, + }) + } + } + deserializer.deserialize_struct("xmtp.mls.database.AppDataUpdateData", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for app_data_update_data::V1 { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.component_id != 0 { + len += 1; + } + if !self.payload.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.database.AppDataUpdateData.V1", len)?; + if self.component_id != 0 { + struct_ser.serialize_field("component_id", &self.component_id)?; + } + if !self.payload.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("payload", pbjson::private::base64::encode(&self.payload).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for app_data_update_data::V1 { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "component_id", + "componentId", + "payload", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + ComponentId, + Payload, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "componentId" | "component_id" => Ok(GeneratedField::ComponentId), + "payload" => Ok(GeneratedField::Payload), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = app_data_update_data::V1; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.database.AppDataUpdateData.V1") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut component_id__ = None; + let mut payload__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::ComponentId => { + if component_id__.is_some() { + return Err(serde::de::Error::duplicate_field("componentId")); + } + component_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::Payload => { + if payload__.is_some() { + return Err(serde::de::Error::duplicate_field("payload")); + } + payload__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(app_data_update_data::V1 { + component_id: component_id__.unwrap_or_default(), + payload: payload__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.mls.database.AppDataUpdateData.V1", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for CommitPendingProposalsData { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -1324,6 +1543,106 @@ impl<'de> serde::Deserialize<'de> for post_commit_action::SendWelcomes { deserializer.deserialize_struct("xmtp.mls.database.PostCommitAction.SendWelcomes", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for ProcessPendingSelfRemove { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.group_id.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.database.ProcessPendingSelfRemove", len)?; + if !self.group_id.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("group_id", pbjson::private::base64::encode(&self.group_id).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ProcessPendingSelfRemove { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "group_id", + "groupId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + GroupId, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "groupId" | "group_id" => Ok(GeneratedField::GroupId), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ProcessPendingSelfRemove; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.database.ProcessPendingSelfRemove") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut group_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::GroupId => { + if group_id__.is_some() { + return Err(serde::de::Error::duplicate_field("groupId")); + } + group_id__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(ProcessPendingSelfRemove { + group_id: group_id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.mls.database.ProcessPendingSelfRemove", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for ProposeGroupContextExtensionData { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -2511,6 +2830,9 @@ impl serde::Serialize for Task { task::Task::SendSyncArchive(v) => { struct_ser.serialize_field("send_sync_archive", v)?; } + task::Task::ProcessPendingSelfRemove(v) => { + struct_ser.serialize_field("process_pending_self_remove", v)?; + } } } struct_ser.end() @@ -2527,12 +2849,15 @@ impl<'de> serde::Deserialize<'de> for Task { "processWelcomePointer", "send_sync_archive", "sendSyncArchive", + "process_pending_self_remove", + "processPendingSelfRemove", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { ProcessWelcomePointer, SendSyncArchive, + ProcessPendingSelfRemove, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -2557,6 +2882,7 @@ impl<'de> serde::Deserialize<'de> for Task { match value { "processWelcomePointer" | "process_welcome_pointer" => Ok(GeneratedField::ProcessWelcomePointer), "sendSyncArchive" | "send_sync_archive" => Ok(GeneratedField::SendSyncArchive), + "processPendingSelfRemove" | "process_pending_self_remove" => Ok(GeneratedField::ProcessPendingSelfRemove), _ => Ok(GeneratedField::__SkipField__), } } @@ -2591,6 +2917,13 @@ impl<'de> serde::Deserialize<'de> for Task { return Err(serde::de::Error::duplicate_field("sendSyncArchive")); } task__ = map_.next_value::<::std::option::Option<_>>()?.map(task::Task::SendSyncArchive) +; + } + GeneratedField::ProcessPendingSelfRemove => { + if task__.is_some() { + return Err(serde::de::Error::duplicate_field("processPendingSelfRemove")); + } + task__ = map_.next_value::<::std::option::Option<_>>()?.map(task::Task::ProcessPendingSelfRemove) ; } GeneratedField::__SkipField__ => { diff --git a/crates/xmtp_proto/src/gen/xmtp.mls.message_contents.content_types.rs b/crates/xmtp_proto/src/gen/xmtp.mls.message_contents.content_types.rs index de3b0d73b7..aeddaaa493 100644 --- a/crates/xmtp_proto/src/gen/xmtp.mls.message_contents.content_types.rs +++ b/crates/xmtp_proto/src/gen/xmtp.mls.message_contents.content_types.rs @@ -1,4 +1,95 @@ // This file is @generated by prost-build. +/// DeleteMessage message type +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct DeleteMessage { + /// ID of the message to delete + #[prost(string, tag = "1")] + pub message_id: ::prost::alloc::string::String, +} +impl ::prost::Name for DeleteMessage { + const NAME: &'static str = "DeleteMessage"; + const PACKAGE: &'static str = "xmtp.mls.message_contents.content_types"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.content_types.DeleteMessage".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.content_types.DeleteMessage".into() + } +} +/// MultiRemoteAttachment message type +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MultiRemoteAttachment { + /// Array of attachment information + #[prost(message, repeated, tag = "1")] + pub attachments: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for MultiRemoteAttachment { + const NAME: &'static str = "MultiRemoteAttachment"; + const PACKAGE: &'static str = "xmtp.mls.message_contents.content_types"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.content_types.MultiRemoteAttachment".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.content_types.MultiRemoteAttachment".into() + } +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct RemoteAttachmentInfo { + /// The SHA256 hash of the remote content + #[prost(string, tag = "1")] + pub content_digest: ::prost::alloc::string::String, + /// A 32 byte array for decrypting the remote content payload + #[prost(bytes = "vec", tag = "2")] + pub secret: ::prost::alloc::vec::Vec, + /// A byte array for the nonce used to encrypt the remote content payload + #[prost(bytes = "vec", tag = "3")] + pub nonce: ::prost::alloc::vec::Vec, + /// A byte array for the salt used to encrypt the remote content payload + #[prost(bytes = "vec", tag = "4")] + pub salt: ::prost::alloc::vec::Vec, + /// The scheme of the URL. Must be " + #[prost(string, tag = "5")] + pub scheme: ::prost::alloc::string::String, + /// The URL of the remote content + #[prost(string, tag = "6")] + pub url: ::prost::alloc::string::String, + /// The size of the encrypted content in bytes (max size of 4GB) + #[prost(uint32, optional, tag = "7")] + pub content_length: ::core::option::Option, + /// The filename of the remote content + #[prost(string, optional, tag = "8")] + pub filename: ::core::option::Option<::prost::alloc::string::String>, +} +impl ::prost::Name for RemoteAttachmentInfo { + const NAME: &'static str = "RemoteAttachmentInfo"; + const PACKAGE: &'static str = "xmtp.mls.message_contents.content_types"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.content_types.RemoteAttachmentInfo".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.content_types.RemoteAttachmentInfo".into() + } +} +/// EditMessage message type +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EditMessage { + /// ID of the message to edit + #[prost(string, tag = "1")] + pub message_id: ::prost::alloc::string::String, + /// The new content for the message + #[prost(message, optional, tag = "2")] + pub edited_content: ::core::option::Option, +} +impl ::prost::Name for EditMessage { + const NAME: &'static str = "EditMessage"; + const PACKAGE: &'static str = "xmtp.mls.message_contents.content_types"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.content_types.EditMessage".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.content_types.EditMessage".into() + } +} /// Reaction message type #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct ReactionV2 { @@ -92,77 +183,6 @@ impl ReactionSchema { } } } -/// DeleteMessage message type -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct DeleteMessage { - /// ID of the message to delete - #[prost(string, tag = "1")] - pub message_id: ::prost::alloc::string::String, -} -impl ::prost::Name for DeleteMessage { - const NAME: &'static str = "DeleteMessage"; - const PACKAGE: &'static str = "xmtp.mls.message_contents.content_types"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.content_types.DeleteMessage".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.content_types.DeleteMessage".into() - } -} -/// MultiRemoteAttachment message type -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MultiRemoteAttachment { - /// Array of attachment information - #[prost(message, repeated, tag = "1")] - pub attachments: ::prost::alloc::vec::Vec, -} -impl ::prost::Name for MultiRemoteAttachment { - const NAME: &'static str = "MultiRemoteAttachment"; - const PACKAGE: &'static str = "xmtp.mls.message_contents.content_types"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.content_types.MultiRemoteAttachment".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.content_types.MultiRemoteAttachment".into() - } -} -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct RemoteAttachmentInfo { - /// The SHA256 hash of the remote content - #[prost(string, tag = "1")] - pub content_digest: ::prost::alloc::string::String, - /// A 32 byte array for decrypting the remote content payload - #[prost(bytes = "vec", tag = "2")] - pub secret: ::prost::alloc::vec::Vec, - /// A byte array for the nonce used to encrypt the remote content payload - #[prost(bytes = "vec", tag = "3")] - pub nonce: ::prost::alloc::vec::Vec, - /// A byte array for the salt used to encrypt the remote content payload - #[prost(bytes = "vec", tag = "4")] - pub salt: ::prost::alloc::vec::Vec, - /// The scheme of the URL. Must be " - #[prost(string, tag = "5")] - pub scheme: ::prost::alloc::string::String, - /// The URL of the remote content - #[prost(string, tag = "6")] - pub url: ::prost::alloc::string::String, - /// The size of the encrypted content in bytes (max size of 4GB) - #[prost(uint32, optional, tag = "7")] - pub content_length: ::core::option::Option, - /// The filename of the remote content - #[prost(string, optional, tag = "8")] - pub filename: ::core::option::Option<::prost::alloc::string::String>, -} -impl ::prost::Name for RemoteAttachmentInfo { - const NAME: &'static str = "RemoteAttachmentInfo"; - const PACKAGE: &'static str = "xmtp.mls.message_contents.content_types"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.content_types.RemoteAttachmentInfo".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.content_types.RemoteAttachmentInfo".into() - } -} /// LeaveRequest message type #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct LeaveRequest { @@ -242,23 +262,3 @@ impl ::prost::Name for Call { "/xmtp.mls.message_contents.content_types.Call".into() } } -/// EditMessage message type -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct EditMessage { - /// ID of the message to edit - #[prost(string, tag = "1")] - pub message_id: ::prost::alloc::string::String, - /// The new content for the message - #[prost(message, optional, tag = "2")] - pub edited_content: ::core::option::Option, -} -impl ::prost::Name for EditMessage { - const NAME: &'static str = "EditMessage"; - const PACKAGE: &'static str = "xmtp.mls.message_contents.content_types"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.content_types.EditMessage".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.content_types.EditMessage".into() - } -} diff --git a/crates/xmtp_proto/src/gen/xmtp.mls.message_contents.rs b/crates/xmtp_proto/src/gen/xmtp.mls.message_contents.rs index bd21161a1a..7e59ce5b20 100644 --- a/crates/xmtp_proto/src/gen/xmtp.mls.message_contents.rs +++ b/crates/xmtp_proto/src/gen/xmtp.mls.message_contents.rs @@ -1,4 +1,93 @@ // This file is @generated by prost-build. +/// PlaintextCommitLogEntry indicates whether a commit was successful or not, +/// when applied on top of the indicated `last_epoch_authenticator`. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct PlaintextCommitLogEntry { + /// The group_id of the group that the commit belongs to. + #[prost(bytes = "vec", tag = "1")] + pub group_id: ::prost::alloc::vec::Vec, + /// The sequence ID of the commit payload being validated. + #[prost(uint64, tag = "2")] + pub commit_sequence_id: u64, + /// The encryption state before the commit was applied. + #[prost(bytes = "vec", tag = "3")] + pub last_epoch_authenticator: ::prost::alloc::vec::Vec, + /// Indicates whether the commit was successful, or why it failed. + #[prost(enumeration = "CommitResult", tag = "4")] + pub commit_result: i32, + /// The epoch number after the commit was applied, if successful. + #[prost(uint64, tag = "5")] + pub applied_epoch_number: u64, + /// The encryption state after the commit was applied, if successful. + #[prost(bytes = "vec", tag = "6")] + pub applied_epoch_authenticator: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for PlaintextCommitLogEntry { + const NAME: &'static str = "PlaintextCommitLogEntry"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.PlaintextCommitLogEntry".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.PlaintextCommitLogEntry".into() + } +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct CommitLogEntry { + #[prost(uint64, tag = "1")] + pub sequence_id: u64, + #[prost(bytes = "vec", tag = "2")] + pub serialized_commit_log_entry: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "3")] + pub signature: ::core::option::Option< + super::super::identity::associations::RecoverableEd25519Signature, + >, +} +impl ::prost::Name for CommitLogEntry { + const NAME: &'static str = "CommitLogEntry"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.CommitLogEntry".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.CommitLogEntry".into() + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum CommitResult { + Unspecified = 0, + Applied = 1, + WrongEpoch = 2, + Undecryptable = 3, + Invalid = 4, +} +impl CommitResult { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "COMMIT_RESULT_UNSPECIFIED", + Self::Applied => "COMMIT_RESULT_APPLIED", + Self::WrongEpoch => "COMMIT_RESULT_WRONG_EPOCH", + Self::Undecryptable => "COMMIT_RESULT_UNDECRYPTABLE", + Self::Invalid => "COMMIT_RESULT_INVALID", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "COMMIT_RESULT_UNSPECIFIED" => Some(Self::Unspecified), + "COMMIT_RESULT_APPLIED" => Some(Self::Applied), + "COMMIT_RESULT_WRONG_EPOCH" => Some(Self::WrongEpoch), + "COMMIT_RESULT_UNDECRYPTABLE" => Some(Self::Undecryptable), + "COMMIT_RESULT_INVALID" => Some(Self::Invalid), + _ => None, + } + } +} /// A WelcomePointer is used to point to the welcome message for several installations at once to save overhead #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct WelcomePointer { @@ -188,421 +277,87 @@ impl WelcomeWrapperAlgorithm { } /// Message for group mutable metadata #[derive(Clone, PartialEq, ::prost::Message)] -pub struct GroupMutableMetadataV1 { - /// Map to store various metadata attributes (Group name, etc.) - #[prost(map = "string, string", tag = "1")] - pub attributes: ::std::collections::HashMap< - ::prost::alloc::string::String, - ::prost::alloc::string::String, - >, - #[prost(message, optional, tag = "2")] - pub admin_list: ::core::option::Option, - /// Creator starts as only super_admin - /// Only super_admin can add/remove other super_admin - #[prost(message, optional, tag = "3")] - pub super_admin_list: ::core::option::Option, +pub struct GroupMutablePermissionsV1 { + #[prost(message, optional, tag = "1")] + pub policies: ::core::option::Option, } -impl ::prost::Name for GroupMutableMetadataV1 { - const NAME: &'static str = "GroupMutableMetadataV1"; +impl ::prost::Name for GroupMutablePermissionsV1 { + const NAME: &'static str = "GroupMutablePermissionsV1"; const PACKAGE: &'static str = "xmtp.mls.message_contents"; fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.GroupMutableMetadataV1".into() + "xmtp.mls.message_contents.GroupMutablePermissionsV1".into() } fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.GroupMutableMetadataV1".into() + "/xmtp.mls.message_contents.GroupMutablePermissionsV1".into() } } -/// Wrapper around a list of repeated Inbox Ids -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct Inboxes { - #[prost(string, repeated, tag = "1")] - pub inbox_ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +/// The set of policies that govern the group +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PolicySet { + #[prost(message, optional, tag = "1")] + pub add_member_policy: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub remove_member_policy: ::core::option::Option, + #[prost(map = "string, message", tag = "3")] + pub update_metadata_policy: ::std::collections::HashMap< + ::prost::alloc::string::String, + MetadataPolicy, + >, + #[prost(message, optional, tag = "4")] + pub add_admin_policy: ::core::option::Option, + #[prost(message, optional, tag = "5")] + pub remove_admin_policy: ::core::option::Option, + #[prost(message, optional, tag = "6")] + pub update_permissions_policy: ::core::option::Option, } -impl ::prost::Name for Inboxes { - const NAME: &'static str = "Inboxes"; +impl ::prost::Name for PolicySet { + const NAME: &'static str = "PolicySet"; const PACKAGE: &'static str = "xmtp.mls.message_contents"; fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.Inboxes".into() + "xmtp.mls.message_contents.PolicySet".into() } fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.Inboxes".into() + "/xmtp.mls.message_contents.PolicySet".into() } } -/// PlaintextCommitLogEntry indicates whether a commit was successful or not, -/// when applied on top of the indicated `last_epoch_authenticator`. -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct PlaintextCommitLogEntry { - /// The group_id of the group that the commit belongs to. - #[prost(bytes = "vec", tag = "1")] - pub group_id: ::prost::alloc::vec::Vec, - /// The sequence ID of the commit payload being validated. - #[prost(uint64, tag = "2")] - pub commit_sequence_id: u64, - /// The encryption state before the commit was applied. - #[prost(bytes = "vec", tag = "3")] - pub last_epoch_authenticator: ::prost::alloc::vec::Vec, - /// Indicates whether the commit was successful, or why it failed. - #[prost(enumeration = "CommitResult", tag = "4")] - pub commit_result: i32, - /// The epoch number after the commit was applied, if successful. - #[prost(uint64, tag = "5")] - pub applied_epoch_number: u64, - /// The encryption state after the commit was applied, if successful. - #[prost(bytes = "vec", tag = "6")] - pub applied_epoch_authenticator: ::prost::alloc::vec::Vec, +/// A policy that governs adding/removing members or installations +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MembershipPolicy { + #[prost(oneof = "membership_policy::Kind", tags = "1, 2, 3")] + pub kind: ::core::option::Option, } -impl ::prost::Name for PlaintextCommitLogEntry { - const NAME: &'static str = "PlaintextCommitLogEntry"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.PlaintextCommitLogEntry".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.PlaintextCommitLogEntry".into() +/// Nested message and enum types in `MembershipPolicy`. +pub mod membership_policy { + /// Combine multiple policies. All must evaluate to true + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct AndCondition { + #[prost(message, repeated, tag = "1")] + pub policies: ::prost::alloc::vec::Vec, } -} -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct CommitLogEntry { - #[prost(uint64, tag = "1")] - pub sequence_id: u64, - #[prost(bytes = "vec", tag = "2")] - pub serialized_commit_log_entry: ::prost::alloc::vec::Vec, - #[prost(message, optional, tag = "3")] - pub signature: ::core::option::Option< - super::super::identity::associations::RecoverableEd25519Signature, - >, -} -impl ::prost::Name for CommitLogEntry { - const NAME: &'static str = "CommitLogEntry"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.CommitLogEntry".into() + impl ::prost::Name for AndCondition { + const NAME: &'static str = "AndCondition"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.MembershipPolicy.AndCondition".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.MembershipPolicy.AndCondition".into() + } } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.CommitLogEntry".into() + /// Combine multiple policies. Any must evaluate to true + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct AnyCondition { + #[prost(message, repeated, tag = "1")] + pub policies: ::prost::alloc::vec::Vec, } -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum CommitResult { - Unspecified = 0, - Applied = 1, - WrongEpoch = 2, - Undecryptable = 3, - Invalid = 4, -} -impl CommitResult { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - Self::Unspecified => "COMMIT_RESULT_UNSPECIFIED", - Self::Applied => "COMMIT_RESULT_APPLIED", - Self::WrongEpoch => "COMMIT_RESULT_WRONG_EPOCH", - Self::Undecryptable => "COMMIT_RESULT_UNDECRYPTABLE", - Self::Invalid => "COMMIT_RESULT_INVALID", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "COMMIT_RESULT_UNSPECIFIED" => Some(Self::Unspecified), - "COMMIT_RESULT_APPLIED" => Some(Self::Applied), - "COMMIT_RESULT_WRONG_EPOCH" => Some(Self::WrongEpoch), - "COMMIT_RESULT_UNDECRYPTABLE" => Some(Self::Undecryptable), - "COMMIT_RESULT_INVALID" => Some(Self::Invalid), - _ => None, - } - } -} -/// Extension data for proposal support in group context. -/// When present in the group context extensions, indicates the group -/// uses proposal-by-reference flow. -#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] -pub struct ProposalSupport { - #[prost(uint32, tag = "1")] - pub version: u32, -} -impl ::prost::Name for ProposalSupport { - const NAME: &'static str = "ProposalSupport"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.ProposalSupport".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.ProposalSupport".into() - } -} -/// ContentTypeId is used to identify the type of content stored in a Message. -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct ContentTypeId { - /// authority governing this content type - #[prost(string, tag = "1")] - pub authority_id: ::prost::alloc::string::String, - /// type identifier - #[prost(string, tag = "2")] - pub type_id: ::prost::alloc::string::String, - /// major version of the type - #[prost(uint32, tag = "3")] - pub version_major: u32, - /// minor version of the type - #[prost(uint32, tag = "4")] - pub version_minor: u32, -} -impl ::prost::Name for ContentTypeId { - const NAME: &'static str = "ContentTypeId"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.ContentTypeId".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.ContentTypeId".into() - } -} -/// EncodedContent bundles the content with metadata identifying its type -/// and parameters required for correct decoding and presentation of the content. -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct EncodedContent { - /// content type identifier used to match the payload with - /// the correct decoding machinery - #[prost(message, optional, tag = "1")] - pub r#type: ::core::option::Option, - /// optional encoding parameters required to correctly decode the content - #[prost(map = "string, string", tag = "2")] - pub parameters: ::std::collections::HashMap< - ::prost::alloc::string::String, - ::prost::alloc::string::String, - >, - /// optional fallback description of the content that can be used in case - /// the client cannot decode or render the content - #[prost(string, optional, tag = "3")] - pub fallback: ::core::option::Option<::prost::alloc::string::String>, - /// optional compression; the value indicates algorithm used to - /// compress the encoded content bytes - #[prost(enumeration = "Compression", optional, tag = "5")] - pub compression: ::core::option::Option, - /// encoded content itself - #[prost(bytes = "vec", tag = "4")] - pub content: ::prost::alloc::vec::Vec, -} -impl ::prost::Name for EncodedContent { - const NAME: &'static str = "EncodedContent"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.EncodedContent".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.EncodedContent".into() - } -} -/// A PlaintextEnvelope is the outermost payload that gets encrypted by MLS -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct PlaintextEnvelope { - /// Selector which declares which version of the EncodedContent this - /// PlaintextEnvelope is - #[prost(oneof = "plaintext_envelope::Content", tags = "1, 2")] - pub content: ::core::option::Option, -} -/// Nested message and enum types in `PlaintextEnvelope`. -pub mod plaintext_envelope { - /// Version 1 of the encrypted envelope - #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] - pub struct V1 { - /// Expected to be EncodedContent - #[prost(bytes = "vec", tag = "1")] - pub content: ::prost::alloc::vec::Vec, - /// A unique value that can be used to ensure that the same content can - /// produce different hashes. May be the sender timestamp. - #[prost(string, tag = "2")] - pub idempotency_key: ::prost::alloc::string::String, - } - impl ::prost::Name for V1 { - const NAME: &'static str = "V1"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.PlaintextEnvelope.V1".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.PlaintextEnvelope.V1".into() - } - } - /// Version 2 of the encrypted envelope - #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] - pub struct V2 { - /// A unique value that can be used to ensure that the same content can - /// produce different hashes. May be the sender timestamp. - #[prost(string, tag = "1")] - pub idempotency_key: ::prost::alloc::string::String, - #[prost(oneof = "v2::MessageType", tags = "2, 3, 4, 5")] - pub message_type: ::core::option::Option, - } - /// Nested message and enum types in `V2`. - pub mod v2 { - #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] - pub enum MessageType { - /// Expected to be EncodedContent - #[prost(bytes, tag = "2")] - Content(::prost::alloc::vec::Vec), - /// Initiator sends a request to receive sync payload - #[prost(message, tag = "3")] - DeviceSyncRequest( - super::super::super::super::device_sync::content::DeviceSyncRequest, - ), - /// Some other authorized installation sends a reply with a link to payload - #[prost(message, tag = "4")] - DeviceSyncReply( - super::super::super::super::device_sync::content::DeviceSyncReply, - ), - /// A serialized user preference update - #[prost(message, tag = "5")] - UserPreferenceUpdate( - super::super::super::super::device_sync::content::V1UserPreferenceUpdate, - ), - } - } - impl ::prost::Name for V2 { - const NAME: &'static str = "V2"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.PlaintextEnvelope.V2".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.PlaintextEnvelope.V2".into() - } - } - /// Selector which declares which version of the EncodedContent this - /// PlaintextEnvelope is - #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] - pub enum Content { - #[prost(message, tag = "1")] - V1(V1), - #[prost(message, tag = "2")] - V2(V2), - } -} -impl ::prost::Name for PlaintextEnvelope { - const NAME: &'static str = "PlaintextEnvelope"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.PlaintextEnvelope".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.PlaintextEnvelope".into() - } -} -/// Recognized compression algorithms -/// protolint:disable ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum Compression { - Deflate = 0, - Gzip = 1, -} -impl Compression { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - Self::Deflate => "COMPRESSION_DEFLATE", - Self::Gzip => "COMPRESSION_GZIP", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "COMPRESSION_DEFLATE" => Some(Self::Deflate), - "COMPRESSION_GZIP" => Some(Self::Gzip), - _ => None, - } - } -} -/// Message for group mutable metadata -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GroupMutablePermissionsV1 { - #[prost(message, optional, tag = "1")] - pub policies: ::core::option::Option, -} -impl ::prost::Name for GroupMutablePermissionsV1 { - const NAME: &'static str = "GroupMutablePermissionsV1"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.GroupMutablePermissionsV1".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.GroupMutablePermissionsV1".into() - } -} -/// The set of policies that govern the group -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PolicySet { - #[prost(message, optional, tag = "1")] - pub add_member_policy: ::core::option::Option, - #[prost(message, optional, tag = "2")] - pub remove_member_policy: ::core::option::Option, - #[prost(map = "string, message", tag = "3")] - pub update_metadata_policy: ::std::collections::HashMap< - ::prost::alloc::string::String, - MetadataPolicy, - >, - #[prost(message, optional, tag = "4")] - pub add_admin_policy: ::core::option::Option, - #[prost(message, optional, tag = "5")] - pub remove_admin_policy: ::core::option::Option, - #[prost(message, optional, tag = "6")] - pub update_permissions_policy: ::core::option::Option, -} -impl ::prost::Name for PolicySet { - const NAME: &'static str = "PolicySet"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.PolicySet".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.PolicySet".into() - } -} -/// A policy that governs adding/removing members or installations -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MembershipPolicy { - #[prost(oneof = "membership_policy::Kind", tags = "1, 2, 3")] - pub kind: ::core::option::Option, -} -/// Nested message and enum types in `MembershipPolicy`. -pub mod membership_policy { - /// Combine multiple policies. All must evaluate to true - #[derive(Clone, PartialEq, ::prost::Message)] - pub struct AndCondition { - #[prost(message, repeated, tag = "1")] - pub policies: ::prost::alloc::vec::Vec, - } - impl ::prost::Name for AndCondition { - const NAME: &'static str = "AndCondition"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.MembershipPolicy.AndCondition".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.MembershipPolicy.AndCondition".into() - } - } - /// Combine multiple policies. Any must evaluate to true - #[derive(Clone, PartialEq, ::prost::Message)] - pub struct AnyCondition { - #[prost(message, repeated, tag = "1")] - pub policies: ::prost::alloc::vec::Vec, - } - impl ::prost::Name for AnyCondition { - const NAME: &'static str = "AnyCondition"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.MembershipPolicy.AnyCondition".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.MembershipPolicy.AnyCondition".into() + impl ::prost::Name for AnyCondition { + const NAME: &'static str = "AnyCondition"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.MembershipPolicy.AnyCondition".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.MembershipPolicy.AnyCondition".into() } } /// Base policy @@ -855,267 +610,498 @@ pub mod permissions_update_policy { Self::AllowIfSuperAdmin => "PERMISSIONS_BASE_POLICY_ALLOW_IF_SUPER_ADMIN", } } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "PERMISSIONS_BASE_POLICY_UNSPECIFIED" => Some(Self::Unspecified), - "PERMISSIONS_BASE_POLICY_DENY" => Some(Self::Deny), - "PERMISSIONS_BASE_POLICY_ALLOW_IF_ADMIN" => Some(Self::AllowIfAdmin), - "PERMISSIONS_BASE_POLICY_ALLOW_IF_SUPER_ADMIN" => { - Some(Self::AllowIfSuperAdmin) - } - _ => None, - } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "PERMISSIONS_BASE_POLICY_UNSPECIFIED" => Some(Self::Unspecified), + "PERMISSIONS_BASE_POLICY_DENY" => Some(Self::Deny), + "PERMISSIONS_BASE_POLICY_ALLOW_IF_ADMIN" => Some(Self::AllowIfAdmin), + "PERMISSIONS_BASE_POLICY_ALLOW_IF_SUPER_ADMIN" => { + Some(Self::AllowIfSuperAdmin) + } + _ => None, + } + } + } + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Kind { + #[prost(enumeration = "PermissionsBasePolicy", tag = "1")] + Base(i32), + #[prost(message, tag = "2")] + AndCondition(AndCondition), + #[prost(message, tag = "3")] + AnyCondition(AnyCondition), + } +} +impl ::prost::Name for PermissionsUpdatePolicy { + const NAME: &'static str = "PermissionsUpdatePolicy"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.PermissionsUpdatePolicy".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.PermissionsUpdatePolicy".into() + } +} +/// Per-component permission policy with separate rules for insert, update, +/// and delete operations. +/// +/// Insert and update are separate because some components need different +/// permission levels for creating vs modifying entries. For example, group +/// membership allows any member to update (installations/sequence ID) but +/// only admins to insert (add a new member). +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ComponentPermissions { + /// Policy for inserting a new value (component does not yet exist) + #[prost(message, optional, tag = "1")] + pub insert_policy: ::core::option::Option, + /// Policy for updating an existing value + #[prost(message, optional, tag = "2")] + pub update_policy: ::core::option::Option, + /// Policy for deleting a value + #[prost(message, optional, tag = "3")] + pub delete_policy: ::core::option::Option, +} +impl ::prost::Name for ComponentPermissions { + const NAME: &'static str = "ComponentPermissions"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.ComponentPermissions".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.ComponentPermissions".into() + } +} +/// Metadata describing a component: its data type and permission policies. +/// +/// Stored as the value in the component registry (ComponentId 0x8000). +/// Each registered component has one of these describing what kind of data +/// it holds and who can insert, update, or delete it. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ComponentMetadata { + /// The data structure type of the component's value + #[prost(enumeration = "ComponentType", tag = "1")] + pub component_type: i32, + /// Permission policies for this component, evaluated against regular + /// (member-issued) commits. + #[prost(message, optional, tag = "2")] + pub permissions: ::core::option::Option, + /// Permission policies for this component, evaluated against MLS External + /// Commits (RFC 9420 §12.4.3.2). Absent / unset is equivalent to all-Deny: + /// external committers cannot touch this component. Each component opts in + /// explicitly by setting this field. Combined with the EXTERNAL_COMMIT_POLICY + /// master switch (`allow_external_commit`), this is the per-component declarative + /// authorization for external-commit-driven joins. + #[prost(message, optional, tag = "3")] + pub external_committer_permissions: ::core::option::Option, +} +impl ::prost::Name for ComponentMetadata { + const NAME: &'static str = "ComponentMetadata"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.ComponentMetadata".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.ComponentMetadata".into() + } +} +/// The data structure type of a component's value +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ComponentType { + Unspecified = 0, + /// Opaque bytes, replaced atomically + Bytes = 1, + /// A utf-8 encoded string, replaced atomically + String = 2, + /// A TlsMap\ supporting key-level insert/update/delete via deltas + TlsMapBytesBytes = 3, + /// A TlsMap\ supporting key-level insert/update/delete via deltas + TlsMapInboxIdBytes = 4, + /// A TlsSet supporting insert/remove/remove-by-hash via deltas + TlsSetBytes = 5, + /// A TlsSet supporting insert/remove/remove-by-hash via deltas + TlsSetInboxId = 6, +} +impl ComponentType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "COMPONENT_TYPE_UNSPECIFIED", + Self::Bytes => "COMPONENT_TYPE_BYTES", + Self::String => "COMPONENT_TYPE_STRING", + Self::TlsMapBytesBytes => "COMPONENT_TYPE_TLS_MAP_BYTES_BYTES", + Self::TlsMapInboxIdBytes => "COMPONENT_TYPE_TLS_MAP_INBOX_ID_BYTES", + Self::TlsSetBytes => "COMPONENT_TYPE_TLS_SET_BYTES", + Self::TlsSetInboxId => "COMPONENT_TYPE_TLS_SET_INBOX_ID", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "COMPONENT_TYPE_UNSPECIFIED" => Some(Self::Unspecified), + "COMPONENT_TYPE_BYTES" => Some(Self::Bytes), + "COMPONENT_TYPE_STRING" => Some(Self::String), + "COMPONENT_TYPE_TLS_MAP_BYTES_BYTES" => Some(Self::TlsMapBytesBytes), + "COMPONENT_TYPE_TLS_MAP_INBOX_ID_BYTES" => Some(Self::TlsMapInboxIdBytes), + "COMPONENT_TYPE_TLS_SET_BYTES" => Some(Self::TlsSetBytes), + "COMPONENT_TYPE_TLS_SET_INBOX_ID" => Some(Self::TlsSetInboxId), + _ => None, + } + } +} +/// ContentTypeId is used to identify the type of content stored in a Message. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ContentTypeId { + /// authority governing this content type + #[prost(string, tag = "1")] + pub authority_id: ::prost::alloc::string::String, + /// type identifier + #[prost(string, tag = "2")] + pub type_id: ::prost::alloc::string::String, + /// major version of the type + #[prost(uint32, tag = "3")] + pub version_major: u32, + /// minor version of the type + #[prost(uint32, tag = "4")] + pub version_minor: u32, +} +impl ::prost::Name for ContentTypeId { + const NAME: &'static str = "ContentTypeId"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.ContentTypeId".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.ContentTypeId".into() + } +} +/// EncodedContent bundles the content with metadata identifying its type +/// and parameters required for correct decoding and presentation of the content. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EncodedContent { + /// content type identifier used to match the payload with + /// the correct decoding machinery + #[prost(message, optional, tag = "1")] + pub r#type: ::core::option::Option, + /// optional encoding parameters required to correctly decode the content + #[prost(map = "string, string", tag = "2")] + pub parameters: ::std::collections::HashMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, + /// optional fallback description of the content that can be used in case + /// the client cannot decode or render the content + #[prost(string, optional, tag = "3")] + pub fallback: ::core::option::Option<::prost::alloc::string::String>, + /// optional compression; the value indicates algorithm used to + /// compress the encoded content bytes + #[prost(enumeration = "Compression", optional, tag = "5")] + pub compression: ::core::option::Option, + /// encoded content itself + #[prost(bytes = "vec", tag = "4")] + pub content: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for EncodedContent { + const NAME: &'static str = "EncodedContent"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.EncodedContent".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.EncodedContent".into() + } +} +/// A PlaintextEnvelope is the outermost payload that gets encrypted by MLS +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct PlaintextEnvelope { + /// Selector which declares which version of the EncodedContent this + /// PlaintextEnvelope is + #[prost(oneof = "plaintext_envelope::Content", tags = "1, 2")] + pub content: ::core::option::Option, +} +/// Nested message and enum types in `PlaintextEnvelope`. +pub mod plaintext_envelope { + /// Version 1 of the encrypted envelope + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] + pub struct V1 { + /// Expected to be EncodedContent + #[prost(bytes = "vec", tag = "1")] + pub content: ::prost::alloc::vec::Vec, + /// A unique value that can be used to ensure that the same content can + /// produce different hashes. May be the sender timestamp. + #[prost(string, tag = "2")] + pub idempotency_key: ::prost::alloc::string::String, + } + impl ::prost::Name for V1 { + const NAME: &'static str = "V1"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.PlaintextEnvelope.V1".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.PlaintextEnvelope.V1".into() + } + } + /// Version 2 of the encrypted envelope + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] + pub struct V2 { + /// A unique value that can be used to ensure that the same content can + /// produce different hashes. May be the sender timestamp. + #[prost(string, tag = "1")] + pub idempotency_key: ::prost::alloc::string::String, + #[prost(oneof = "v2::MessageType", tags = "2, 3, 4, 5")] + pub message_type: ::core::option::Option, + } + /// Nested message and enum types in `V2`. + pub mod v2 { + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] + pub enum MessageType { + /// Expected to be EncodedContent + #[prost(bytes, tag = "2")] + Content(::prost::alloc::vec::Vec), + /// Initiator sends a request to receive sync payload + #[prost(message, tag = "3")] + DeviceSyncRequest( + super::super::super::super::device_sync::content::DeviceSyncRequest, + ), + /// Some other authorized installation sends a reply with a link to payload + #[prost(message, tag = "4")] + DeviceSyncReply( + super::super::super::super::device_sync::content::DeviceSyncReply, + ), + /// A serialized user preference update + #[prost(message, tag = "5")] + UserPreferenceUpdate( + super::super::super::super::device_sync::content::V1UserPreferenceUpdate, + ), + } + } + impl ::prost::Name for V2 { + const NAME: &'static str = "V2"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.PlaintextEnvelope.V2".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.PlaintextEnvelope.V2".into() } } - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Kind { - #[prost(enumeration = "PermissionsBasePolicy", tag = "1")] - Base(i32), + /// Selector which declares which version of the EncodedContent this + /// PlaintextEnvelope is + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] + pub enum Content { + #[prost(message, tag = "1")] + V1(V1), #[prost(message, tag = "2")] - AndCondition(AndCondition), - #[prost(message, tag = "3")] - AnyCondition(AnyCondition), - } -} -impl ::prost::Name for PermissionsUpdatePolicy { - const NAME: &'static str = "PermissionsUpdatePolicy"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.PermissionsUpdatePolicy".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.PermissionsUpdatePolicy".into() - } -} -/// Per-component permission policy with separate rules for insert, update, -/// and delete operations. -/// -/// Insert and update are separate because some components need different -/// permission levels for creating vs modifying entries. For example, group -/// membership allows any member to update (installations/sequence ID) but -/// only admins to insert (add a new member). -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ComponentPermissions { - /// Policy for inserting a new value (component does not yet exist) - #[prost(message, optional, tag = "1")] - pub insert_policy: ::core::option::Option, - /// Policy for updating an existing value - #[prost(message, optional, tag = "2")] - pub update_policy: ::core::option::Option, - /// Policy for deleting a value - #[prost(message, optional, tag = "3")] - pub delete_policy: ::core::option::Option, -} -impl ::prost::Name for ComponentPermissions { - const NAME: &'static str = "ComponentPermissions"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.ComponentPermissions".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.ComponentPermissions".into() + V2(V2), } } -/// Metadata describing a component: its data type and permission policies. -/// -/// Stored as the value in the component registry (ComponentId 0x8000). -/// Each registered component has one of these describing what kind of data -/// it holds and who can insert, update, or delete it. -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ComponentMetadata { - /// Permission policies for this component - #[prost(message, optional, tag = "1")] - pub permissions: ::core::option::Option, - /// The data structure type of the component's value - #[prost(enumeration = "ComponentType", tag = "2")] - pub component_type: i32, -} -impl ::prost::Name for ComponentMetadata { - const NAME: &'static str = "ComponentMetadata"; +impl ::prost::Name for PlaintextEnvelope { + const NAME: &'static str = "PlaintextEnvelope"; const PACKAGE: &'static str = "xmtp.mls.message_contents"; fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.ComponentMetadata".into() + "xmtp.mls.message_contents.PlaintextEnvelope".into() } fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.ComponentMetadata".into() + "/xmtp.mls.message_contents.PlaintextEnvelope".into() } } -/// The data structure type of a component's value +/// Recognized compression algorithms +/// protolint:disable ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] -pub enum ComponentType { - Unspecified = 0, - /// Opaque bytes, replaced atomically - Bytes = 1, - /// A TlsMap\ supporting key-level insert/update/delete via deltas - TlsMapBytesBytes = 2, - /// A TlsMap\ supporting key-level insert/update/delete via deltas - TlsMapInboxIdBytes = 3, - /// A TlsSet supporting insert/remove/remove-by-hash via deltas - SetBytes = 4, - /// A TlsSet supporting insert/remove/remove-by-hash via deltas - SetInboxId = 5, +pub enum Compression { + Deflate = 0, + Gzip = 1, } -impl ComponentType { +impl Compression { /// String value of the enum field names used in the ProtoBuf definition. /// /// The values are not transformed in any way and thus are considered stable /// (if the ProtoBuf definition does not change) and safe for programmatic use. pub fn as_str_name(&self) -> &'static str { match self { - Self::Unspecified => "COMPONENT_TYPE_UNSPECIFIED", - Self::Bytes => "COMPONENT_TYPE_BYTES", - Self::TlsMapBytesBytes => "COMPONENT_TYPE_TLS_MAP_BYTES_BYTES", - Self::TlsMapInboxIdBytes => "COMPONENT_TYPE_TLS_MAP_INBOX_ID_BYTES", - Self::SetBytes => "COMPONENT_TYPE_SET_BYTES", - Self::SetInboxId => "COMPONENT_TYPE_SET_INBOX_ID", + Self::Deflate => "COMPRESSION_DEFLATE", + Self::Gzip => "COMPRESSION_GZIP", } } /// Creates an enum from field names used in the ProtoBuf definition. pub fn from_str_name(value: &str) -> ::core::option::Option { match value { - "COMPONENT_TYPE_UNSPECIFIED" => Some(Self::Unspecified), - "COMPONENT_TYPE_BYTES" => Some(Self::Bytes), - "COMPONENT_TYPE_TLS_MAP_BYTES_BYTES" => Some(Self::TlsMapBytesBytes), - "COMPONENT_TYPE_TLS_MAP_INBOX_ID_BYTES" => Some(Self::TlsMapInboxIdBytes), - "COMPONENT_TYPE_SET_BYTES" => Some(Self::SetBytes), - "COMPONENT_TYPE_SET_INBOX_ID" => Some(Self::SetInboxId), + "COMPRESSION_DEFLATE" => Some(Self::Deflate), + "COMPRESSION_GZIP" => Some(Self::Gzip), _ => None, } } } -/// A group member and affected installation IDs +/// v1 shape of the shareable invite blob for QR-code or link-based joining +/// of an XMTP group via an MLS external commit. #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct MembershipChange { - #[prost(bytes = "vec", repeated, tag = "1")] - pub installation_ids: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, - #[prost(string, tag = "2")] - pub account_address: ::prost::alloc::string::String, - #[prost(string, tag = "3")] - pub initiated_by_account_address: ::prost::alloc::string::String, +pub struct ExternalInvitePayloadV1 { + /// Application-defined opaque bytes identifying the service location. + #[prost(bytes = "vec", tag = "1")] + pub service_pointer: ::prost::alloc::vec::Vec, + /// Identifier for the service slot holding the encrypted blob. Format + /// is application-defined (UUID, snowflake, short slot key, etc.) and + /// opaque to libxmtp; the only constraint is that the value is unique + /// within the chosen service. Decoupled from the MLS group_id — + /// rotation may keep this stable (overwrite the same slot) or change + /// it (new slot on the service); the admin chooses per invite. + /// + /// MUST be at least 4 bytes (collision-avoidance floor for tiny + /// services). RECOMMENDED: 16 random bytes when no application- + /// specific scheme is in use. Maximum length is not capped by the + /// protocol; applications should bound it to fit their QR / link + /// transport. + /// + /// After joining, the joiner verifies this matches + /// `EXTERNAL_COMMIT_POLICY.external_group_id` in the group state as + /// defense-in-depth against a stale or swapped QR. + #[prost(bytes = "vec", tag = "2")] + pub external_group_id: ::prost::alloc::vec::Vec, + /// 32 bytes; ChaCha20Poly1305 key used to wrap the GroupInfo. Matches + /// `EXTERNAL_COMMIT_POLICY.symmetric_key` in the group state. + #[prost(bytes = "vec", tag = "3")] + pub symmetric_key: ::prost::alloc::vec::Vec, } -impl ::prost::Name for MembershipChange { - const NAME: &'static str = "MembershipChange"; +impl ::prost::Name for ExternalInvitePayloadV1 { + const NAME: &'static str = "ExternalInvitePayloadV1"; const PACKAGE: &'static str = "xmtp.mls.message_contents"; fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.MembershipChange".into() + "xmtp.mls.message_contents.ExternalInvitePayloadV1".into() } fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.MembershipChange".into() + "/xmtp.mls.message_contents.ExternalInvitePayloadV1".into() } } -/// The group membership change proto +/// Versioned envelope for the shareable invite blob. The application embeds +/// the serialized bytes in whatever transport it prefers (hex, base64, raw +/// QR, NFC, etc.) and stores the corresponding EncryptedGroupInfoBlob on an +/// external service keyed by the v1 payload's `external_group_id`. /// -/// protolint:disable REPEATED_FIELD_NAMES_PLURALIZED -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GroupMembershipChanges { - /// Members that have been added in the commit - #[prost(message, repeated, tag = "1")] - pub members_added: ::prost::alloc::vec::Vec, - /// Members that have been removed in the commit - #[prost(message, repeated, tag = "2")] - pub members_removed: ::prost::alloc::vec::Vec, - /// Installations that have been added in the commit, grouped by member - #[prost(message, repeated, tag = "3")] - pub installations_added: ::prost::alloc::vec::Vec, - /// Installations removed in the commit, grouped by member - #[prost(message, repeated, tag = "4")] - pub installations_removed: ::prost::alloc::vec::Vec, +/// New wire-format variants are added as new oneof entries; readers that +/// don't recognize a variant treat the invite as unparseable and fail +/// closed (no implicit downgrade). +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ExternalInvitePayload { + #[prost(oneof = "external_invite_payload::Version", tags = "1")] + pub version: ::core::option::Option, } -impl ::prost::Name for GroupMembershipChanges { - const NAME: &'static str = "GroupMembershipChanges"; +/// Nested message and enum types in `ExternalInvitePayload`. +pub mod external_invite_payload { + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] + pub enum Version { + #[prost(message, tag = "1")] + V1(super::ExternalInvitePayloadV1), + } +} +impl ::prost::Name for ExternalInvitePayload { + const NAME: &'static str = "ExternalInvitePayload"; const PACKAGE: &'static str = "xmtp.mls.message_contents"; fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.GroupMembershipChanges".into() + "xmtp.mls.message_contents.ExternalInvitePayload".into() } fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.GroupMembershipChanges".into() + "/xmtp.mls.message_contents.ExternalInvitePayload".into() } } -/// A summary of the changes in a commit. -/// Includes added/removed inboxes and changes to metadata -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GroupUpdated { - #[prost(string, tag = "1")] - pub initiated_by_inbox_id: ::prost::alloc::string::String, - /// The inboxes added in the commit - #[prost(message, repeated, tag = "2")] - pub added_inboxes: ::prost::alloc::vec::Vec, - /// The inboxes removed in the commit - #[prost(message, repeated, tag = "3")] - pub removed_inboxes: ::prost::alloc::vec::Vec, - /// The metadata changes in the commit - #[prost(message, repeated, tag = "4")] - pub metadata_field_changes: ::prost::alloc::vec::Vec< - group_updated::MetadataFieldChange, - >, - /// / The inboxes that were removed from the group in response to pending-remove/self-remove requests - #[prost(message, repeated, tag = "5")] - pub left_inboxes: ::prost::alloc::vec::Vec, - /// The inboxes that were added to admin list in the commit - #[prost(message, repeated, tag = "6")] - pub added_admin_inboxes: ::prost::alloc::vec::Vec, - /// The inboxes that were removed from admin list in the commit - #[prost(message, repeated, tag = "7")] - pub removed_admin_inboxes: ::prost::alloc::vec::Vec, - /// The inboxes that were added to super admin list in the commit - #[prost(message, repeated, tag = "8")] - pub added_super_admin_inboxes: ::prost::alloc::vec::Vec, - /// The inboxes that were removed from super admin list in the commit - #[prost(message, repeated, tag = "9")] - pub removed_super_admin_inboxes: ::prost::alloc::vec::Vec, +/// v1 shape of the encrypted-GroupInfo envelope. +/// +/// `epoch` and `group_state_hash` are plaintext metadata serving two +/// distinct purposes: +/// +/// * `epoch` provides a total ordering on uploads. The service accepts +/// an upload iff `upload.epoch > current.epoch` (strictly newer); +/// lower-epoch uploads are stale and rejected outright. +/// +/// * `group_state_hash` is a consistency check at a single epoch. MLS +/// is deterministic — every member that applies the same commit +/// derives identical group state — so two correct uploads at the +/// same epoch MUST carry the same hash. When `upload.epoch == current.epoch`: equal hashes mean an idempotent re-upload (no-op +/// or duplicate-reject); different hashes mean the uploaders are on +/// forked views of the group and the service must refuse to pick a +/// winner. +/// +/// The joiner additionally verifies on download that the blob's `epoch` +/// and `group_state_hash` match the decrypted GroupInfo before +/// attempting to join — closes the "malicious service swapped +/// ciphertext" gap. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct EncryptedGroupInfoBlobV1 { + /// 12 bytes; ChaCha20Poly1305 nonce specific to this ciphertext. + #[prost(bytes = "vec", tag = "1")] + pub nonce: ::prost::alloc::vec::Vec, + /// wrap_payload_symmetric output: AEAD ciphertext over the serialized + /// MlsMessageOut(GroupInfo). + #[prost(bytes = "vec", tag = "2")] + pub ciphertext: ::prost::alloc::vec::Vec, + /// MLS group epoch of the wrapped GroupInfo. Plaintext; the service + /// totally orders uploads by this value — strictly-newer wins, stale + /// is rejected. Joiner verifies against the decrypted GroupInfo + /// before joining. + #[prost(uint64, tag = "3")] + pub epoch: u64, + /// Tree-hash (or equivalent group-state digest) of the wrapped + /// GroupInfo. Plaintext; the service uses this only at equal epochs + /// to detect forks (same epoch + differing hash = forked uploaders). + /// Not used for ordering. Joiner verifies against the decrypted + /// GroupInfo before joining. + #[prost(bytes = "vec", tag = "4")] + pub group_state_hash: ::prost::alloc::vec::Vec, + /// Wall-clock expiry of this blob, in nanoseconds since UNIX epoch. + /// 0 means no expiry. The service uses this as a TTL hint and MAY + /// garbage-collect blobs past their `expires_at_ns` autonomously. + /// The joining client also enforces this — refuses to join from an + /// expired blob even if the service is still serving it. Admin + /// bounds the campaign by setting this at upload time; extending an + /// invite is a re-upload with a later value. + #[prost(uint64, tag = "5")] + pub expires_at_ns: u64, } -/// Nested message and enum types in `GroupUpdated`. -pub mod group_updated { - /// An inbox that was added or removed in this commit - #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] - pub struct Inbox { - #[prost(string, tag = "1")] - pub inbox_id: ::prost::alloc::string::String, - } - impl ::prost::Name for Inbox { - const NAME: &'static str = "Inbox"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.GroupUpdated.Inbox".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.GroupUpdated.Inbox".into() - } +impl ::prost::Name for EncryptedGroupInfoBlobV1 { + const NAME: &'static str = "EncryptedGroupInfoBlobV1"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.EncryptedGroupInfoBlobV1".into() } - /// A summary of a change to the mutable metadata - #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] - pub struct MetadataFieldChange { - /// The field that was changed - #[prost(string, tag = "1")] - pub field_name: ::prost::alloc::string::String, - /// The previous value - #[prost(string, optional, tag = "2")] - pub old_value: ::core::option::Option<::prost::alloc::string::String>, - /// The updated value - #[prost(string, optional, tag = "3")] - pub new_value: ::core::option::Option<::prost::alloc::string::String>, + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.EncryptedGroupInfoBlobV1".into() } - impl ::prost::Name for MetadataFieldChange { - const NAME: &'static str = "MetadataFieldChange"; - const PACKAGE: &'static str = "xmtp.mls.message_contents"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.GroupUpdated.MetadataFieldChange".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.GroupUpdated.MetadataFieldChange".into() - } +} +/// Versioned envelope wrapping a single GroupInfo TLS-serialized bytes +/// under an AEAD scheme (ChaCha20Poly1305 in v1) with a fresh nonce per +/// re-encryption. Stored on the external service and replaced by joiners +/// (with a fresh nonce) after each successful join. +/// +/// New variants represent breaking wire-format changes (different AEAD, +/// different metadata layout). Readers that don't recognize a variant +/// fail closed — the joiner cannot attempt MLS state transitions against +/// a blob it can't validate. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct EncryptedGroupInfoBlob { + #[prost(oneof = "encrypted_group_info_blob::Version", tags = "1")] + pub version: ::core::option::Option, +} +/// Nested message and enum types in `EncryptedGroupInfoBlob`. +pub mod encrypted_group_info_blob { + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] + pub enum Version { + #[prost(message, tag = "1")] + V1(super::EncryptedGroupInfoBlobV1), } } -impl ::prost::Name for GroupUpdated { - const NAME: &'static str = "GroupUpdated"; +impl ::prost::Name for EncryptedGroupInfoBlob { + const NAME: &'static str = "EncryptedGroupInfoBlob"; const PACKAGE: &'static str = "xmtp.mls.message_contents"; fn full_name() -> ::prost::alloc::string::String { - "xmtp.mls.message_contents.GroupUpdated".into() + "xmtp.mls.message_contents.EncryptedGroupInfoBlob".into() } fn type_url() -> ::prost::alloc::string::String { - "/xmtp.mls.message_contents.GroupUpdated".into() + "/xmtp.mls.message_contents.EncryptedGroupInfoBlob".into() } } #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] @@ -1281,3 +1267,390 @@ impl ::prost::Name for GroupMembership { "/xmtp.mls.message_contents.GroupMembership".into() } } +/// Per-member membership state stored inside the GROUP_MEMBERSHIP component +/// as a TlsMap\. Keys are 32-byte inbox ids, values are the +/// encoded bytes of this message. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GroupMembershipEntry { + #[prost(oneof = "group_membership_entry::Version", tags = "1")] + pub version: ::core::option::Option, +} +/// Nested message and enum types in `GroupMembershipEntry`. +pub mod group_membership_entry { + /// V1 of the per-member membership state. + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] + pub struct V1 { + /// Latest identity-update sequence id this client has applied for this + /// member. Validator-checked at bootstrap against the pre-flip + /// `GroupMembership.members\[inbox_id\]` value. + #[prost(uint64, tag = "1")] + pub sequence_id: u64, + /// Installation ids belonging to this member that we previously failed + /// to add (expired key package, validation failure, etc.). Used to + /// suppress retries on later membership updates. + /// + /// Sender-authoritative at migration: the migrator partitions the + /// global `failed_installations` per inbox by walking identity-update + /// history. Receivers accept these bytes as-is — the validator only + /// checks `sequence_id`, so the blast radius of a bad partition is + /// bounded to extra or silenced retries. Installations whose owning + /// inbox can't be determined are dropped. + #[prost(bytes = "vec", repeated, tag = "2")] + pub failed_installations: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + } + impl ::prost::Name for V1 { + const NAME: &'static str = "V1"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.GroupMembershipEntry.V1".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.GroupMembershipEntry.V1".into() + } + } + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] + pub enum Version { + #[prost(message, tag = "1")] + V1(V1), + } +} +impl ::prost::Name for GroupMembershipEntry { + const NAME: &'static str = "GroupMembershipEntry"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.GroupMembershipEntry".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.GroupMembershipEntry".into() + } +} +/// Extension data for proposal support in group context. +/// When present in the group context extensions, indicates the group +/// uses proposal-by-reference flow. +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ProposalSupport { + #[prost(uint32, tag = "1")] + pub version: u32, +} +impl ::prost::Name for ProposalSupport { + const NAME: &'static str = "ProposalSupport"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.ProposalSupport".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.ProposalSupport".into() + } +} +/// v1 external-commit-policy payload. +/// Field-coupling invariants enforced by libxmtp when applying an +/// AppDataUpdate(EXTERNAL_COMMIT_POLICY) proposal: +/// +/// * When `allow_external_commit` transitions to true: `symmetric_key` +/// and `external_group_id` MUST be populated (non-empty, meeting +/// their length requirements) in the same proposal. The two +/// transitions are atomic — there is no window where the bit is on +/// but the invite coordinates are unset. +/// +/// * When `allow_external_commit` transitions to false (revoke): +/// `symmetric_key` and `external_group_id` MUST be cleared (set to +/// empty bytes) in the same proposal. Leaving stale coordinates in +/// the group state after revoke would let a future re-enable +/// accidentally revive a previously-distributed key. +/// +/// * On re-enable (false → true after a prior revoke): the new +/// `symmetric_key` MUST differ from every previously-used value for +/// this group, and the new `external_group_id` SHOULD differ as +/// well. Reusing a revoked key would re-validate every QR ever +/// printed under that key, defeating the revocation. Admin clients +/// are responsible for generating fresh material on each enable. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ExternalCommitPolicyV1 { + /// Master switch for MLS External Commits adding new members. + /// Required for the QR-invite flow. Defaults to false; admins + /// (super-admin by default) opt in via + /// AppDataUpdate(EXTERNAL_COMMIT_POLICY). + /// + /// See the field-coupling invariants in the message-level comment + /// above: enabling MUST populate symmetric_key + external_group_id; + /// revoking (true → false) MUST clear them. + #[prost(bool, tag = "1")] + pub allow_external_commit: bool, + /// Wall-clock auto-disable timestamp (ns since UNIX epoch). + /// 0 = no automatic expiry. After this timestamp the validator + /// rejects all external commits regardless of `allow_external_commit`. + /// Lets admins issue time-bounded invite campaigns without having to + /// come back and flip the bit manually. + #[prost(uint64, tag = "2")] + pub expires_at_ns: u64, + /// Maximum staleness of the GroupInfo referenced by an external + /// commit, in nanoseconds since GroupInfo export. 0 = no staleness + /// limit. External commits whose referenced GroupInfo was exported + /// more than `expire_in_ns` ago are rejected. Narrows the replay + /// window for stolen-blob attacks and forces re-export frequency. + #[prost(uint64, tag = "3")] + pub expire_in_ns: u64, + /// 32-byte ChaCha20Poly1305 key used to wrap the EncryptedGroupInfoBlob + /// for the currently-active invite. Carried in the group state so any + /// member (especially a just-joined external committer) can re-export + /// GroupInfo and re-upload a refreshed blob under the same key after a + /// join — without this, a printed QR / link would die the moment the + /// issuing admin went offline. + /// + /// The QR carries the same key bytes. Rotation = admin sets a new value + /// here in a single AppDataUpdate(EXTERNAL_COMMIT_POLICY) proposal AND + /// issues a new QR carrying the matching key; old QR holders' keys no + /// longer decrypt blobs the service serves under the rotated slot. + /// + /// Length MUST be exactly 32 bytes when populated. Empty (zero-length) + /// means no active invite — and MUST coincide with + /// `allow_external_commit == false` (see the field-coupling invariants + /// at the top of this message). Revoking the invite MUST clear this + /// field; re-enabling MUST populate it with a freshly-generated value + /// distinct from any previously-used key for this group. + /// + /// Note: the service_pointer (where the blob lives) is intentionally + /// NOT stored in the group. It is per-QR application-defined opaque + /// bytes; different invites for the same group may point at different + /// services. Joiners use the service_pointer from the QR they scanned. + #[prost(bytes = "vec", tag = "4")] + pub symmetric_key: ::prost::alloc::vec::Vec, + /// Identifier for the service slot holding the active invite's + /// encrypted blob. Application-defined opaque bytes (UUID, snowflake, + /// short slot key, etc.); decoupled from the MLS group_id. Admins + /// MAY rotate the symmetric_key while keeping this stable (overwrite + /// the same slot on the service) or change both together (new slot, + /// leaves the old slot orphaned for application-side GC). + /// + /// The QR carries the same value. The joiner verifies that the QR's + /// `external_group_id` equals this field after joining, as + /// defense-in-depth against a stale or swapped QR. Mismatch indicates + /// the admin rotated to a new slot after the QR was minted; the + /// joining client SHOULD treat the just-published commit as orphaned + /// (it validates fine, but the refreshed blob the joiner would upload + /// to the old slot will not be reachable by holders of the new QR). + /// + /// MUST be at least 4 bytes when populated (collision-avoidance floor + /// for tiny services). RECOMMENDED: 16 random bytes when no + /// application-specific scheme is in use. Empty (zero-length) means + /// no active invite — and MUST coincide with + /// `allow_external_commit == false` (see the field-coupling + /// invariants at the top of this message). Revoking the invite MUST + /// clear this field; re-enabling SHOULD use a freshly-generated value + /// (reusing a prior `external_group_id` is permitted only when the + /// admin intends to overwrite the old service slot — typically the + /// admin generates a new value to leave the prior slot orphaned). + #[prost(bytes = "vec", tag = "5")] + pub external_group_id: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for ExternalCommitPolicyV1 { + const NAME: &'static str = "ExternalCommitPolicyV1"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.ExternalCommitPolicyV1".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.ExternalCommitPolicyV1".into() + } +} +/// Versioned envelope. New variants are added as new oneof variants; +/// readers that don't recognize a variant treat the policy as default +/// (all fields zero) per the standard unknown-variant tolerance rules. +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct ExternalCommitPolicyEntry { + #[prost(oneof = "external_commit_policy_entry::Version", tags = "1")] + pub version: ::core::option::Option, +} +/// Nested message and enum types in `ExternalCommitPolicyEntry`. +pub mod external_commit_policy_entry { + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)] + pub enum Version { + #[prost(message, tag = "1")] + V1(super::ExternalCommitPolicyV1), + } +} +impl ::prost::Name for ExternalCommitPolicyEntry { + const NAME: &'static str = "ExternalCommitPolicyEntry"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.ExternalCommitPolicyEntry".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.ExternalCommitPolicyEntry".into() + } +} +/// A group member and affected installation IDs +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct MembershipChange { + #[prost(bytes = "vec", repeated, tag = "1")] + pub installation_ids: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + #[prost(string, tag = "2")] + pub account_address: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub initiated_by_account_address: ::prost::alloc::string::String, +} +impl ::prost::Name for MembershipChange { + const NAME: &'static str = "MembershipChange"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.MembershipChange".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.MembershipChange".into() + } +} +/// The group membership change proto +/// +/// protolint:disable REPEATED_FIELD_NAMES_PLURALIZED +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GroupMembershipChanges { + /// Members that have been added in the commit + #[prost(message, repeated, tag = "1")] + pub members_added: ::prost::alloc::vec::Vec, + /// Members that have been removed in the commit + #[prost(message, repeated, tag = "2")] + pub members_removed: ::prost::alloc::vec::Vec, + /// Installations that have been added in the commit, grouped by member + #[prost(message, repeated, tag = "3")] + pub installations_added: ::prost::alloc::vec::Vec, + /// Installations removed in the commit, grouped by member + #[prost(message, repeated, tag = "4")] + pub installations_removed: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for GroupMembershipChanges { + const NAME: &'static str = "GroupMembershipChanges"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.GroupMembershipChanges".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.GroupMembershipChanges".into() + } +} +/// A summary of the changes in a commit. +/// Includes added/removed inboxes and changes to metadata +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GroupUpdated { + #[prost(string, tag = "1")] + pub initiated_by_inbox_id: ::prost::alloc::string::String, + /// The inboxes added in the commit + #[prost(message, repeated, tag = "2")] + pub added_inboxes: ::prost::alloc::vec::Vec, + /// The inboxes removed in the commit + #[prost(message, repeated, tag = "3")] + pub removed_inboxes: ::prost::alloc::vec::Vec, + /// The metadata changes in the commit + #[prost(message, repeated, tag = "4")] + pub metadata_field_changes: ::prost::alloc::vec::Vec< + group_updated::MetadataFieldChange, + >, + /// / The inboxes that were removed from the group in response to pending-remove/self-remove requests + #[prost(message, repeated, tag = "5")] + pub left_inboxes: ::prost::alloc::vec::Vec, + /// The inboxes that were added to admin list in the commit + #[prost(message, repeated, tag = "6")] + pub added_admin_inboxes: ::prost::alloc::vec::Vec, + /// The inboxes that were removed from admin list in the commit + #[prost(message, repeated, tag = "7")] + pub removed_admin_inboxes: ::prost::alloc::vec::Vec, + /// The inboxes that were added to super admin list in the commit + #[prost(message, repeated, tag = "8")] + pub added_super_admin_inboxes: ::prost::alloc::vec::Vec, + /// The inboxes that were removed from super admin list in the commit + #[prost(message, repeated, tag = "9")] + pub removed_super_admin_inboxes: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `GroupUpdated`. +pub mod group_updated { + /// An inbox that was added or removed in this commit + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] + pub struct Inbox { + #[prost(string, tag = "1")] + pub inbox_id: ::prost::alloc::string::String, + } + impl ::prost::Name for Inbox { + const NAME: &'static str = "Inbox"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.GroupUpdated.Inbox".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.GroupUpdated.Inbox".into() + } + } + /// A summary of a change to the mutable metadata + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] + pub struct MetadataFieldChange { + /// The field that was changed + #[prost(string, tag = "1")] + pub field_name: ::prost::alloc::string::String, + /// The previous value + #[prost(string, optional, tag = "2")] + pub old_value: ::core::option::Option<::prost::alloc::string::String>, + /// The updated value + #[prost(string, optional, tag = "3")] + pub new_value: ::core::option::Option<::prost::alloc::string::String>, + } + impl ::prost::Name for MetadataFieldChange { + const NAME: &'static str = "MetadataFieldChange"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.GroupUpdated.MetadataFieldChange".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.GroupUpdated.MetadataFieldChange".into() + } + } +} +impl ::prost::Name for GroupUpdated { + const NAME: &'static str = "GroupUpdated"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.GroupUpdated".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.GroupUpdated".into() + } +} +/// Message for group mutable metadata +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GroupMutableMetadataV1 { + /// Map to store various metadata attributes (Group name, etc.) + #[prost(map = "string, string", tag = "1")] + pub attributes: ::std::collections::HashMap< + ::prost::alloc::string::String, + ::prost::alloc::string::String, + >, + #[prost(message, optional, tag = "2")] + pub admin_list: ::core::option::Option, + /// Creator starts as only super_admin + /// Only super_admin can add/remove other super_admin + #[prost(message, optional, tag = "3")] + pub super_admin_list: ::core::option::Option, +} +impl ::prost::Name for GroupMutableMetadataV1 { + const NAME: &'static str = "GroupMutableMetadataV1"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.GroupMutableMetadataV1".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.GroupMutableMetadataV1".into() + } +} +/// Wrapper around a list of repeated Inbox Ids +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Inboxes { + #[prost(string, repeated, tag = "1")] + pub inbox_ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +impl ::prost::Name for Inboxes { + const NAME: &'static str = "Inboxes"; + const PACKAGE: &'static str = "xmtp.mls.message_contents"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.mls.message_contents.Inboxes".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.mls.message_contents.Inboxes".into() + } +} diff --git a/crates/xmtp_proto/src/gen/xmtp.mls.message_contents.serde.rs b/crates/xmtp_proto/src/gen/xmtp.mls.message_contents.serde.rs index 09b6e01452..edac2dc005 100644 --- a/crates/xmtp_proto/src/gen/xmtp.mls.message_contents.serde.rs +++ b/crates/xmtp_proto/src/gen/xmtp.mls.message_contents.serde.rs @@ -225,21 +225,27 @@ impl serde::Serialize for ComponentMetadata { { use serde::ser::SerializeStruct; let mut len = 0; + if self.component_type != 0 { + len += 1; + } if self.permissions.is_some() { len += 1; } - if self.component_type != 0 { + if self.external_committer_permissions.is_some() { len += 1; } let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.ComponentMetadata", len)?; - if let Some(v) = self.permissions.as_ref() { - struct_ser.serialize_field("permissions", v)?; - } if self.component_type != 0 { let v = ComponentType::try_from(self.component_type) .map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", self.component_type)))?; struct_ser.serialize_field("component_type", &v)?; } + if let Some(v) = self.permissions.as_ref() { + struct_ser.serialize_field("permissions", v)?; + } + if let Some(v) = self.external_committer_permissions.as_ref() { + struct_ser.serialize_field("external_committer_permissions", v)?; + } struct_ser.end() } } @@ -250,15 +256,18 @@ impl<'de> serde::Deserialize<'de> for ComponentMetadata { D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "permissions", "component_type", "componentType", + "permissions", + "external_committer_permissions", + "externalCommitterPermissions", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - Permissions, ComponentType, + Permissions, + ExternalCommitterPermissions, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -281,8 +290,9 @@ impl<'de> serde::Deserialize<'de> for ComponentMetadata { E: serde::de::Error, { match value { - "permissions" => Ok(GeneratedField::Permissions), "componentType" | "component_type" => Ok(GeneratedField::ComponentType), + "permissions" => Ok(GeneratedField::Permissions), + "externalCommitterPermissions" | "external_committer_permissions" => Ok(GeneratedField::ExternalCommitterPermissions), _ => Ok(GeneratedField::__SkipField__), } } @@ -302,21 +312,28 @@ impl<'de> serde::Deserialize<'de> for ComponentMetadata { where V: serde::de::MapAccess<'de>, { - let mut permissions__ = None; let mut component_type__ = None; + let mut permissions__ = None; + let mut external_committer_permissions__ = None; while let Some(k) = map_.next_key()? { match k { + GeneratedField::ComponentType => { + if component_type__.is_some() { + return Err(serde::de::Error::duplicate_field("componentType")); + } + component_type__ = Some(map_.next_value::()? as i32); + } GeneratedField::Permissions => { if permissions__.is_some() { return Err(serde::de::Error::duplicate_field("permissions")); } permissions__ = map_.next_value()?; } - GeneratedField::ComponentType => { - if component_type__.is_some() { - return Err(serde::de::Error::duplicate_field("componentType")); + GeneratedField::ExternalCommitterPermissions => { + if external_committer_permissions__.is_some() { + return Err(serde::de::Error::duplicate_field("externalCommitterPermissions")); } - component_type__ = Some(map_.next_value::()? as i32); + external_committer_permissions__ = map_.next_value()?; } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; @@ -324,8 +341,9 @@ impl<'de> serde::Deserialize<'de> for ComponentMetadata { } } Ok(ComponentMetadata { - permissions: permissions__, component_type: component_type__.unwrap_or_default(), + permissions: permissions__, + external_committer_permissions: external_committer_permissions__, }) } } @@ -473,10 +491,11 @@ impl serde::Serialize for ComponentType { let variant = match self { Self::Unspecified => "COMPONENT_TYPE_UNSPECIFIED", Self::Bytes => "COMPONENT_TYPE_BYTES", + Self::String => "COMPONENT_TYPE_STRING", Self::TlsMapBytesBytes => "COMPONENT_TYPE_TLS_MAP_BYTES_BYTES", Self::TlsMapInboxIdBytes => "COMPONENT_TYPE_TLS_MAP_INBOX_ID_BYTES", - Self::SetBytes => "COMPONENT_TYPE_SET_BYTES", - Self::SetInboxId => "COMPONENT_TYPE_SET_INBOX_ID", + Self::TlsSetBytes => "COMPONENT_TYPE_TLS_SET_BYTES", + Self::TlsSetInboxId => "COMPONENT_TYPE_TLS_SET_INBOX_ID", }; serializer.serialize_str(variant) } @@ -490,10 +509,11 @@ impl<'de> serde::Deserialize<'de> for ComponentType { const FIELDS: &[&str] = &[ "COMPONENT_TYPE_UNSPECIFIED", "COMPONENT_TYPE_BYTES", + "COMPONENT_TYPE_STRING", "COMPONENT_TYPE_TLS_MAP_BYTES_BYTES", "COMPONENT_TYPE_TLS_MAP_INBOX_ID_BYTES", - "COMPONENT_TYPE_SET_BYTES", - "COMPONENT_TYPE_SET_INBOX_ID", + "COMPONENT_TYPE_TLS_SET_BYTES", + "COMPONENT_TYPE_TLS_SET_INBOX_ID", ]; struct GeneratedVisitor; @@ -536,10 +556,11 @@ impl<'de> serde::Deserialize<'de> for ComponentType { match value { "COMPONENT_TYPE_UNSPECIFIED" => Ok(ComponentType::Unspecified), "COMPONENT_TYPE_BYTES" => Ok(ComponentType::Bytes), + "COMPONENT_TYPE_STRING" => Ok(ComponentType::String), "COMPONENT_TYPE_TLS_MAP_BYTES_BYTES" => Ok(ComponentType::TlsMapBytesBytes), "COMPONENT_TYPE_TLS_MAP_INBOX_ID_BYTES" => Ok(ComponentType::TlsMapInboxIdBytes), - "COMPONENT_TYPE_SET_BYTES" => Ok(ComponentType::SetBytes), - "COMPONENT_TYPE_SET_INBOX_ID" => Ok(ComponentType::SetInboxId), + "COMPONENT_TYPE_TLS_SET_BYTES" => Ok(ComponentType::TlsSetBytes), + "COMPONENT_TYPE_TLS_SET_INBOX_ID" => Ok(ComponentType::TlsSetInboxId), _ => Err(serde::de::Error::unknown_variant(value, FIELDS)), } } @@ -1137,7 +1158,7 @@ impl<'de> serde::Deserialize<'de> for EncodedContent { deserializer.deserialize_struct("xmtp.mls.message_contents.EncodedContent", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for GroupMembership { +impl serde::Serialize for EncryptedGroupInfoBlob { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -1145,40 +1166,33 @@ impl serde::Serialize for GroupMembership { { use serde::ser::SerializeStruct; let mut len = 0; - if !self.members.is_empty() { - len += 1; - } - if !self.failed_installations.is_empty() { + if self.version.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.GroupMembership", len)?; - if !self.members.is_empty() { - let v: std::collections::HashMap<_, _> = self.members.iter() - .map(|(k, v)| (k, v.to_string())).collect(); - struct_ser.serialize_field("members", &v)?; - } - if !self.failed_installations.is_empty() { - struct_ser.serialize_field("failed_installations", &self.failed_installations.iter().map(pbjson::private::base64::encode).collect::>())?; + let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.EncryptedGroupInfoBlob", len)?; + if let Some(v) = self.version.as_ref() { + match v { + encrypted_group_info_blob::Version::V1(v) => { + struct_ser.serialize_field("v1", v)?; + } + } } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for GroupMembership { +impl<'de> serde::Deserialize<'de> for EncryptedGroupInfoBlob { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "members", - "failed_installations", - "failedInstallations", + "v1", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - Members, - FailedInstallations, + V1, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -1201,8 +1215,7 @@ impl<'de> serde::Deserialize<'de> for GroupMembership { E: serde::de::Error, { match value { - "members" => Ok(GeneratedField::Members), - "failedInstallations" | "failed_installations" => Ok(GeneratedField::FailedInstallations), + "v1" => Ok(GeneratedField::V1), _ => Ok(GeneratedField::__SkipField__), } } @@ -1212,53 +1225,40 @@ impl<'de> serde::Deserialize<'de> for GroupMembership { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GroupMembership; + type Value = EncryptedGroupInfoBlob; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.message_contents.GroupMembership") + formatter.write_str("struct xmtp.mls.message_contents.EncryptedGroupInfoBlob") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { - let mut members__ = None; - let mut failed_installations__ = None; + let mut version__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::Members => { - if members__.is_some() { - return Err(serde::de::Error::duplicate_field("members")); - } - members__ = Some( - map_.next_value::>>()? - .into_iter().map(|(k,v)| (k, v.0)).collect() - ); - } - GeneratedField::FailedInstallations => { - if failed_installations__.is_some() { - return Err(serde::de::Error::duplicate_field("failedInstallations")); + GeneratedField::V1 => { + if version__.is_some() { + return Err(serde::de::Error::duplicate_field("v1")); } - failed_installations__ = - Some(map_.next_value::>>()? - .into_iter().map(|x| x.0).collect()) - ; + version__ = map_.next_value::<::std::option::Option<_>>()?.map(encrypted_group_info_blob::Version::V1) +; } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } } } - Ok(GroupMembership { - members: members__.unwrap_or_default(), - failed_installations: failed_installations__.unwrap_or_default(), + Ok(EncryptedGroupInfoBlob { + version: version__, }) } } - deserializer.deserialize_struct("xmtp.mls.message_contents.GroupMembership", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls.message_contents.EncryptedGroupInfoBlob", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for GroupMembershipChanges { +impl serde::Serialize for EncryptedGroupInfoBlobV1 { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -1266,57 +1266,73 @@ impl serde::Serialize for GroupMembershipChanges { { use serde::ser::SerializeStruct; let mut len = 0; - if !self.members_added.is_empty() { + if !self.nonce.is_empty() { len += 1; } - if !self.members_removed.is_empty() { + if !self.ciphertext.is_empty() { len += 1; } - if !self.installations_added.is_empty() { + if self.epoch != 0 { len += 1; } - if !self.installations_removed.is_empty() { + if !self.group_state_hash.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.GroupMembershipChanges", len)?; - if !self.members_added.is_empty() { - struct_ser.serialize_field("members_added", &self.members_added)?; + if self.expires_at_ns != 0 { + len += 1; } - if !self.members_removed.is_empty() { - struct_ser.serialize_field("members_removed", &self.members_removed)?; + let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.EncryptedGroupInfoBlobV1", len)?; + if !self.nonce.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("nonce", pbjson::private::base64::encode(&self.nonce).as_str())?; } - if !self.installations_added.is_empty() { - struct_ser.serialize_field("installations_added", &self.installations_added)?; + if !self.ciphertext.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("ciphertext", pbjson::private::base64::encode(&self.ciphertext).as_str())?; } - if !self.installations_removed.is_empty() { - struct_ser.serialize_field("installations_removed", &self.installations_removed)?; + if self.epoch != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("epoch", ToString::to_string(&self.epoch).as_str())?; + } + if !self.group_state_hash.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("group_state_hash", pbjson::private::base64::encode(&self.group_state_hash).as_str())?; + } + if self.expires_at_ns != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("expires_at_ns", ToString::to_string(&self.expires_at_ns).as_str())?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for GroupMembershipChanges { +impl<'de> serde::Deserialize<'de> for EncryptedGroupInfoBlobV1 { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "members_added", - "membersAdded", - "members_removed", - "membersRemoved", - "installations_added", - "installationsAdded", - "installations_removed", - "installationsRemoved", + "nonce", + "ciphertext", + "epoch", + "group_state_hash", + "groupStateHash", + "expires_at_ns", + "expiresAtNs", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - MembersAdded, - MembersRemoved, - InstallationsAdded, - InstallationsRemoved, + Nonce, + Ciphertext, + Epoch, + GroupStateHash, + ExpiresAtNs, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -1339,10 +1355,11 @@ impl<'de> serde::Deserialize<'de> for GroupMembershipChanges { E: serde::de::Error, { match value { - "membersAdded" | "members_added" => Ok(GeneratedField::MembersAdded), - "membersRemoved" | "members_removed" => Ok(GeneratedField::MembersRemoved), - "installationsAdded" | "installations_added" => Ok(GeneratedField::InstallationsAdded), - "installationsRemoved" | "installations_removed" => Ok(GeneratedField::InstallationsRemoved), + "nonce" => Ok(GeneratedField::Nonce), + "ciphertext" => Ok(GeneratedField::Ciphertext), + "epoch" => Ok(GeneratedField::Epoch), + "groupStateHash" | "group_state_hash" => Ok(GeneratedField::GroupStateHash), + "expiresAtNs" | "expires_at_ns" => Ok(GeneratedField::ExpiresAtNs), _ => Ok(GeneratedField::__SkipField__), } } @@ -1352,60 +1369,1098 @@ impl<'de> serde::Deserialize<'de> for GroupMembershipChanges { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GroupMembershipChanges; + type Value = EncryptedGroupInfoBlobV1; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.message_contents.GroupMembershipChanges") + formatter.write_str("struct xmtp.mls.message_contents.EncryptedGroupInfoBlobV1") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { - let mut members_added__ = None; - let mut members_removed__ = None; - let mut installations_added__ = None; - let mut installations_removed__ = None; + let mut nonce__ = None; + let mut ciphertext__ = None; + let mut epoch__ = None; + let mut group_state_hash__ = None; + let mut expires_at_ns__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::MembersAdded => { - if members_added__.is_some() { - return Err(serde::de::Error::duplicate_field("membersAdded")); + GeneratedField::Nonce => { + if nonce__.is_some() { + return Err(serde::de::Error::duplicate_field("nonce")); } - members_added__ = Some(map_.next_value()?); + nonce__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; } - GeneratedField::MembersRemoved => { - if members_removed__.is_some() { - return Err(serde::de::Error::duplicate_field("membersRemoved")); + GeneratedField::Ciphertext => { + if ciphertext__.is_some() { + return Err(serde::de::Error::duplicate_field("ciphertext")); } - members_removed__ = Some(map_.next_value()?); + ciphertext__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; } - GeneratedField::InstallationsAdded => { - if installations_added__.is_some() { - return Err(serde::de::Error::duplicate_field("installationsAdded")); + GeneratedField::Epoch => { + if epoch__.is_some() { + return Err(serde::de::Error::duplicate_field("epoch")); } - installations_added__ = Some(map_.next_value()?); + epoch__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; } - GeneratedField::InstallationsRemoved => { - if installations_removed__.is_some() { - return Err(serde::de::Error::duplicate_field("installationsRemoved")); + GeneratedField::GroupStateHash => { + if group_state_hash__.is_some() { + return Err(serde::de::Error::duplicate_field("groupStateHash")); } - installations_removed__ = Some(map_.next_value()?); + group_state_hash__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::ExpiresAtNs => { + if expires_at_ns__.is_some() { + return Err(serde::de::Error::duplicate_field("expiresAtNs")); + } + expires_at_ns__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } } } - Ok(GroupMembershipChanges { - members_added: members_added__.unwrap_or_default(), - members_removed: members_removed__.unwrap_or_default(), - installations_added: installations_added__.unwrap_or_default(), - installations_removed: installations_removed__.unwrap_or_default(), + Ok(EncryptedGroupInfoBlobV1 { + nonce: nonce__.unwrap_or_default(), + ciphertext: ciphertext__.unwrap_or_default(), + epoch: epoch__.unwrap_or_default(), + group_state_hash: group_state_hash__.unwrap_or_default(), + expires_at_ns: expires_at_ns__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("xmtp.mls.message_contents.GroupMembershipChanges", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls.message_contents.EncryptedGroupInfoBlobV1", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for ExternalCommitPolicyEntry { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.version.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.ExternalCommitPolicyEntry", len)?; + if let Some(v) = self.version.as_ref() { + match v { + external_commit_policy_entry::Version::V1(v) => { + struct_ser.serialize_field("v1", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ExternalCommitPolicyEntry { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "v1", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + V1, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "v1" => Ok(GeneratedField::V1), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ExternalCommitPolicyEntry; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.message_contents.ExternalCommitPolicyEntry") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut version__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::V1 => { + if version__.is_some() { + return Err(serde::de::Error::duplicate_field("v1")); + } + version__ = map_.next_value::<::std::option::Option<_>>()?.map(external_commit_policy_entry::Version::V1) +; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(ExternalCommitPolicyEntry { + version: version__, + }) + } + } + deserializer.deserialize_struct("xmtp.mls.message_contents.ExternalCommitPolicyEntry", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for ExternalCommitPolicyV1 { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.allow_external_commit { + len += 1; + } + if self.expires_at_ns != 0 { + len += 1; + } + if self.expire_in_ns != 0 { + len += 1; + } + if !self.symmetric_key.is_empty() { + len += 1; + } + if !self.external_group_id.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.ExternalCommitPolicyV1", len)?; + if self.allow_external_commit { + struct_ser.serialize_field("allow_external_commit", &self.allow_external_commit)?; + } + if self.expires_at_ns != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("expires_at_ns", ToString::to_string(&self.expires_at_ns).as_str())?; + } + if self.expire_in_ns != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("expire_in_ns", ToString::to_string(&self.expire_in_ns).as_str())?; + } + if !self.symmetric_key.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("symmetric_key", pbjson::private::base64::encode(&self.symmetric_key).as_str())?; + } + if !self.external_group_id.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("external_group_id", pbjson::private::base64::encode(&self.external_group_id).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ExternalCommitPolicyV1 { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "allow_external_commit", + "allowExternalCommit", + "expires_at_ns", + "expiresAtNs", + "expire_in_ns", + "expireInNs", + "symmetric_key", + "symmetricKey", + "external_group_id", + "externalGroupId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + AllowExternalCommit, + ExpiresAtNs, + ExpireInNs, + SymmetricKey, + ExternalGroupId, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "allowExternalCommit" | "allow_external_commit" => Ok(GeneratedField::AllowExternalCommit), + "expiresAtNs" | "expires_at_ns" => Ok(GeneratedField::ExpiresAtNs), + "expireInNs" | "expire_in_ns" => Ok(GeneratedField::ExpireInNs), + "symmetricKey" | "symmetric_key" => Ok(GeneratedField::SymmetricKey), + "externalGroupId" | "external_group_id" => Ok(GeneratedField::ExternalGroupId), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ExternalCommitPolicyV1; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.message_contents.ExternalCommitPolicyV1") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut allow_external_commit__ = None; + let mut expires_at_ns__ = None; + let mut expire_in_ns__ = None; + let mut symmetric_key__ = None; + let mut external_group_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::AllowExternalCommit => { + if allow_external_commit__.is_some() { + return Err(serde::de::Error::duplicate_field("allowExternalCommit")); + } + allow_external_commit__ = Some(map_.next_value()?); + } + GeneratedField::ExpiresAtNs => { + if expires_at_ns__.is_some() { + return Err(serde::de::Error::duplicate_field("expiresAtNs")); + } + expires_at_ns__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::ExpireInNs => { + if expire_in_ns__.is_some() { + return Err(serde::de::Error::duplicate_field("expireInNs")); + } + expire_in_ns__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::SymmetricKey => { + if symmetric_key__.is_some() { + return Err(serde::de::Error::duplicate_field("symmetricKey")); + } + symmetric_key__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::ExternalGroupId => { + if external_group_id__.is_some() { + return Err(serde::de::Error::duplicate_field("externalGroupId")); + } + external_group_id__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(ExternalCommitPolicyV1 { + allow_external_commit: allow_external_commit__.unwrap_or_default(), + expires_at_ns: expires_at_ns__.unwrap_or_default(), + expire_in_ns: expire_in_ns__.unwrap_or_default(), + symmetric_key: symmetric_key__.unwrap_or_default(), + external_group_id: external_group_id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.mls.message_contents.ExternalCommitPolicyV1", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for ExternalInvitePayload { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.version.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.ExternalInvitePayload", len)?; + if let Some(v) = self.version.as_ref() { + match v { + external_invite_payload::Version::V1(v) => { + struct_ser.serialize_field("v1", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ExternalInvitePayload { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "v1", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + V1, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "v1" => Ok(GeneratedField::V1), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ExternalInvitePayload; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.message_contents.ExternalInvitePayload") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut version__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::V1 => { + if version__.is_some() { + return Err(serde::de::Error::duplicate_field("v1")); + } + version__ = map_.next_value::<::std::option::Option<_>>()?.map(external_invite_payload::Version::V1) +; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(ExternalInvitePayload { + version: version__, + }) + } + } + deserializer.deserialize_struct("xmtp.mls.message_contents.ExternalInvitePayload", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for ExternalInvitePayloadV1 { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.service_pointer.is_empty() { + len += 1; + } + if !self.external_group_id.is_empty() { + len += 1; + } + if !self.symmetric_key.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.ExternalInvitePayloadV1", len)?; + if !self.service_pointer.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("service_pointer", pbjson::private::base64::encode(&self.service_pointer).as_str())?; + } + if !self.external_group_id.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("external_group_id", pbjson::private::base64::encode(&self.external_group_id).as_str())?; + } + if !self.symmetric_key.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("symmetric_key", pbjson::private::base64::encode(&self.symmetric_key).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ExternalInvitePayloadV1 { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "service_pointer", + "servicePointer", + "external_group_id", + "externalGroupId", + "symmetric_key", + "symmetricKey", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + ServicePointer, + ExternalGroupId, + SymmetricKey, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "servicePointer" | "service_pointer" => Ok(GeneratedField::ServicePointer), + "externalGroupId" | "external_group_id" => Ok(GeneratedField::ExternalGroupId), + "symmetricKey" | "symmetric_key" => Ok(GeneratedField::SymmetricKey), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ExternalInvitePayloadV1; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.message_contents.ExternalInvitePayloadV1") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut service_pointer__ = None; + let mut external_group_id__ = None; + let mut symmetric_key__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::ServicePointer => { + if service_pointer__.is_some() { + return Err(serde::de::Error::duplicate_field("servicePointer")); + } + service_pointer__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::ExternalGroupId => { + if external_group_id__.is_some() { + return Err(serde::de::Error::duplicate_field("externalGroupId")); + } + external_group_id__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::SymmetricKey => { + if symmetric_key__.is_some() { + return Err(serde::de::Error::duplicate_field("symmetricKey")); + } + symmetric_key__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(ExternalInvitePayloadV1 { + service_pointer: service_pointer__.unwrap_or_default(), + external_group_id: external_group_id__.unwrap_or_default(), + symmetric_key: symmetric_key__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.mls.message_contents.ExternalInvitePayloadV1", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GroupMembership { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.members.is_empty() { + len += 1; + } + if !self.failed_installations.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.GroupMembership", len)?; + if !self.members.is_empty() { + let v: std::collections::HashMap<_, _> = self.members.iter() + .map(|(k, v)| (k, v.to_string())).collect(); + struct_ser.serialize_field("members", &v)?; + } + if !self.failed_installations.is_empty() { + struct_ser.serialize_field("failed_installations", &self.failed_installations.iter().map(pbjson::private::base64::encode).collect::>())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GroupMembership { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "members", + "failed_installations", + "failedInstallations", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Members, + FailedInstallations, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "members" => Ok(GeneratedField::Members), + "failedInstallations" | "failed_installations" => Ok(GeneratedField::FailedInstallations), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GroupMembership; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.message_contents.GroupMembership") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut members__ = None; + let mut failed_installations__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Members => { + if members__.is_some() { + return Err(serde::de::Error::duplicate_field("members")); + } + members__ = Some( + map_.next_value::>>()? + .into_iter().map(|(k,v)| (k, v.0)).collect() + ); + } + GeneratedField::FailedInstallations => { + if failed_installations__.is_some() { + return Err(serde::de::Error::duplicate_field("failedInstallations")); + } + failed_installations__ = + Some(map_.next_value::>>()? + .into_iter().map(|x| x.0).collect()) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(GroupMembership { + members: members__.unwrap_or_default(), + failed_installations: failed_installations__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.mls.message_contents.GroupMembership", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GroupMembershipChanges { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.members_added.is_empty() { + len += 1; + } + if !self.members_removed.is_empty() { + len += 1; + } + if !self.installations_added.is_empty() { + len += 1; + } + if !self.installations_removed.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.GroupMembershipChanges", len)?; + if !self.members_added.is_empty() { + struct_ser.serialize_field("members_added", &self.members_added)?; + } + if !self.members_removed.is_empty() { + struct_ser.serialize_field("members_removed", &self.members_removed)?; + } + if !self.installations_added.is_empty() { + struct_ser.serialize_field("installations_added", &self.installations_added)?; + } + if !self.installations_removed.is_empty() { + struct_ser.serialize_field("installations_removed", &self.installations_removed)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GroupMembershipChanges { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "members_added", + "membersAdded", + "members_removed", + "membersRemoved", + "installations_added", + "installationsAdded", + "installations_removed", + "installationsRemoved", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + MembersAdded, + MembersRemoved, + InstallationsAdded, + InstallationsRemoved, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "membersAdded" | "members_added" => Ok(GeneratedField::MembersAdded), + "membersRemoved" | "members_removed" => Ok(GeneratedField::MembersRemoved), + "installationsAdded" | "installations_added" => Ok(GeneratedField::InstallationsAdded), + "installationsRemoved" | "installations_removed" => Ok(GeneratedField::InstallationsRemoved), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GroupMembershipChanges; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.message_contents.GroupMembershipChanges") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut members_added__ = None; + let mut members_removed__ = None; + let mut installations_added__ = None; + let mut installations_removed__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::MembersAdded => { + if members_added__.is_some() { + return Err(serde::de::Error::duplicate_field("membersAdded")); + } + members_added__ = Some(map_.next_value()?); + } + GeneratedField::MembersRemoved => { + if members_removed__.is_some() { + return Err(serde::de::Error::duplicate_field("membersRemoved")); + } + members_removed__ = Some(map_.next_value()?); + } + GeneratedField::InstallationsAdded => { + if installations_added__.is_some() { + return Err(serde::de::Error::duplicate_field("installationsAdded")); + } + installations_added__ = Some(map_.next_value()?); + } + GeneratedField::InstallationsRemoved => { + if installations_removed__.is_some() { + return Err(serde::de::Error::duplicate_field("installationsRemoved")); + } + installations_removed__ = Some(map_.next_value()?); + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(GroupMembershipChanges { + members_added: members_added__.unwrap_or_default(), + members_removed: members_removed__.unwrap_or_default(), + installations_added: installations_added__.unwrap_or_default(), + installations_removed: installations_removed__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.mls.message_contents.GroupMembershipChanges", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GroupMembershipEntry { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.version.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.GroupMembershipEntry", len)?; + if let Some(v) = self.version.as_ref() { + match v { + group_membership_entry::Version::V1(v) => { + struct_ser.serialize_field("v1", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GroupMembershipEntry { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "v1", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + V1, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "v1" => Ok(GeneratedField::V1), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GroupMembershipEntry; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.message_contents.GroupMembershipEntry") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut version__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::V1 => { + if version__.is_some() { + return Err(serde::de::Error::duplicate_field("v1")); + } + version__ = map_.next_value::<::std::option::Option<_>>()?.map(group_membership_entry::Version::V1) +; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(GroupMembershipEntry { + version: version__, + }) + } + } + deserializer.deserialize_struct("xmtp.mls.message_contents.GroupMembershipEntry", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for group_membership_entry::V1 { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.sequence_id != 0 { + len += 1; + } + if !self.failed_installations.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.GroupMembershipEntry.V1", len)?; + if self.sequence_id != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("sequence_id", ToString::to_string(&self.sequence_id).as_str())?; + } + if !self.failed_installations.is_empty() { + struct_ser.serialize_field("failed_installations", &self.failed_installations.iter().map(pbjson::private::base64::encode).collect::>())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for group_membership_entry::V1 { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "sequence_id", + "sequenceId", + "failed_installations", + "failedInstallations", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + SequenceId, + FailedInstallations, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "sequenceId" | "sequence_id" => Ok(GeneratedField::SequenceId), + "failedInstallations" | "failed_installations" => Ok(GeneratedField::FailedInstallations), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = group_membership_entry::V1; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.message_contents.GroupMembershipEntry.V1") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut sequence_id__ = None; + let mut failed_installations__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::SequenceId => { + if sequence_id__.is_some() { + return Err(serde::de::Error::duplicate_field("sequenceId")); + } + sequence_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::FailedInstallations => { + if failed_installations__.is_some() { + return Err(serde::de::Error::duplicate_field("failedInstallations")); + } + failed_installations__ = + Some(map_.next_value::>>()? + .into_iter().map(|x| x.0).collect()) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(group_membership_entry::V1 { + sequence_id: sequence_id__.unwrap_or_default(), + failed_installations: failed_installations__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.mls.message_contents.GroupMembershipEntry.V1", FIELDS, GeneratedVisitor) } } impl serde::Serialize for GroupMetadataV1 { diff --git a/crates/xmtp_proto/src/gen/xmtp.xmtpv4.message_api.rs b/crates/xmtp_proto/src/gen/xmtp.xmtpv4.message_api.rs index 0740fb53c3..2d89afa938 100644 --- a/crates/xmtp_proto/src/gen/xmtp.xmtpv4.message_api.rs +++ b/crates/xmtp_proto/src/gen/xmtp.xmtpv4.message_api.rs @@ -190,6 +190,343 @@ impl ::prost::Name for SubscribeEnvelopesResponse { "/xmtp.xmtpv4.message_api.SubscribeEnvelopesResponse".into() } } +/// Client -> node. Sent one or more times over the life of the stream. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SubscribeRequest { + #[prost(oneof = "subscribe_request::Version", tags = "1")] + pub version: ::core::option::Option, +} +/// Nested message and enum types in `SubscribeRequest`. +pub mod subscribe_request { + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct V1 { + /// Each frame is exactly one of: a mutation, a Ping, or a Pong. + #[prost(oneof = "v1::Request", tags = "1, 2, 3")] + pub request: ::core::option::Option, + } + /// Nested message and enum types in `V1`. + pub mod v1 { + /// Add and/or remove subscriptions in place (applied atomically per frame). + /// Topics use the kind-prefixed binary representation (XIP-49 §3.3.2): the + /// first byte is the topic kind, the remainder is the identifier. A topic + /// whose kind the node does not serve fails the stream with INVALID_ARGUMENT. + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Mutate { + /// begin delivering these topics + #[prost(message, repeated, tag = "1")] + pub adds: ::prost::alloc::vec::Vec, + /// topics to stop delivering + #[prost(bytes = "vec", repeated, tag = "2")] + pub removes: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + /// Catch this Mutate's adds up to the live edge — history, TopicsLive + /// markers, and the wave's CatchupComplete — but do NOT register them for + /// live delivery. The markers then mean "you have everything as of now". + /// Combined with half-closing the request stream, this is the bounded + /// catch-up ("sync") mode: the node finishes the wave then closes the + /// stream itself. Removals in the Mutate are unaffected. + #[prost(bool, tag = "3")] + pub history_only: bool, + /// Client-chosen correlation id, echoed on this wave's CatchupComplete so + /// completions are attributable when waves overlap. SHOULD be unique per + /// stream; 0 = no correlation requested (still echoed as 0). + #[prost(uint64, tag = "4")] + pub mutate_id: u64, + } + /// Nested message and enum types in `Mutate`. + pub mod mutate { + /// A topic to subscribe, with the vector cursor to resume from. + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Subscription { + #[prost(bytes = "vec", tag = "1")] + pub topic: ::prost::alloc::vec::Vec, + /// Resume point: deliver envelopes beyond this per-originator vector + /// cursor. Omitted/empty = from the beginning. Originators absent from + /// the cursor map are treated as sequence 0 (the node fills them in), + /// mirroring SubscribeTopics.TopicFilter.last_seen. + #[prost(message, optional, tag = "2")] + pub last_seen: ::core::option::Option< + super::super::super::super::envelopes::Cursor, + >, + } + impl ::prost::Name for Subscription { + const NAME: &'static str = "Subscription"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.SubscribeRequest.V1.Mutate.Subscription" + .into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.SubscribeRequest.V1.Mutate.Subscription" + .into() + } + } + } + impl ::prost::Name for Mutate { + const NAME: &'static str = "Mutate"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.SubscribeRequest.V1.Mutate".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.SubscribeRequest.V1.Mutate".into() + } + } + /// Each frame is exactly one of: a mutation, a Ping, or a Pong. + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Request { + #[prost(message, tag = "1")] + Mutate(Mutate), + /// liveness challenge (e.g. probe the link after resuming) + #[prost(message, tag = "2")] + Ping(super::super::Ping), + /// answer to a node Ping + #[prost(message, tag = "3")] + Pong(super::super::Pong), + } + } + impl ::prost::Name for V1 { + const NAME: &'static str = "V1"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.SubscribeRequest.V1".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.SubscribeRequest.V1".into() + } + } + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Version { + #[prost(message, tag = "1")] + V1(V1), + } +} +impl ::prost::Name for SubscribeRequest { + const NAME: &'static str = "SubscribeRequest"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.SubscribeRequest".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.SubscribeRequest".into() + } +} +/// Node -> client. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SubscribeResponse { + #[prost(oneof = "subscribe_response::Version", tags = "1")] + pub version: ::core::option::Option, +} +/// Nested message and enum types in `SubscribeResponse`. +pub mod subscribe_response { + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct V1 { + #[prost(oneof = "v1::Response", tags = "1, 2, 3, 4, 5, 6")] + pub response: ::core::option::Option, + } + /// Nested message and enum types in `V1`. + pub mod v1 { + /// A batch of envelopes across the active subscriptions; the client demuxes + /// by each envelope's target topic. + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Envelopes { + #[prost(message, repeated, tag = "1")] + pub envelopes: ::prost::alloc::vec::Vec< + super::super::super::envelopes::OriginatorEnvelope, + >, + } + impl ::prost::Name for Envelopes { + const NAME: &'static str = "Envelopes"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.SubscribeResponse.V1.Envelopes".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.SubscribeResponse.V1.Envelopes".into() + } + } + /// The first frame on every stream. + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] + pub struct Started { + /// The node's ping cadence (ms): the basis for the client's staleness + /// threshold and the node's reap deadline. + #[prost(uint32, tag = "1")] + pub keepalive_interval_ms: u32, + /// Optional protocol features the node supports on this stream. The node + /// silently ignores request types it does not understand, so a client MUST + /// NOT send an optional request type whose capability the node did not + /// advertise (it would hang waiting on a response that never comes). + #[prost(enumeration = "Capability", repeated, tag = "2")] + pub capabilities: ::prost::alloc::vec::Vec, + } + impl ::prost::Name for Started { + const NAME: &'static str = "Started"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.SubscribeResponse.V1.Started".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.SubscribeResponse.V1.Started".into() + } + } + /// Sent once per Mutate that adds subscriptions (a catch-up "wave"), after + /// the wave's last TopicsLive: everything the Mutate asked for is delivered. + #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] + pub struct CatchupComplete { + /// echoes the Mutate that started this wave (0 if none given) + #[prost(uint64, tag = "1")] + pub mutate_id: u64, + } + impl ::prost::Name for CatchupComplete { + const NAME: &'static str = "CatchupComplete"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.SubscribeResponse.V1.CatchupComplete".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.SubscribeResponse.V1.CatchupComplete".into() + } + } + /// Emitted when topics finish catch-up, AFTER the last history frame for + /// them — including any live envelopes that queued behind the catch-up, + /// which were equally historical from the client's perspective — so every + /// later frame for a listed topic is live tail. Informational only: delivery + /// correctness (no duplicates, no gaps) never depends on it. Re-adding a + /// topic re-runs catch-up and re-emits it; receivers treat it idempotently. + #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] + pub struct TopicsLive { + /// kind-prefixed topics now tailing live + #[prost(bytes = "vec", repeated, tag = "1")] + pub topics: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, + } + impl ::prost::Name for TopicsLive { + const NAME: &'static str = "TopicsLive"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.SubscribeResponse.V1.TopicsLive".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.SubscribeResponse.V1.TopicsLive".into() + } + } + /// Optional per-stream protocol features (none defined yet; future revisions + /// add values, e.g. fetch-over-stream lookups answered with the same read + /// view that feeds the stream, or new streamable topic kinds). + #[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + ::prost::Enumeration + )] + #[repr(i32)] + pub enum Capability { + Unspecified = 0, + } + impl Capability { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "CAPABILITY_UNSPECIFIED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "CAPABILITY_UNSPECIFIED" => Some(Self::Unspecified), + _ => None, + } + } + } + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Response { + #[prost(message, tag = "1")] + Envelopes(Envelopes), + /// sent once, immediately on open, before any catch-up + #[prost(message, tag = "2")] + Started(Started), + /// idle liveness challenge; receiver MUST answer with Pong + #[prost(message, tag = "3")] + Ping(super::super::Ping), + /// answer to a client Ping + #[prost(message, tag = "4")] + Pong(super::super::Pong), + /// these topics just crossed from catch-up to live + #[prost(message, tag = "5")] + TopicsLive(TopicsLive), + /// a Mutate's adds are fully delivered + #[prost(message, tag = "6")] + CatchupComplete(CatchupComplete), + } + } + impl ::prost::Name for V1 { + const NAME: &'static str = "V1"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.SubscribeResponse.V1".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.SubscribeResponse.V1".into() + } + } + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Version { + #[prost(message, tag = "1")] + V1(V1), + } +} +impl ::prost::Name for SubscribeResponse { + const NAME: &'static str = "SubscribeResponse"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.SubscribeResponse".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.SubscribeResponse".into() + } +} +/// Liveness challenge/response for Subscribe, shared across versions. Either peer +/// MAY send a Ping; the receiver MUST reply with a Pong echoing the nonce. The +/// sender closes the stream if no Pong arrives within its deadline — how a node +/// reaps a vanished peer (e.g. a mobile client the OS suspended behind a proxy +/// that still ACKs the transport). +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Ping { + #[prost(uint64, tag = "1")] + pub nonce: u64, +} +impl ::prost::Name for Ping { + const NAME: &'static str = "Ping"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.Ping".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.Ping".into() + } +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct Pong { + /// echoes the nonce of the Ping it answers + #[prost(uint64, tag = "1")] + pub nonce: u64, +} +impl ::prost::Name for Pong { + const NAME: &'static str = "Pong"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.Pong".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.Pong".into() + } +} /// Batch subscribe to all envelopes #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct SubscribeAllEnvelopesRequest {} @@ -1029,7 +1366,7 @@ pub mod replication_api_server { } /// Generated server implementations. #[cfg(any(not(target_arch = "wasm32"), feature = "grpc_server_impls"))] -pub mod notification_api_server { +pub mod publish_api_server { #![allow( unused_variables, dead_code, @@ -1038,36 +1375,27 @@ pub mod notification_api_server { clippy::let_unit_value, )] use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with NotificationApiServer. + /// Generated trait containing gRPC methods that should be implemented for use with PublishApiServer. #[async_trait] - pub trait NotificationApi: std::marker::Send + std::marker::Sync + 'static { - /// Server streaming response type for the SubscribeAllEnvelopes method. - type SubscribeAllEnvelopesStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::SubscribeEnvelopesResponse, - tonic::Status, - >, - > - + std::marker::Send - + 'static; - async fn subscribe_all_envelopes( + pub trait PublishApi: std::marker::Send + std::marker::Sync + 'static { + async fn publish_payer_envelopes( &self, - request: tonic::Request, + request: tonic::Request, ) -> std::result::Result< - tonic::Response, + tonic::Response, tonic::Status, >; } - /// Full envelope stream for notification services. + /// Gateway -> Node. #[derive(Debug)] - pub struct NotificationApiServer { + pub struct PublishApiServer { inner: Arc, accept_compression_encodings: EnabledCompressionEncodings, send_compression_encodings: EnabledCompressionEncodings, max_decoding_message_size: Option, max_encoding_message_size: Option, } - impl NotificationApiServer { + impl PublishApiServer { pub fn new(inner: T) -> Self { Self::from_arc(Arc::new(inner)) } @@ -1118,9 +1446,9 @@ pub mod notification_api_server { self } } - impl tonic::codegen::Service> for NotificationApiServer + impl tonic::codegen::Service> for PublishApiServer where - T: NotificationApi, + T: PublishApi, B: Body + std::marker::Send + 'static, B::Error: Into + std::marker::Send + 'static, { @@ -1135,30 +1463,25 @@ pub mod notification_api_server { } fn call(&mut self, req: http::Request) -> Self::Future { match req.uri().path() { - "/xmtp.xmtpv4.message_api.NotificationApi/SubscribeAllEnvelopes" => { + "/xmtp.xmtpv4.message_api.PublishApi/PublishPayerEnvelopes" => { #[allow(non_camel_case_types)] - struct SubscribeAllEnvelopesSvc(pub Arc); + struct PublishPayerEnvelopesSvc(pub Arc); impl< - T: NotificationApi, - > tonic::server::ServerStreamingService< - super::SubscribeAllEnvelopesRequest, - > for SubscribeAllEnvelopesSvc { - type Response = super::SubscribeEnvelopesResponse; - type ResponseStream = T::SubscribeAllEnvelopesStream; + T: PublishApi, + > tonic::server::UnaryService + for PublishPayerEnvelopesSvc { + type Response = super::PublishPayerEnvelopesResponse; type Future = BoxFuture< - tonic::Response, + tonic::Response, tonic::Status, >; fn call( &mut self, - request: tonic::Request, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::subscribe_all_envelopes( - &inner, - request, - ) + ::publish_payer_envelopes(&inner, request) .await }; Box::pin(fut) @@ -1170,7 +1493,7 @@ pub mod notification_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let method = SubscribeAllEnvelopesSvc(inner); + let method = PublishPayerEnvelopesSvc(inner); let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( @@ -1181,7 +1504,7 @@ pub mod notification_api_server { max_decoding_message_size, max_encoding_message_size, ); - let res = grpc.server_streaming(method, req).await; + let res = grpc.unary(method, req).await; Ok(res) }; Box::pin(fut) @@ -1208,7 +1531,7 @@ pub mod notification_api_server { } } } - impl Clone for NotificationApiServer { + impl Clone for PublishApiServer { fn clone(&self) -> Self { let inner = self.inner.clone(); Self { @@ -1221,14 +1544,14 @@ pub mod notification_api_server { } } /// Generated gRPC service name - pub const SERVICE_NAME: &str = "xmtp.xmtpv4.message_api.NotificationApi"; - impl tonic::server::NamedService for NotificationApiServer { + pub const SERVICE_NAME: &str = "xmtp.xmtpv4.message_api.PublishApi"; + impl tonic::server::NamedService for PublishApiServer { const NAME: &'static str = SERVICE_NAME; } } /// Generated server implementations. #[cfg(any(not(target_arch = "wasm32"), feature = "grpc_server_impls"))] -pub mod publish_api_server { +pub mod notification_api_server { #![allow( unused_variables, dead_code, @@ -1237,27 +1560,36 @@ pub mod publish_api_server { clippy::let_unit_value, )] use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with PublishApiServer. + /// Generated trait containing gRPC methods that should be implemented for use with NotificationApiServer. #[async_trait] - pub trait PublishApi: std::marker::Send + std::marker::Sync + 'static { - async fn publish_payer_envelopes( + pub trait NotificationApi: std::marker::Send + std::marker::Sync + 'static { + /// Server streaming response type for the SubscribeAllEnvelopes method. + type SubscribeAllEnvelopesStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + super::SubscribeEnvelopesResponse, + tonic::Status, + >, + > + + std::marker::Send + + 'static; + async fn subscribe_all_envelopes( &self, - request: tonic::Request, + request: tonic::Request, ) -> std::result::Result< - tonic::Response, + tonic::Response, tonic::Status, >; } - /// Gateway -> Node. + /// Full envelope stream for notification services. #[derive(Debug)] - pub struct PublishApiServer { + pub struct NotificationApiServer { inner: Arc, accept_compression_encodings: EnabledCompressionEncodings, send_compression_encodings: EnabledCompressionEncodings, max_decoding_message_size: Option, max_encoding_message_size: Option, } - impl PublishApiServer { + impl NotificationApiServer { pub fn new(inner: T) -> Self { Self::from_arc(Arc::new(inner)) } @@ -1308,9 +1640,9 @@ pub mod publish_api_server { self } } - impl tonic::codegen::Service> for PublishApiServer + impl tonic::codegen::Service> for NotificationApiServer where - T: PublishApi, + T: NotificationApi, B: Body + std::marker::Send + 'static, B::Error: Into + std::marker::Send + 'static, { @@ -1325,25 +1657,30 @@ pub mod publish_api_server { } fn call(&mut self, req: http::Request) -> Self::Future { match req.uri().path() { - "/xmtp.xmtpv4.message_api.PublishApi/PublishPayerEnvelopes" => { + "/xmtp.xmtpv4.message_api.NotificationApi/SubscribeAllEnvelopes" => { #[allow(non_camel_case_types)] - struct PublishPayerEnvelopesSvc(pub Arc); + struct SubscribeAllEnvelopesSvc(pub Arc); impl< - T: PublishApi, - > tonic::server::UnaryService - for PublishPayerEnvelopesSvc { - type Response = super::PublishPayerEnvelopesResponse; + T: NotificationApi, + > tonic::server::ServerStreamingService< + super::SubscribeAllEnvelopesRequest, + > for SubscribeAllEnvelopesSvc { + type Response = super::SubscribeEnvelopesResponse; + type ResponseStream = T::SubscribeAllEnvelopesStream; type Future = BoxFuture< - tonic::Response, + tonic::Response, tonic::Status, >; fn call( &mut self, - request: tonic::Request, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::publish_payer_envelopes(&inner, request) + ::subscribe_all_envelopes( + &inner, + request, + ) .await }; Box::pin(fut) @@ -1355,7 +1692,7 @@ pub mod publish_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let method = PublishPayerEnvelopesSvc(inner); + let method = SubscribeAllEnvelopesSvc(inner); let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( @@ -1366,7 +1703,7 @@ pub mod publish_api_server { max_decoding_message_size, max_encoding_message_size, ); - let res = grpc.unary(method, req).await; + let res = grpc.server_streaming(method, req).await; Ok(res) }; Box::pin(fut) @@ -1393,7 +1730,7 @@ pub mod publish_api_server { } } } - impl Clone for PublishApiServer { + impl Clone for NotificationApiServer { fn clone(&self) -> Self { let inner = self.inner.clone(); Self { @@ -1406,220 +1743,14 @@ pub mod publish_api_server { } } /// Generated gRPC service name - pub const SERVICE_NAME: &str = "xmtp.xmtpv4.message_api.PublishApi"; - impl tonic::server::NamedService for PublishApiServer { + pub const SERVICE_NAME: &str = "xmtp.xmtpv4.message_api.NotificationApi"; + impl tonic::server::NamedService for NotificationApiServer { const NAME: &'static str = SERVICE_NAME; } } -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct LivenessFailure { - #[prost(uint32, tag = "1")] - pub response_time_ns: u32, - #[prost(oneof = "liveness_failure::Request", tags = "2, 3, 4")] - pub request: ::core::option::Option, -} -/// Nested message and enum types in `LivenessFailure`. -pub mod liveness_failure { - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Request { - #[prost(message, tag = "2")] - Subscribe(super::SubscribeEnvelopesRequest), - #[prost(message, tag = "3")] - Query(super::QueryEnvelopesRequest), - #[prost(message, tag = "4")] - Publish(super::PublishPayerEnvelopesRequest), - } -} -impl ::prost::Name for LivenessFailure { - const NAME: &'static str = "LivenessFailure"; - const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.xmtpv4.message_api.LivenessFailure".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.xmtpv4.message_api.LivenessFailure".into() - } -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct SafetyFailure { - #[prost(message, repeated, tag = "1")] - pub envelopes: ::prost::alloc::vec::Vec, -} -impl ::prost::Name for SafetyFailure { - const NAME: &'static str = "SafetyFailure"; - const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.xmtpv4.message_api.SafetyFailure".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.xmtpv4.message_api.SafetyFailure".into() - } -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct UnsignedMisbehaviorReport { - #[prost(uint64, tag = "1")] - pub reporter_time_ns: u64, - #[prost(uint32, tag = "2")] - pub misbehaving_node_id: u32, - #[prost(enumeration = "Misbehavior", tag = "3")] - pub r#type: i32, - /// Nodes must verify this field is false for client-submitted reports - #[prost(bool, tag = "6")] - pub submitted_by_node: bool, - #[prost(oneof = "unsigned_misbehavior_report::Failure", tags = "4, 5")] - pub failure: ::core::option::Option, -} -/// Nested message and enum types in `UnsignedMisbehaviorReport`. -pub mod unsigned_misbehavior_report { - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Failure { - #[prost(message, tag = "4")] - Liveness(super::LivenessFailure), - #[prost(message, tag = "5")] - Safety(super::SafetyFailure), - } -} -impl ::prost::Name for UnsignedMisbehaviorReport { - const NAME: &'static str = "UnsignedMisbehaviorReport"; - const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.xmtpv4.message_api.UnsignedMisbehaviorReport".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.xmtpv4.message_api.UnsignedMisbehaviorReport".into() - } -} -#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] -pub struct MisbehaviorReport { - /// Server time when the report was stored. Used only for querying reports. - /// This field is not signed. - #[prost(uint64, tag = "1")] - pub server_time_ns: u64, - #[prost(bytes = "vec", tag = "2")] - pub unsigned_misbehavior_report: ::prost::alloc::vec::Vec, - /// Signed by the node hosting the report - #[prost(message, optional, tag = "3")] - pub signature: ::core::option::Option< - super::super::identity::associations::RecoverableEcdsaSignature, - >, -} -impl ::prost::Name for MisbehaviorReport { - const NAME: &'static str = "MisbehaviorReport"; - const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.xmtpv4.message_api.MisbehaviorReport".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.xmtpv4.message_api.MisbehaviorReport".into() - } -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct SubmitMisbehaviorReportRequest { - #[prost(message, optional, tag = "1")] - pub report: ::core::option::Option, -} -impl ::prost::Name for SubmitMisbehaviorReportRequest { - const NAME: &'static str = "SubmitMisbehaviorReportRequest"; - const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.xmtpv4.message_api.SubmitMisbehaviorReportRequest".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.xmtpv4.message_api.SubmitMisbehaviorReportRequest".into() - } -} -#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] -pub struct SubmitMisbehaviorReportResponse {} -impl ::prost::Name for SubmitMisbehaviorReportResponse { - const NAME: &'static str = "SubmitMisbehaviorReportResponse"; - const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.xmtpv4.message_api.SubmitMisbehaviorReportResponse".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.xmtpv4.message_api.SubmitMisbehaviorReportResponse".into() - } -} -#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] -pub struct QueryMisbehaviorReportsRequest { - #[prost(uint64, tag = "1")] - pub after_ns: u64, -} -impl ::prost::Name for QueryMisbehaviorReportsRequest { - const NAME: &'static str = "QueryMisbehaviorReportsRequest"; - const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.xmtpv4.message_api.QueryMisbehaviorReportsRequest".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.xmtpv4.message_api.QueryMisbehaviorReportsRequest".into() - } -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct QueryMisbehaviorReportsResponse { - #[prost(message, repeated, tag = "1")] - pub reports: ::prost::alloc::vec::Vec, -} -impl ::prost::Name for QueryMisbehaviorReportsResponse { - const NAME: &'static str = "QueryMisbehaviorReportsResponse"; - const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; - fn full_name() -> ::prost::alloc::string::String { - "xmtp.xmtpv4.message_api.QueryMisbehaviorReportsResponse".into() - } - fn type_url() -> ::prost::alloc::string::String { - "/xmtp.xmtpv4.message_api.QueryMisbehaviorReportsResponse".into() - } -} -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum Misbehavior { - Unspecified = 0, - UnresponsiveNode = 1, - SlowNode = 2, - FailedRequest = 3, - OutOfOrder = 4, - DuplicateSequenceId = 5, - CausalOrdering = 6, - InvalidPayload = 7, - BlockchainInconsistency = 8, -} -impl Misbehavior { - /// String value of the enum field names used in the ProtoBuf definition. - /// - /// The values are not transformed in any way and thus are considered stable - /// (if the ProtoBuf definition does not change) and safe for programmatic use. - pub fn as_str_name(&self) -> &'static str { - match self { - Self::Unspecified => "MISBEHAVIOR_UNSPECIFIED", - Self::UnresponsiveNode => "MISBEHAVIOR_UNRESPONSIVE_NODE", - Self::SlowNode => "MISBEHAVIOR_SLOW_NODE", - Self::FailedRequest => "MISBEHAVIOR_FAILED_REQUEST", - Self::OutOfOrder => "MISBEHAVIOR_OUT_OF_ORDER", - Self::DuplicateSequenceId => "MISBEHAVIOR_DUPLICATE_SEQUENCE_ID", - Self::CausalOrdering => "MISBEHAVIOR_CAUSAL_ORDERING", - Self::InvalidPayload => "MISBEHAVIOR_INVALID_PAYLOAD", - Self::BlockchainInconsistency => "MISBEHAVIOR_BLOCKCHAIN_INCONSISTENCY", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "MISBEHAVIOR_UNSPECIFIED" => Some(Self::Unspecified), - "MISBEHAVIOR_UNRESPONSIVE_NODE" => Some(Self::UnresponsiveNode), - "MISBEHAVIOR_SLOW_NODE" => Some(Self::SlowNode), - "MISBEHAVIOR_FAILED_REQUEST" => Some(Self::FailedRequest), - "MISBEHAVIOR_OUT_OF_ORDER" => Some(Self::OutOfOrder), - "MISBEHAVIOR_DUPLICATE_SEQUENCE_ID" => Some(Self::DuplicateSequenceId), - "MISBEHAVIOR_CAUSAL_ORDERING" => Some(Self::CausalOrdering), - "MISBEHAVIOR_INVALID_PAYLOAD" => Some(Self::InvalidPayload), - "MISBEHAVIOR_BLOCKCHAIN_INCONSISTENCY" => Some(Self::BlockchainInconsistency), - _ => None, - } - } -} /// Generated server implementations. #[cfg(any(not(target_arch = "wasm32"), feature = "grpc_server_impls"))] -pub mod misbehavior_api_server { +pub mod query_api_server { #![allow( unused_variables, dead_code, @@ -1628,33 +1759,69 @@ pub mod misbehavior_api_server { clippy::let_unit_value, )] use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with MisbehaviorApiServer. + /// Generated trait containing gRPC methods that should be implemented for use with QueryApiServer. #[async_trait] - pub trait MisbehaviorApi: std::marker::Send + std::marker::Sync + 'static { - async fn submit_misbehavior_report( + pub trait QueryApi: std::marker::Send + std::marker::Sync + 'static { + async fn query_envelopes( &self, - request: tonic::Request, + request: tonic::Request, ) -> std::result::Result< - tonic::Response, + tonic::Response, tonic::Status, >; - async fn query_misbehavior_reports( + /// Server streaming response type for the SubscribeTopics method. + type SubscribeTopicsStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result, + > + + std::marker::Send + + 'static; + async fn subscribe_topics( &self, - request: tonic::Request, + request: tonic::Request, ) -> std::result::Result< - tonic::Response, + tonic::Response, + tonic::Status, + >; + /// Server streaming response type for the Subscribe method. + type SubscribeStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result, + > + + std::marker::Send + + 'static; + /// XIP-83 bidirectional mutable subscription: a single long-lived stream the + /// client mutates in place (add/remove topics) with ping/pong liveness, in + /// contrast to SubscribeTopics' fixed, immutable, server-streaming filter set. + /// Bidi streaming requires HTTP/2 (not grpc-web / connect-web); browser + /// clients stay on SubscribeTopics. + async fn subscribe( + &self, + request: tonic::Request>, + ) -> std::result::Result, tonic::Status>; + async fn get_inbox_ids( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + async fn get_newest_envelope( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, tonic::Status, >; } + /// Client -> Node. No auth token required. #[derive(Debug)] - pub struct MisbehaviorApiServer { + pub struct QueryApiServer { inner: Arc, accept_compression_encodings: EnabledCompressionEncodings, send_compression_encodings: EnabledCompressionEncodings, max_decoding_message_size: Option, max_encoding_message_size: Option, } - impl MisbehaviorApiServer { + impl QueryApiServer { pub fn new(inner: T) -> Self { Self::from_arc(Arc::new(inner)) } @@ -1705,9 +1872,9 @@ pub mod misbehavior_api_server { self } } - impl tonic::codegen::Service> for MisbehaviorApiServer + impl tonic::codegen::Service> for QueryApiServer where - T: MisbehaviorApi, + T: QueryApi, B: Body + std::marker::Send + 'static, B::Error: Into + std::marker::Send + 'static, { @@ -1722,31 +1889,25 @@ pub mod misbehavior_api_server { } fn call(&mut self, req: http::Request) -> Self::Future { match req.uri().path() { - "/xmtp.xmtpv4.message_api.MisbehaviorApi/SubmitMisbehaviorReport" => { + "/xmtp.xmtpv4.message_api.QueryApi/QueryEnvelopes" => { #[allow(non_camel_case_types)] - struct SubmitMisbehaviorReportSvc(pub Arc); + struct QueryEnvelopesSvc(pub Arc); impl< - T: MisbehaviorApi, - > tonic::server::UnaryService - for SubmitMisbehaviorReportSvc { - type Response = super::SubmitMisbehaviorReportResponse; + T: QueryApi, + > tonic::server::UnaryService + for QueryEnvelopesSvc { + type Response = super::QueryEnvelopesResponse; type Future = BoxFuture< tonic::Response, tonic::Status, >; fn call( &mut self, - request: tonic::Request< - super::SubmitMisbehaviorReportRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::submit_misbehavior_report( - &inner, - request, - ) - .await + ::query_envelopes(&inner, request).await }; Box::pin(fut) } @@ -1757,7 +1918,7 @@ pub mod misbehavior_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let method = SubmitMisbehaviorReportSvc(inner); + let method = QueryEnvelopesSvc(inner); let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( @@ -1773,31 +1934,27 @@ pub mod misbehavior_api_server { }; Box::pin(fut) } - "/xmtp.xmtpv4.message_api.MisbehaviorApi/QueryMisbehaviorReports" => { + "/xmtp.xmtpv4.message_api.QueryApi/SubscribeTopics" => { #[allow(non_camel_case_types)] - struct QueryMisbehaviorReportsSvc(pub Arc); + struct SubscribeTopicsSvc(pub Arc); impl< - T: MisbehaviorApi, - > tonic::server::UnaryService - for QueryMisbehaviorReportsSvc { - type Response = super::QueryMisbehaviorReportsResponse; + T: QueryApi, + > tonic::server::ServerStreamingService< + super::SubscribeTopicsRequest, + > for SubscribeTopicsSvc { + type Response = super::SubscribeTopicsResponse; + type ResponseStream = T::SubscribeTopicsStream; type Future = BoxFuture< - tonic::Response, + tonic::Response, tonic::Status, >; fn call( &mut self, - request: tonic::Request< - super::QueryMisbehaviorReportsRequest, - >, + request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::query_misbehavior_reports( - &inner, - request, - ) - .await + ::subscribe_topics(&inner, request).await }; Box::pin(fut) } @@ -1808,7 +1965,145 @@ pub mod misbehavior_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let method = QueryMisbehaviorReportsSvc(inner); + let method = SubscribeTopicsSvc(inner); + let codec = tonic_prost::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/xmtp.xmtpv4.message_api.QueryApi/Subscribe" => { + #[allow(non_camel_case_types)] + struct SubscribeSvc(pub Arc); + impl< + T: QueryApi, + > tonic::server::StreamingService + for SubscribeSvc { + type Response = super::SubscribeResponse; + type ResponseStream = T::SubscribeStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + tonic::Streaming, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::subscribe(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = SubscribeSvc(inner); + let codec = tonic_prost::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/xmtp.xmtpv4.message_api.QueryApi/GetInboxIds" => { + #[allow(non_camel_case_types)] + struct GetInboxIdsSvc(pub Arc); + impl< + T: QueryApi, + > tonic::server::UnaryService + for GetInboxIdsSvc { + type Response = super::GetInboxIdsResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_inbox_ids(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = GetInboxIdsSvc(inner); + let codec = tonic_prost::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/xmtp.xmtpv4.message_api.QueryApi/GetNewestEnvelope" => { + #[allow(non_camel_case_types)] + struct GetNewestEnvelopeSvc(pub Arc); + impl< + T: QueryApi, + > tonic::server::UnaryService + for GetNewestEnvelopeSvc { + type Response = super::GetNewestEnvelopeResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_newest_envelope(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = GetNewestEnvelopeSvc(inner); let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( @@ -1846,27 +2141,233 @@ pub mod misbehavior_api_server { } } } - impl Clone for MisbehaviorApiServer { - fn clone(&self) -> Self { - let inner = self.inner.clone(); - Self { - inner, - accept_compression_encodings: self.accept_compression_encodings, - send_compression_encodings: self.send_compression_encodings, - max_decoding_message_size: self.max_decoding_message_size, - max_encoding_message_size: self.max_encoding_message_size, - } + impl Clone for QueryApiServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + /// Generated gRPC service name + pub const SERVICE_NAME: &str = "xmtp.xmtpv4.message_api.QueryApi"; + impl tonic::server::NamedService for QueryApiServer { + const NAME: &'static str = SERVICE_NAME; + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LivenessFailure { + #[prost(uint32, tag = "1")] + pub response_time_ns: u32, + #[prost(oneof = "liveness_failure::Request", tags = "2, 3, 4")] + pub request: ::core::option::Option, +} +/// Nested message and enum types in `LivenessFailure`. +pub mod liveness_failure { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Request { + #[prost(message, tag = "2")] + Subscribe(super::SubscribeEnvelopesRequest), + #[prost(message, tag = "3")] + Query(super::QueryEnvelopesRequest), + #[prost(message, tag = "4")] + Publish(super::PublishPayerEnvelopesRequest), + } +} +impl ::prost::Name for LivenessFailure { + const NAME: &'static str = "LivenessFailure"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.LivenessFailure".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.LivenessFailure".into() + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SafetyFailure { + #[prost(message, repeated, tag = "1")] + pub envelopes: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for SafetyFailure { + const NAME: &'static str = "SafetyFailure"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.SafetyFailure".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.SafetyFailure".into() + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UnsignedMisbehaviorReport { + #[prost(uint64, tag = "1")] + pub reporter_time_ns: u64, + #[prost(uint32, tag = "2")] + pub misbehaving_node_id: u32, + #[prost(enumeration = "Misbehavior", tag = "3")] + pub r#type: i32, + /// Nodes must verify this field is false for client-submitted reports + #[prost(bool, tag = "6")] + pub submitted_by_node: bool, + #[prost(oneof = "unsigned_misbehavior_report::Failure", tags = "4, 5")] + pub failure: ::core::option::Option, +} +/// Nested message and enum types in `UnsignedMisbehaviorReport`. +pub mod unsigned_misbehavior_report { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Failure { + #[prost(message, tag = "4")] + Liveness(super::LivenessFailure), + #[prost(message, tag = "5")] + Safety(super::SafetyFailure), + } +} +impl ::prost::Name for UnsignedMisbehaviorReport { + const NAME: &'static str = "UnsignedMisbehaviorReport"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.UnsignedMisbehaviorReport".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.UnsignedMisbehaviorReport".into() + } +} +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct MisbehaviorReport { + /// Server time when the report was stored. Used only for querying reports. + /// This field is not signed. + #[prost(uint64, tag = "1")] + pub server_time_ns: u64, + #[prost(bytes = "vec", tag = "2")] + pub unsigned_misbehavior_report: ::prost::alloc::vec::Vec, + /// Signed by the node hosting the report + #[prost(message, optional, tag = "3")] + pub signature: ::core::option::Option< + super::super::identity::associations::RecoverableEcdsaSignature, + >, +} +impl ::prost::Name for MisbehaviorReport { + const NAME: &'static str = "MisbehaviorReport"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.MisbehaviorReport".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.MisbehaviorReport".into() + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SubmitMisbehaviorReportRequest { + #[prost(message, optional, tag = "1")] + pub report: ::core::option::Option, +} +impl ::prost::Name for SubmitMisbehaviorReportRequest { + const NAME: &'static str = "SubmitMisbehaviorReportRequest"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.SubmitMisbehaviorReportRequest".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.SubmitMisbehaviorReportRequest".into() + } +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct SubmitMisbehaviorReportResponse {} +impl ::prost::Name for SubmitMisbehaviorReportResponse { + const NAME: &'static str = "SubmitMisbehaviorReportResponse"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.SubmitMisbehaviorReportResponse".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.SubmitMisbehaviorReportResponse".into() + } +} +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct QueryMisbehaviorReportsRequest { + #[prost(uint64, tag = "1")] + pub after_ns: u64, +} +impl ::prost::Name for QueryMisbehaviorReportsRequest { + const NAME: &'static str = "QueryMisbehaviorReportsRequest"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.QueryMisbehaviorReportsRequest".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.QueryMisbehaviorReportsRequest".into() + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryMisbehaviorReportsResponse { + #[prost(message, repeated, tag = "1")] + pub reports: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for QueryMisbehaviorReportsResponse { + const NAME: &'static str = "QueryMisbehaviorReportsResponse"; + const PACKAGE: &'static str = "xmtp.xmtpv4.message_api"; + fn full_name() -> ::prost::alloc::string::String { + "xmtp.xmtpv4.message_api.QueryMisbehaviorReportsResponse".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/xmtp.xmtpv4.message_api.QueryMisbehaviorReportsResponse".into() + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum Misbehavior { + Unspecified = 0, + UnresponsiveNode = 1, + SlowNode = 2, + FailedRequest = 3, + OutOfOrder = 4, + DuplicateSequenceId = 5, + CausalOrdering = 6, + InvalidPayload = 7, + BlockchainInconsistency = 8, +} +impl Misbehavior { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "MISBEHAVIOR_UNSPECIFIED", + Self::UnresponsiveNode => "MISBEHAVIOR_UNRESPONSIVE_NODE", + Self::SlowNode => "MISBEHAVIOR_SLOW_NODE", + Self::FailedRequest => "MISBEHAVIOR_FAILED_REQUEST", + Self::OutOfOrder => "MISBEHAVIOR_OUT_OF_ORDER", + Self::DuplicateSequenceId => "MISBEHAVIOR_DUPLICATE_SEQUENCE_ID", + Self::CausalOrdering => "MISBEHAVIOR_CAUSAL_ORDERING", + Self::InvalidPayload => "MISBEHAVIOR_INVALID_PAYLOAD", + Self::BlockchainInconsistency => "MISBEHAVIOR_BLOCKCHAIN_INCONSISTENCY", } } - /// Generated gRPC service name - pub const SERVICE_NAME: &str = "xmtp.xmtpv4.message_api.MisbehaviorApi"; - impl tonic::server::NamedService for MisbehaviorApiServer { - const NAME: &'static str = SERVICE_NAME; + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "MISBEHAVIOR_UNSPECIFIED" => Some(Self::Unspecified), + "MISBEHAVIOR_UNRESPONSIVE_NODE" => Some(Self::UnresponsiveNode), + "MISBEHAVIOR_SLOW_NODE" => Some(Self::SlowNode), + "MISBEHAVIOR_FAILED_REQUEST" => Some(Self::FailedRequest), + "MISBEHAVIOR_OUT_OF_ORDER" => Some(Self::OutOfOrder), + "MISBEHAVIOR_DUPLICATE_SEQUENCE_ID" => Some(Self::DuplicateSequenceId), + "MISBEHAVIOR_CAUSAL_ORDERING" => Some(Self::CausalOrdering), + "MISBEHAVIOR_INVALID_PAYLOAD" => Some(Self::InvalidPayload), + "MISBEHAVIOR_BLOCKCHAIN_INCONSISTENCY" => Some(Self::BlockchainInconsistency), + _ => None, + } } } /// Generated server implementations. #[cfg(any(not(target_arch = "wasm32"), feature = "grpc_server_impls"))] -pub mod query_api_server { +pub mod misbehavior_api_server { #![allow( unused_variables, dead_code, @@ -1875,54 +2376,33 @@ pub mod query_api_server { clippy::let_unit_value, )] use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with QueryApiServer. + /// Generated trait containing gRPC methods that should be implemented for use with MisbehaviorApiServer. #[async_trait] - pub trait QueryApi: std::marker::Send + std::marker::Sync + 'static { - async fn query_envelopes( - &self, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - /// Server streaming response type for the SubscribeTopics method. - type SubscribeTopicsStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result, - > - + std::marker::Send - + 'static; - async fn subscribe_topics( - &self, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - async fn get_inbox_ids( + pub trait MisbehaviorApi: std::marker::Send + std::marker::Sync + 'static { + async fn submit_misbehavior_report( &self, - request: tonic::Request, + request: tonic::Request, ) -> std::result::Result< - tonic::Response, + tonic::Response, tonic::Status, >; - async fn get_newest_envelope( + async fn query_misbehavior_reports( &self, - request: tonic::Request, + request: tonic::Request, ) -> std::result::Result< - tonic::Response, + tonic::Response, tonic::Status, >; } - /// Client -> Node. No auth token required. #[derive(Debug)] - pub struct QueryApiServer { + pub struct MisbehaviorApiServer { inner: Arc, accept_compression_encodings: EnabledCompressionEncodings, send_compression_encodings: EnabledCompressionEncodings, max_decoding_message_size: Option, max_encoding_message_size: Option, } - impl QueryApiServer { + impl MisbehaviorApiServer { pub fn new(inner: T) -> Self { Self::from_arc(Arc::new(inner)) } @@ -1973,9 +2453,9 @@ pub mod query_api_server { self } } - impl tonic::codegen::Service> for QueryApiServer + impl tonic::codegen::Service> for MisbehaviorApiServer where - T: QueryApi, + T: MisbehaviorApi, B: Body + std::marker::Send + 'static, B::Error: Into + std::marker::Send + 'static, { @@ -1990,117 +2470,31 @@ pub mod query_api_server { } fn call(&mut self, req: http::Request) -> Self::Future { match req.uri().path() { - "/xmtp.xmtpv4.message_api.QueryApi/QueryEnvelopes" => { - #[allow(non_camel_case_types)] - struct QueryEnvelopesSvc(pub Arc); - impl< - T: QueryApi, - > tonic::server::UnaryService - for QueryEnvelopesSvc { - type Response = super::QueryEnvelopesResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::query_envelopes(&inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let method = QueryEnvelopesSvc(inner); - let codec = tonic_prost::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - "/xmtp.xmtpv4.message_api.QueryApi/SubscribeTopics" => { - #[allow(non_camel_case_types)] - struct SubscribeTopicsSvc(pub Arc); - impl< - T: QueryApi, - > tonic::server::ServerStreamingService< - super::SubscribeTopicsRequest, - > for SubscribeTopicsSvc { - type Response = super::SubscribeTopicsResponse; - type ResponseStream = T::SubscribeTopicsStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::subscribe_topics(&inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let method = SubscribeTopicsSvc(inner); - let codec = tonic_prost::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.server_streaming(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - "/xmtp.xmtpv4.message_api.QueryApi/GetInboxIds" => { + "/xmtp.xmtpv4.message_api.MisbehaviorApi/SubmitMisbehaviorReport" => { #[allow(non_camel_case_types)] - struct GetInboxIdsSvc(pub Arc); + struct SubmitMisbehaviorReportSvc(pub Arc); impl< - T: QueryApi, - > tonic::server::UnaryService - for GetInboxIdsSvc { - type Response = super::GetInboxIdsResponse; + T: MisbehaviorApi, + > tonic::server::UnaryService + for SubmitMisbehaviorReportSvc { + type Response = super::SubmitMisbehaviorReportResponse; type Future = BoxFuture< tonic::Response, tonic::Status, >; fn call( &mut self, - request: tonic::Request, + request: tonic::Request< + super::SubmitMisbehaviorReportRequest, + >, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_inbox_ids(&inner, request).await + ::submit_misbehavior_report( + &inner, + request, + ) + .await }; Box::pin(fut) } @@ -2111,7 +2505,7 @@ pub mod query_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let method = GetInboxIdsSvc(inner); + let method = SubmitMisbehaviorReportSvc(inner); let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( @@ -2127,25 +2521,31 @@ pub mod query_api_server { }; Box::pin(fut) } - "/xmtp.xmtpv4.message_api.QueryApi/GetNewestEnvelope" => { + "/xmtp.xmtpv4.message_api.MisbehaviorApi/QueryMisbehaviorReports" => { #[allow(non_camel_case_types)] - struct GetNewestEnvelopeSvc(pub Arc); + struct QueryMisbehaviorReportsSvc(pub Arc); impl< - T: QueryApi, - > tonic::server::UnaryService - for GetNewestEnvelopeSvc { - type Response = super::GetNewestEnvelopeResponse; + T: MisbehaviorApi, + > tonic::server::UnaryService + for QueryMisbehaviorReportsSvc { + type Response = super::QueryMisbehaviorReportsResponse; type Future = BoxFuture< tonic::Response, tonic::Status, >; fn call( &mut self, - request: tonic::Request, + request: tonic::Request< + super::QueryMisbehaviorReportsRequest, + >, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_newest_envelope(&inner, request).await + ::query_misbehavior_reports( + &inner, + request, + ) + .await }; Box::pin(fut) } @@ -2156,7 +2556,7 @@ pub mod query_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let method = GetNewestEnvelopeSvc(inner); + let method = QueryMisbehaviorReportsSvc(inner); let codec = tonic_prost::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( @@ -2194,7 +2594,7 @@ pub mod query_api_server { } } } - impl Clone for QueryApiServer { + impl Clone for MisbehaviorApiServer { fn clone(&self) -> Self { let inner = self.inner.clone(); Self { @@ -2207,8 +2607,8 @@ pub mod query_api_server { } } /// Generated gRPC service name - pub const SERVICE_NAME: &str = "xmtp.xmtpv4.message_api.QueryApi"; - impl tonic::server::NamedService for QueryApiServer { + pub const SERVICE_NAME: &str = "xmtp.xmtpv4.message_api.MisbehaviorApi"; + impl tonic::server::NamedService for MisbehaviorApiServer { const NAME: &'static str = SERVICE_NAME; } } diff --git a/crates/xmtp_proto/src/gen/xmtp.xmtpv4.message_api.serde.rs b/crates/xmtp_proto/src/gen/xmtp.xmtpv4.message_api.serde.rs index 04d4dfd4f8..5cf0e40d23 100644 --- a/crates/xmtp_proto/src/gen/xmtp.xmtpv4.message_api.serde.rs +++ b/crates/xmtp_proto/src/gen/xmtp.xmtpv4.message_api.serde.rs @@ -1239,6 +1239,204 @@ impl<'de> serde::Deserialize<'de> for MisbehaviorReport { deserializer.deserialize_struct("xmtp.xmtpv4.message_api.MisbehaviorReport", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for Ping { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.nonce != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.message_api.Ping", len)?; + if self.nonce != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("nonce", ToString::to_string(&self.nonce).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for Ping { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "nonce", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Nonce, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "nonce" => Ok(GeneratedField::Nonce), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = Ping; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.message_api.Ping") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut nonce__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Nonce => { + if nonce__.is_some() { + return Err(serde::de::Error::duplicate_field("nonce")); + } + nonce__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(Ping { + nonce: nonce__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.message_api.Ping", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for Pong { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.nonce != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.message_api.Pong", len)?; + if self.nonce != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("nonce", ToString::to_string(&self.nonce).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for Pong { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "nonce", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Nonce, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "nonce" => Ok(GeneratedField::Nonce), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = Pong; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.message_api.Pong") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut nonce__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Nonce => { + if nonce__.is_some() { + return Err(serde::de::Error::duplicate_field("nonce")); + } + nonce__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(Pong { + nonce: nonce__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.message_api.Pong", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for PublishPayerEnvelopesRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -2766,6 +2964,1251 @@ impl<'de> serde::Deserialize<'de> for subscribe_originators_response::Envelopes deserializer.deserialize_struct("xmtp.xmtpv4.message_api.SubscribeOriginatorsResponse.Envelopes", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for SubscribeRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.version.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.message_api.SubscribeRequest", len)?; + if let Some(v) = self.version.as_ref() { + match v { + subscribe_request::Version::V1(v) => { + struct_ser.serialize_field("v1", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for SubscribeRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "v1", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + V1, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "v1" => Ok(GeneratedField::V1), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = SubscribeRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.message_api.SubscribeRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut version__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::V1 => { + if version__.is_some() { + return Err(serde::de::Error::duplicate_field("v1")); + } + version__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_request::Version::V1) +; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(SubscribeRequest { + version: version__, + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.message_api.SubscribeRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_request::V1 { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.request.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.message_api.SubscribeRequest.V1", len)?; + if let Some(v) = self.request.as_ref() { + match v { + subscribe_request::v1::Request::Mutate(v) => { + struct_ser.serialize_field("mutate", v)?; + } + subscribe_request::v1::Request::Ping(v) => { + struct_ser.serialize_field("ping", v)?; + } + subscribe_request::v1::Request::Pong(v) => { + struct_ser.serialize_field("pong", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_request::V1 { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "mutate", + "ping", + "pong", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Mutate, + Ping, + Pong, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "mutate" => Ok(GeneratedField::Mutate), + "ping" => Ok(GeneratedField::Ping), + "pong" => Ok(GeneratedField::Pong), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_request::V1; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.message_api.SubscribeRequest.V1") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut request__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Mutate => { + if request__.is_some() { + return Err(serde::de::Error::duplicate_field("mutate")); + } + request__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_request::v1::Request::Mutate) +; + } + GeneratedField::Ping => { + if request__.is_some() { + return Err(serde::de::Error::duplicate_field("ping")); + } + request__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_request::v1::Request::Ping) +; + } + GeneratedField::Pong => { + if request__.is_some() { + return Err(serde::de::Error::duplicate_field("pong")); + } + request__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_request::v1::Request::Pong) +; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_request::V1 { + request: request__, + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.message_api.SubscribeRequest.V1", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_request::v1::Mutate { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.adds.is_empty() { + len += 1; + } + if !self.removes.is_empty() { + len += 1; + } + if self.history_only { + len += 1; + } + if self.mutate_id != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.message_api.SubscribeRequest.V1.Mutate", len)?; + if !self.adds.is_empty() { + struct_ser.serialize_field("adds", &self.adds)?; + } + if !self.removes.is_empty() { + struct_ser.serialize_field("removes", &self.removes.iter().map(pbjson::private::base64::encode).collect::>())?; + } + if self.history_only { + struct_ser.serialize_field("history_only", &self.history_only)?; + } + if self.mutate_id != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("mutate_id", ToString::to_string(&self.mutate_id).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_request::v1::Mutate { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "adds", + "removes", + "history_only", + "historyOnly", + "mutate_id", + "mutateId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Adds, + Removes, + HistoryOnly, + MutateId, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "adds" => Ok(GeneratedField::Adds), + "removes" => Ok(GeneratedField::Removes), + "historyOnly" | "history_only" => Ok(GeneratedField::HistoryOnly), + "mutateId" | "mutate_id" => Ok(GeneratedField::MutateId), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_request::v1::Mutate; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.message_api.SubscribeRequest.V1.Mutate") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut adds__ = None; + let mut removes__ = None; + let mut history_only__ = None; + let mut mutate_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Adds => { + if adds__.is_some() { + return Err(serde::de::Error::duplicate_field("adds")); + } + adds__ = Some(map_.next_value()?); + } + GeneratedField::Removes => { + if removes__.is_some() { + return Err(serde::de::Error::duplicate_field("removes")); + } + removes__ = + Some(map_.next_value::>>()? + .into_iter().map(|x| x.0).collect()) + ; + } + GeneratedField::HistoryOnly => { + if history_only__.is_some() { + return Err(serde::de::Error::duplicate_field("historyOnly")); + } + history_only__ = Some(map_.next_value()?); + } + GeneratedField::MutateId => { + if mutate_id__.is_some() { + return Err(serde::de::Error::duplicate_field("mutateId")); + } + mutate_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_request::v1::Mutate { + adds: adds__.unwrap_or_default(), + removes: removes__.unwrap_or_default(), + history_only: history_only__.unwrap_or_default(), + mutate_id: mutate_id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.message_api.SubscribeRequest.V1.Mutate", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_request::v1::mutate::Subscription { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.topic.is_empty() { + len += 1; + } + if self.last_seen.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.message_api.SubscribeRequest.V1.Mutate.Subscription", len)?; + if !self.topic.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("topic", pbjson::private::base64::encode(&self.topic).as_str())?; + } + if let Some(v) = self.last_seen.as_ref() { + struct_ser.serialize_field("last_seen", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_request::v1::mutate::Subscription { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "topic", + "last_seen", + "lastSeen", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Topic, + LastSeen, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "topic" => Ok(GeneratedField::Topic), + "lastSeen" | "last_seen" => Ok(GeneratedField::LastSeen), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_request::v1::mutate::Subscription; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.message_api.SubscribeRequest.V1.Mutate.Subscription") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut topic__ = None; + let mut last_seen__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Topic => { + if topic__.is_some() { + return Err(serde::de::Error::duplicate_field("topic")); + } + topic__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::LastSeen => { + if last_seen__.is_some() { + return Err(serde::de::Error::duplicate_field("lastSeen")); + } + last_seen__ = map_.next_value()?; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_request::v1::mutate::Subscription { + topic: topic__.unwrap_or_default(), + last_seen: last_seen__, + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.message_api.SubscribeRequest.V1.Mutate.Subscription", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for SubscribeResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.version.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.message_api.SubscribeResponse", len)?; + if let Some(v) = self.version.as_ref() { + match v { + subscribe_response::Version::V1(v) => { + struct_ser.serialize_field("v1", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for SubscribeResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "v1", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + V1, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "v1" => Ok(GeneratedField::V1), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = SubscribeResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.message_api.SubscribeResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut version__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::V1 => { + if version__.is_some() { + return Err(serde::de::Error::duplicate_field("v1")); + } + version__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_response::Version::V1) +; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(SubscribeResponse { + version: version__, + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.message_api.SubscribeResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_response::V1 { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.response.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.message_api.SubscribeResponse.V1", len)?; + if let Some(v) = self.response.as_ref() { + match v { + subscribe_response::v1::Response::Envelopes(v) => { + struct_ser.serialize_field("envelopes", v)?; + } + subscribe_response::v1::Response::Started(v) => { + struct_ser.serialize_field("started", v)?; + } + subscribe_response::v1::Response::Ping(v) => { + struct_ser.serialize_field("ping", v)?; + } + subscribe_response::v1::Response::Pong(v) => { + struct_ser.serialize_field("pong", v)?; + } + subscribe_response::v1::Response::TopicsLive(v) => { + struct_ser.serialize_field("topics_live", v)?; + } + subscribe_response::v1::Response::CatchupComplete(v) => { + struct_ser.serialize_field("catchup_complete", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_response::V1 { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "envelopes", + "started", + "ping", + "pong", + "topics_live", + "topicsLive", + "catchup_complete", + "catchupComplete", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Envelopes, + Started, + Ping, + Pong, + TopicsLive, + CatchupComplete, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "envelopes" => Ok(GeneratedField::Envelopes), + "started" => Ok(GeneratedField::Started), + "ping" => Ok(GeneratedField::Ping), + "pong" => Ok(GeneratedField::Pong), + "topicsLive" | "topics_live" => Ok(GeneratedField::TopicsLive), + "catchupComplete" | "catchup_complete" => Ok(GeneratedField::CatchupComplete), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_response::V1; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.message_api.SubscribeResponse.V1") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut response__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Envelopes => { + if response__.is_some() { + return Err(serde::de::Error::duplicate_field("envelopes")); + } + response__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_response::v1::Response::Envelopes) +; + } + GeneratedField::Started => { + if response__.is_some() { + return Err(serde::de::Error::duplicate_field("started")); + } + response__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_response::v1::Response::Started) +; + } + GeneratedField::Ping => { + if response__.is_some() { + return Err(serde::de::Error::duplicate_field("ping")); + } + response__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_response::v1::Response::Ping) +; + } + GeneratedField::Pong => { + if response__.is_some() { + return Err(serde::de::Error::duplicate_field("pong")); + } + response__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_response::v1::Response::Pong) +; + } + GeneratedField::TopicsLive => { + if response__.is_some() { + return Err(serde::de::Error::duplicate_field("topicsLive")); + } + response__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_response::v1::Response::TopicsLive) +; + } + GeneratedField::CatchupComplete => { + if response__.is_some() { + return Err(serde::de::Error::duplicate_field("catchupComplete")); + } + response__ = map_.next_value::<::std::option::Option<_>>()?.map(subscribe_response::v1::Response::CatchupComplete) +; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_response::V1 { + response: response__, + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.message_api.SubscribeResponse.V1", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_response::v1::Capability { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + let variant = match self { + Self::Unspecified => "CAPABILITY_UNSPECIFIED", + }; + serializer.serialize_str(variant) + } +} +impl<'de> serde::Deserialize<'de> for subscribe_response::v1::Capability { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "CAPABILITY_UNSPECIFIED", + ]; + + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = subscribe_response::v1::Capability; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + fn visit_i64(self, v: i64) -> std::result::Result + where + E: serde::de::Error, + { + i32::try_from(v) + .ok() + .and_then(|x| x.try_into().ok()) + .ok_or_else(|| { + serde::de::Error::invalid_value(serde::de::Unexpected::Signed(v), &self) + }) + } + + fn visit_u64(self, v: u64) -> std::result::Result + where + E: serde::de::Error, + { + i32::try_from(v) + .ok() + .and_then(|x| x.try_into().ok()) + .ok_or_else(|| { + serde::de::Error::invalid_value(serde::de::Unexpected::Unsigned(v), &self) + }) + } + + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "CAPABILITY_UNSPECIFIED" => Ok(subscribe_response::v1::Capability::Unspecified), + _ => Err(serde::de::Error::unknown_variant(value, FIELDS)), + } + } + } + deserializer.deserialize_any(GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_response::v1::CatchupComplete { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.mutate_id != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.message_api.SubscribeResponse.V1.CatchupComplete", len)?; + if self.mutate_id != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("mutate_id", ToString::to_string(&self.mutate_id).as_str())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_response::v1::CatchupComplete { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "mutate_id", + "mutateId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + MutateId, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "mutateId" | "mutate_id" => Ok(GeneratedField::MutateId), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_response::v1::CatchupComplete; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.message_api.SubscribeResponse.V1.CatchupComplete") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut mutate_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::MutateId => { + if mutate_id__.is_some() { + return Err(serde::de::Error::duplicate_field("mutateId")); + } + mutate_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_response::v1::CatchupComplete { + mutate_id: mutate_id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.message_api.SubscribeResponse.V1.CatchupComplete", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_response::v1::Envelopes { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.envelopes.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.message_api.SubscribeResponse.V1.Envelopes", len)?; + if !self.envelopes.is_empty() { + struct_ser.serialize_field("envelopes", &self.envelopes)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_response::v1::Envelopes { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "envelopes", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Envelopes, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "envelopes" => Ok(GeneratedField::Envelopes), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_response::v1::Envelopes; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.message_api.SubscribeResponse.V1.Envelopes") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut envelopes__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Envelopes => { + if envelopes__.is_some() { + return Err(serde::de::Error::duplicate_field("envelopes")); + } + envelopes__ = Some(map_.next_value()?); + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_response::v1::Envelopes { + envelopes: envelopes__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.message_api.SubscribeResponse.V1.Envelopes", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_response::v1::Started { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.keepalive_interval_ms != 0 { + len += 1; + } + if !self.capabilities.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.message_api.SubscribeResponse.V1.Started", len)?; + if self.keepalive_interval_ms != 0 { + struct_ser.serialize_field("keepalive_interval_ms", &self.keepalive_interval_ms)?; + } + if !self.capabilities.is_empty() { + let v = self.capabilities.iter().cloned().map(|v| { + subscribe_response::v1::Capability::try_from(v) + .map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", v))) + }).collect::, _>>()?; + struct_ser.serialize_field("capabilities", &v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_response::v1::Started { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "keepalive_interval_ms", + "keepaliveIntervalMs", + "capabilities", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + KeepaliveIntervalMs, + Capabilities, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "keepaliveIntervalMs" | "keepalive_interval_ms" => Ok(GeneratedField::KeepaliveIntervalMs), + "capabilities" => Ok(GeneratedField::Capabilities), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_response::v1::Started; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.message_api.SubscribeResponse.V1.Started") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut keepalive_interval_ms__ = None; + let mut capabilities__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::KeepaliveIntervalMs => { + if keepalive_interval_ms__.is_some() { + return Err(serde::de::Error::duplicate_field("keepaliveIntervalMs")); + } + keepalive_interval_ms__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::Capabilities => { + if capabilities__.is_some() { + return Err(serde::de::Error::duplicate_field("capabilities")); + } + capabilities__ = Some(map_.next_value::>()?.into_iter().map(|x| x as i32).collect()); + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_response::v1::Started { + keepalive_interval_ms: keepalive_interval_ms__.unwrap_or_default(), + capabilities: capabilities__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.message_api.SubscribeResponse.V1.Started", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for subscribe_response::v1::TopicsLive { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.topics.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.message_api.SubscribeResponse.V1.TopicsLive", len)?; + if !self.topics.is_empty() { + struct_ser.serialize_field("topics", &self.topics.iter().map(pbjson::private::base64::encode).collect::>())?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for subscribe_response::v1::TopicsLive { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "topics", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Topics, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "topics" => Ok(GeneratedField::Topics), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = subscribe_response::v1::TopicsLive; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.message_api.SubscribeResponse.V1.TopicsLive") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut topics__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Topics => { + if topics__.is_some() { + return Err(serde::de::Error::duplicate_field("topics")); + } + topics__ = + Some(map_.next_value::>>()? + .into_iter().map(|x| x.0).collect()) + ; + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(subscribe_response::v1::TopicsLive { + topics: topics__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.message_api.SubscribeResponse.V1.TopicsLive", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for SubscribeTopicsRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result