From 5d414a97a5d38543341db63be74a677de768de39 Mon Sep 17 00:00:00 2001 From: xmtp-coder-agent <269038685+xmtp-coder-agent@users.noreply.github.com> Date: Mon, 22 Jun 2026 15:24:07 +0000 Subject: [PATCH] Nightly Protos --- crates/xmtp_proto/proto_version | 2 +- .../xmtp_proto/src/gen/proto_descriptor.bin | Bin 347561 -> 359377 bytes .../xmtp_proto/src/gen/xmtp.message_api.v1.rs | 100 +- .../src/gen/xmtp.message_contents.rs | 936 +++++----- crates/xmtp_proto/src/gen/xmtp.mls.api.v1.rs | 401 +++++ .../src/gen/xmtp.mls.api.v1.serde.rs | 1466 +++++++++++++++ .../xmtp_proto/src/gen/xmtp.mls.database.rs | 187 +- .../src/gen/xmtp.mls.database.serde.rs | 333 ++++ ...xmtp.mls.message_contents.content_types.rs | 182 +- .../src/gen/xmtp.mls.message_contents.rs | 1581 ++++++++++------- .../gen/xmtp.mls.message_contents.serde.rs | 1305 ++++++++++++-- .../src/gen/xmtp.xmtpv4.message_api.rs | 1350 +++++++++----- .../src/gen/xmtp.xmtpv4.message_api.serde.rs | 1443 +++++++++++++++ 13 files changed, 7426 insertions(+), 1860 deletions(-) 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 48ee48c56ac8e6eba6da52de32ab0a86285d9858..c7ecf8ba5d24c05b1ca09d3a6aa86d6d90871a48 100644 GIT binary patch delta 64804 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 Q2GcEDS48QY#0f@JPh5!Hn delta 54088 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, + /// 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