diff --git a/.clippy.toml b/.clippy.toml new file mode 100644 index 000000000..154626ef4 --- /dev/null +++ b/.clippy.toml @@ -0,0 +1 @@ +allow-unwrap-in-tests = true diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 428c38194..0f5d6126d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,3 @@ # Global rule: * @xmtp/Engineering -*.md @fabriguespe +*.md @xmtp/documentation diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 918af841b..000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,221 +0,0 @@ -name: Build -on: - push: - branches: - - main -jobs: - build: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - target: [ - # aarch64-unknown-linux-gnu, - # x86_64-linux-android, - i686-linux-android, - armv7-linux-androideabi, - aarch64-linux-android, - ] - steps: - - name: Checkout - uses: actions/checkout@v1 - - - name: "Cache" - uses: actions/cache@v3 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - ./target/${{ matrix.target }} - key: ${{ matrix.target }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} - - - name: Install rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - target: ${{ matrix.target }} - - # Install latest cross to mitigate unwind linking issue on android builds. - # See https://github.com/cross-rs/cross/issues/1222 - - name: Install rust cross - run: | - cargo install cross --git https://github.com/cross-rs/cross - - - name: Build target - uses: actions-rs/cargo@v1 - with: - use-cross: true - command: build - args: --release --target ${{ matrix.target }} --manifest-path bindings_ffi/Cargo.toml --target-dir bindings_ffi/target - - - name: Upload binary - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.target }} - path: bindings_ffi/target/${{ matrix.target }}/release/libxmtpv3.so - retention-days: 1 - - build-macos: - runs-on: warp-macos-13-arm64-6x - strategy: - fail-fast: false - matrix: - target: [aarch64-apple-darwin, aarch64-apple-ios, aarch64-apple-ios-sim] - steps: - - name: Checkout - uses: actions/checkout@v1 - - - name: "Cache" - uses: actions/cache@v3 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - ./target/${{ matrix.target }} - key: ${{ matrix.target }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} - - - name: Install rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - target: ${{ matrix.target }} - - # Install latest cross to mitigate unwind linking issue on android builds. - # See https://github.com/cross-rs/cross/issues/1222 - - name: Install rust cross - run: | - cargo install cross --git https://github.com/cross-rs/cross - - - name: Build target - uses: actions-rs/cargo@v1 - env: - CROSS_NO_WARNINGS: "0" - with: - use-cross: true - command: build - args: --release --target ${{ matrix.target }} --manifest-path bindings_ffi/Cargo.toml --target-dir bindings_ffi/target - - - name: Upload binary - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.target }} - path: bindings_ffi/target/${{ matrix.target }}/release/libxmtpv3.a - retention-days: 1 - - kotlin: - runs-on: warp-macos-13-arm64-6x - steps: - - name: Checkout - uses: actions/checkout@v1 - - name: Cache - uses: actions/cache@v3 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - ./target/release - key: ${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} - - name: Install rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - target: x86_64-apple-darwin - - name: Install kotlin - run: brew install ktlint - - name: Generate Bindings - working-directory: bindings_ffi - run: ./gen_kotlin.sh - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: kotlin - path: bindings_ffi/src/uniffi/xmtpv3/xmtpv3.kt - retention-days: 1 - - swift: - runs-on: warp-macos-13-arm64-6x - steps: - - name: Checkout - uses: actions/checkout@v1 - - name: Cache - uses: actions/cache@v3 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - ./target/release - key: ${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} - - name: Install rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - target: x86_64-apple-darwin - - name: Install swift - run: brew install swiftformat - - name: Generate bindings - working-directory: bindings_ffi - run: | - make swift - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: swift - path: bindings_ffi/build/swift/ - retention-days: 1 - - package-android: - needs: [build, kotlin] - runs-on: warp-ubuntu-latest-x64-4x - steps: - - name: Download artifacts - uses: actions/download-artifact@v3 - with: - path: . - - name: Build archive - run: | - mkdir -p jniLibs/x86 - mv i686-linux-android/libxmtpv3.so jniLibs/x86/ - mkdir -p jniLibs/armeabi-v7a - mv armv7-linux-androideabi/libxmtpv3.so jniLibs/armeabi-v7a/ - mkdir -p jniLibs/arm64-v8a - mv aarch64-linux-android/libxmtpv3.so jniLibs/arm64-v8a/ - mkdir -p java - mv kotlin/xmtpv3.kt java/ - zip -r libxmtp-android.zip jniLibs java - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: libxmtp-android.zip - path: libxmtp-android.zip - - package-swift: - needs: [build-macos, swift] - runs-on: warp-macos-13-arm64-6x - steps: - - name: Checkout - uses: actions/checkout@v1 - - name: Download artifacts - uses: actions/download-artifact@v3 - with: - path: bindings_ffi/build - - name: Build archive - working-directory: bindings_ffi - run: | - find . - mkdir -p Sources/LibXMTP - mv build/swift/xmtpv3.swift Sources/LibXMTP/ - make framework - zip -r libxmtp-swift.zip Sources LibXMTPSwiftFFI.xcframework - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: libxmtp-swift.zip - path: bindings_ffi/libxmtp-swift.zip diff --git a/.github/workflows/github-pages.yml b/.github/workflows/deploy-docs.yml similarity index 69% rename from .github/workflows/github-pages.yml rename to .github/workflows/deploy-docs.yml index 76aa4a24b..8ca11d2f6 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/deploy-docs.yml @@ -1,8 +1,10 @@ -name: GitHub Pages +name: Deploy Docs to GitHub Pages on: push: - branches: ["main"] + branches: + - main + # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -22,26 +24,28 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive - name: Setup Pages uses: actions/configure-pages@v2 - - name: Install rust - uses: actions-rs/toolchain@v1 + - name: Update rust toolchains + run: rustup update + + - name: Cache + uses: Swatinem/rust-cache@v2 with: - toolchain: stable - profile: minimal - override: true + workspaces: | + . - name: Invoke cargo doc run: ./dev/docs id: docgen - - name: Upload artifact - uses: actions/upload-pages-artifact@v2 + - name: Upload pages artifact + uses: actions/upload-pages-artifact@v3 # Deployment job deploy: @@ -53,4 +57,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v3 + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/validation-server-publish.yml b/.github/workflows/deploy-validation-server.yml similarity index 59% rename from .github/workflows/validation-server-publish.yml rename to .github/workflows/deploy-validation-server.yml index 1e9b5cbde..6a23759ae 100644 --- a/.github/workflows/validation-server-publish.yml +++ b/.github/workflows/deploy-validation-server.yml @@ -1,23 +1,25 @@ -name: Publish Validation Service Image +name: Deploy Validation Service Image on: push: branches: - main + workflow_dispatch: + jobs: push_to_registry: - name: Push Docker image to GitHub Packages + name: Push Docker Image to GitHub Packages runs-on: warp-ubuntu-latest-x64-16x permissions: contents: read packages: write steps: - - name: Check out the repo - uses: actions/checkout@v2 + - name: Checkout + uses: actions/checkout@v4 - - name: Log in to the Container registry - uses: docker/login-action@v1 + - name: Log in to the container registry + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -25,12 +27,12 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v5 with: images: ghcr.io/xmtp/mls-validation-service - name: Build and push Docker image - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 id: push with: context: . @@ -48,3 +50,13 @@ jobs: variable-name: validation_service_image variable-value: "ghcr.io/xmtp/mls-validation-service@${{ steps.push.outputs.digest }}" variable-value-required-prefix: "ghcr.io/xmtp/mls-validation-service@sha256:" + + - name: Deploy (production) + uses: xmtp-labs/terraform-deployer@v1 + with: + terraform-token: ${{ secrets.TERRAFORM_TOKEN }} + terraform-org: xmtp + terraform-workspace: production + variable-name: validation_service_image + variable-value: "ghcr.io/xmtp/mls-validation-service@${{ steps.push.outputs.digest }}" + variable-value-required-prefix: "ghcr.io/xmtp/mls-validation-service@sha256:" diff --git a/.github/workflows/lint-ffi-bindings.yaml b/.github/workflows/lint-ffi-bindings.yaml new file mode 100644 index 000000000..12c45958d --- /dev/null +++ b/.github/workflows/lint-ffi-bindings.yaml @@ -0,0 +1,31 @@ +name: Lint FFI Bindings +on: + push: + branches: + - main + pull_request: + paths: + - "bindings_ffi/**" + - ".github/workflows/lint-ffi-bindings.yaml" + - "rustfmt.toml" +env: + CARGO_TERM_COLOR: always +jobs: + lint: + name: Lint + runs-on: warp-ubuntu-latest-x64-16x + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Update rust toolchains + run: rustup update + - name: Cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + . + bindings_ffi + - name: Run clippy and fail on warnings + run: cargo clippy --manifest-path bindings_ffi/Cargo.toml --all-features --all-targets --no-deps -- -Dwarnings + - name: Run format check + run: cargo fmt --manifest-path bindings_ffi/Cargo.toml --check diff --git a/.github/workflows/lint-node-bindings.yaml b/.github/workflows/lint-node-bindings.yaml index 019153e8d..fb317f1db 100644 --- a/.github/workflows/lint-node-bindings.yaml +++ b/.github/workflows/lint-node-bindings.yaml @@ -5,14 +5,20 @@ on: paths: - "bindings_node/**" - ".github/workflows/lint-node-bindings.yaml" - + - "rustfmt.toml" +env: + CARGO_TERM_COLOR: always jobs: lint: - runs-on: warp-ubuntu-latest-x64-8x + name: Lint + runs-on: warp-ubuntu-latest-x64-16x steps: - name: Checkout uses: actions/checkout@v4 + - name: Update rust toolchains + run: rustup update + - name: Cache uses: Swatinem/rust-cache@v2 with: @@ -26,12 +32,6 @@ jobs: - name: Run format check run: cargo fmt --manifest-path bindings_node/Cargo.toml --check - prettier: - runs-on: warp-ubuntu-latest-x64-8x - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup node uses: actions/setup-node@v4 with: @@ -45,11 +45,11 @@ jobs: run: corepack enable - name: Install dependencies + working-directory: bindings_node run: | - cd bindings_node yarn - name: Format check + working-directory: bindings_node run: | - cd bindings_node yarn format:check diff --git a/.github/workflows/lint-workspace.yaml b/.github/workflows/lint-workspace.yaml new file mode 100644 index 000000000..cbdaaeaff --- /dev/null +++ b/.github/workflows/lint-workspace.yaml @@ -0,0 +1,40 @@ +name: Lint Workspace +on: + push: + branches: + - main + pull_request: + paths: + - ".github/workflows/lint.yaml" + - "mls_validation_service/**" + - "xmtp_api_grpc/**" + - "xmtp_api_http/**" + - "xmtp_cryptography/**" + - "xmtp_id/**" + - "xmtp_mls/**" + - "xmtp_proto/**" + - "xmtp_v2/**" + - "Cargo.toml" + - "Cargo.lock" + - "rust-toolchain" + - "rustfmt.toml" +env: + CARGO_TERM_COLOR: always +jobs: + lint: + name: Lint + runs-on: warp-ubuntu-latest-x64-16x + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Update rust toolchains + run: rustup update + - name: Cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + . + - name: Run clippy and fail on warnings + run: cargo clippy --all-features --all-targets --no-deps -- -Dwarnings + - name: Run format check + run: cargo fmt --check diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml deleted file mode 100644 index 76dad4031..000000000 --- a/.github/workflows/lint.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: Lint -on: - push: - branches: - - main - pull_request: -jobs: - lint: - runs-on: warp-ubuntu-latest-x64-4x - steps: - - uses: actions/checkout@v1 - - run: rustup update - - uses: Swatinem/rust-cache@v2 - with: - workspaces: | - . - bindings_ffi - - uses: actions-rs/clippy-check@v1 - name: Lint main workspace - with: - token: ${{ secrets.GITHUB_TOKEN }} - name: workspace - args: --all-features --all-targets --no-deps - - uses: actions-rs/clippy-check@v1 - name: Lint ffi - with: - token: ${{ secrets.GITHUB_TOKEN }} - name: ffi - args: --all-features --all-targets --no-deps --manifest-path bindings_ffi/Cargo.toml - - name: Format - run: cargo fmt --check - - name: Lint and fail on warnings - run: cargo clippy --no-deps --all-targets --all-features -- -Dwarnings - - name: Lint and fail on warnings - run: cargo clippy --all-features --all-targets --no-deps --manifest-path bindings_ffi/Cargo.toml -- -Dwarnings diff --git a/.github/workflows/nightly-protos.yml b/.github/workflows/nightly-protos.yml new file mode 100644 index 000000000..58b8de22c --- /dev/null +++ b/.github/workflows/nightly-protos.yml @@ -0,0 +1,35 @@ +name: Nightly Automation +on: + schedule: + - cron: '0 10 * * 1' + workflow_dispatch: +jobs: + nightly-protos: + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: bufbuild/buf-setup-action@v1.40.1 + - name: Update rust toolchains + run: rustup update + - name: Cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + . + - name: Generate Protos + run: dev/gen_protos.sh + - name: Create Pull Request + uses: peter-evans/create-pull-request@v7 + with: + commit-message: Nightly Protos + title: Update Protos + body: | + - Nightly Proto Update + + Auto-generated by [create-pull-request][1] + + [1]: https://github.com/peter-evans/create-pull-request + branch: nightly-proto \ No newline at end of file diff --git a/.github/workflows/noop.yml b/.github/workflows/noop.yml new file mode 100644 index 000000000..5d2b96b3e --- /dev/null +++ b/.github/workflows/noop.yml @@ -0,0 +1,45 @@ +name: No Op + +on: + pull_request: + # ignore code and some workflow changes + paths: + - "**" + - "!.github/workflows/lint-ffi-bindings.yml" + - "!.github/workflows/lint-node-bindings.yml" + - "!.github/workflows/lint-workspace.yml" + - "!.github/workflows/test-ffi-bindings.yml" + - "!.github/workflows/test-node-bindings.yml" + - "!.github/workflows/test-workspace.yml" + - "!bindings_ffi/**" + - "!bindings_node/**" + - "!dev/**" + - "!mls_validation_service/**" + - "!xmtp_api_grpc/**" + - "!xmtp_cryptography/**" + - "!xmtp_id/**" + - "!xmtp_mls/**" + - "!xmtp_proto/**" + - "!xmtp_v2/**" + - "!.node-version" + - "!Cargo.toml" + - "!Cargo.lock" + - "!rust-toolchain" + - "!rustfmt.toml" + +# Test and Lint steps are required for pull requests, but some file changes +# don't require them to run. These jobs define the required steps for these +# cases, but don't actually do anything. + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - run: echo "Nothing to test" + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - run: echo "Nothing to lint" diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index 52cea5899..3129591fb 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -1,8 +1,12 @@ name: Release CLI + on: push: branches: - main + workflow_dispatch: +env: + CARGO_TERM_COLOR: always jobs: build: strategy: @@ -15,37 +19,35 @@ jobs: - macos-aarch64 include: - build: linux-x86_64 - os: warp-ubuntu-latest-x64-16x + runner: warp-ubuntu-latest-x64-16x target: x86_64-unknown-linux-musl - build: macos-aarch64 - os: warp-macos-13-arm64-6x + runner: warp-macos-13-arm64-6x target: aarch64-apple-darwin # - build: windows-x86_64-gnu - # os: windows-latest + # runner: windows-latest # target: x86_64-pc-windows-gnu - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.runner }} steps: - name: Checkout - uses: actions/checkout@v1 + uses: actions/checkout@v4 + + - name: Update rust toolchains + run: | + rustup update + rustup target add ${{ matrix.target }} - - name: "Cache" - uses: actions/cache@v3 + - name: Cache + uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo/registry - ~/.cargo/git - ./examples/cli/target/${{ matrix.target }} - key: ${{ matrix.target }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} + workspaces: | + . - - run: sudo apt install musl-tools + - name: Configure x86_64-unknown-linux-musl toolchain if: ${{ matrix.target == 'x86_64-unknown-linux-musl' }} - - name: Install rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - target: ${{ matrix.target }} + run: | + sudo apt-get update + sudo apt-get install -y musl-tools - name: Install openssl windows if: ${{ matrix.build == 'windows-x86_64-gnu' }} @@ -54,29 +56,25 @@ jobs: args: install openssl - name: Build target - uses: actions-rs/cargo@v1 - if: - with: - use-cross: false - command: build - args: --release --target ${{ matrix.target }} --manifest-path examples/cli/Cargo.toml --target-dir examples/cli/target + run: | + cargo build --release --target ${{ matrix.target }} --manifest-path examples/cli/Cargo.toml --target-dir examples/cli/target - name: Upload binary - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.build }} path: examples/cli/target/${{ matrix.target }}/release/xmtp_cli* retention-days: 1 release: - needs: ["build"] + needs: build runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v1 + uses: actions/checkout@v4 - name: Download artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: path: ./artifacts diff --git a/.github/workflows/release-kotlin-bindings.yml b/.github/workflows/release-kotlin-bindings.yml new file mode 100644 index 000000000..ffd9c1781 --- /dev/null +++ b/.github/workflows/release-kotlin-bindings.yml @@ -0,0 +1,106 @@ +name: Release Kotlin Bindings + +on: + workflow_dispatch: + +jobs: + build-linux: + runs-on: warp-ubuntu-latest-x64-16x + strategy: + fail-fast: false + matrix: + target: + - x86_64-linux-android + - i686-linux-android + - armv7-linux-androideabi + - aarch64-linux-android + include: + - target: x86_64-linux-android + output_target: x86_64 + - target: i686-linux-android + output_target: x86 + - target: armv7-linux-androideabi + output_target: armeabi-v7a + - target: aarch64-linux-android + output_target: arm64-v8a + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Update rust toolchains, add target + run: | + rustup update + rustup target add ${{ matrix.target }} + + - name: Cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + . + bindings_ffi + + # Install latest cross to mitigate unwind linking issue on android builds. + # See https://github.com/cross-rs/cross/issues/1222 + - name: Install cross + run: | + cargo install cross --git https://github.com/cross-rs/cross + + - name: Build target + env: + CROSS_NO_WARNINGS: "0" + run: | + cross build --release --target ${{ matrix.target }} --manifest-path bindings_ffi/Cargo.toml --target-dir bindings_ffi/target + + - name: Prepare JNI libs + run: | + mkdir -p bindings_ffi/jniLibs/${{ matrix.output_target }}/ && \ + cp bindings_ffi/target/${{ matrix.target }}/release/libxmtpv3.so bindings_ffi/jniLibs/${{ matrix.output_target }}/libuniffi_xmtpv3.so + + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.output_target }} + path: bindings_ffi/jniLibs/${{ matrix.output_target }}/libuniffi_xmtpv3.so + retention-days: 1 + + package-kotlin: + needs: [build-linux] + runs-on: warp-ubuntu-latest-x64-16x + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: bindings_ffi/jniLibs + + - name: Build archive + working-directory: bindings_ffi + run: | + zip -r LibXMTPKotlinFFI.zip jniLibs + + - name: Get short SHA + id: slug + run: echo "::set-output name=sha7::$(echo ${GITHUB_SHA} | cut -c1-7)" + + - name: Create release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: kotlin-bindings-${{ steps.slug.outputs.sha7 }} + release_name: Kotlin-Bindings-${{ steps.slug.outputs.sha7 }} + draft: false + prerelease: true + + - name: Upload release asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./bindings_ffi/LibXMTPKotlinFFI.zip + asset_name: LibXMTPKotlinFFI.zip + asset_content_type: application/zip diff --git a/.github/workflows/release-node-bindings.yml b/.github/workflows/release-node-bindings.yml index 3f66ef6fe..2c38c822e 100644 --- a/.github/workflows/release-node-bindings.yml +++ b/.github/workflows/release-node-bindings.yml @@ -5,7 +5,7 @@ on: jobs: build-linux: - runs-on: warp-ubuntu-latest-x64-8x + runs-on: warp-ubuntu-latest-x64-16x strategy: fail-fast: false matrix: @@ -26,17 +26,23 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - - name: "Cache" - uses: actions/cache@v3 + + - name: Update rust toolchains, add target + run: | + rustup update + rustup target add ${{ matrix.target }} + + - name: Cache + uses: Swatinem/rust-cache@v2 with: - path: | - ~/.cargo/registry - ~/.cargo/git - ./target/${{ matrix.target }} - key: ${{ matrix.target }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} + workspaces: | + . + bindings_node + - name: Configure aarch64 toolchain if: startsWith(matrix.target, 'aarch64') run: | + sudo apt-get update sudo apt-get install -qq crossbuild-essential-arm64 crossbuild-essential-armhf cat >>~/.cargo/config <> $GITHUB_PATH shell: bash - - name: Install rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - target: ${{ matrix.target }} + - name: Setup node uses: actions/setup-node@v4 with: @@ -205,16 +220,20 @@ jobs: cache: "yarn" env: SKIP_YARN_COREPACK_CHECK: "1" + - name: Enable corepack run: corepack enable + - name: Install dependencies + working-directory: bindings_node run: | - cd bindings_node yarn + - name: Build target + working-directory: bindings_node run: | - cd bindings_node yarn build --target ${{ matrix.target }} + - name: Upload binding uses: actions/upload-artifact@v4 with: @@ -230,12 +249,14 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + - name: Download build artifacts uses: actions/download-artifact@v4 with: pattern: bindings_node_* merge-multiple: true path: bindings_node/dist + - name: Setup node uses: actions/setup-node@v4 with: @@ -244,8 +265,10 @@ jobs: cache: "yarn" env: SKIP_YARN_COREPACK_CHECK: "1" + - name: Enable corepack run: corepack enable + - name: Publish to NPM uses: JS-DevTools/npm-publish@v3 with: diff --git a/.github/workflows/release_swift_bindings.yml b/.github/workflows/release-swift-bindings.yml similarity index 64% rename from .github/workflows/release_swift_bindings.yml rename to .github/workflows/release-swift-bindings.yml index 4cd55d2af..4756b81d9 100644 --- a/.github/workflows/release_swift_bindings.yml +++ b/.github/workflows/release-swift-bindings.yml @@ -9,45 +9,42 @@ jobs: strategy: fail-fast: false matrix: - target: [aarch64-apple-ios, x86_64-apple-ios, aarch64-apple-ios-sim, x86_64-apple-darwin, aarch64-apple-darwin] + target: + - aarch64-apple-ios + - x86_64-apple-ios + - aarch64-apple-ios-sim + - x86_64-apple-darwin + - aarch64-apple-darwin steps: - name: Checkout - uses: actions/checkout@v1 + uses: actions/checkout@v4 - - name: "Cache" - uses: actions/cache@v3 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - ./target/${{ matrix.target }} - key: ${{ matrix.target }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} - - - name: Install rust - uses: actions-rs/toolchain@v1 + - name: Update rust toolchains, add target + run: | + rustup update + rustup target add ${{ matrix.target }} + + - name: Cache + uses: Swatinem/rust-cache@v2 with: - toolchain: stable - profile: minimal - override: true - target: ${{ matrix.target }} + workspaces: | + . + bindings_ffi # Install latest cross to mitigate unwind linking issue on android builds. # See https://github.com/cross-rs/cross/issues/1222 - - name: Install rust cross + - name: Install cross run: | cargo install cross --git https://github.com/cross-rs/cross - name: Build target - uses: actions-rs/cargo@v1 - env: + env: CROSS_NO_WARNINGS: "0" - with: - use-cross: true - command: build - args: --release --target ${{ matrix.target }} --manifest-path bindings_ffi/Cargo.toml --target-dir bindings_ffi/target + run: | + cross build --release --target ${{ matrix.target }} --manifest-path bindings_ffi/Cargo.toml --target-dir bindings_ffi/target - name: Upload binary - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.target }} path: bindings_ffi/target/${{ matrix.target }}/release/libxmtpv3.a @@ -57,30 +54,30 @@ jobs: runs-on: warp-macos-13-arm64-6x steps: - name: Checkout - uses: actions/checkout@v1 + uses: actions/checkout@v4 + + - name: Update rust toolchains, add target + run: | + rustup update + rustup target add x86_64-apple-darwin + - name: Cache - uses: actions/cache@v3 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - ./target/release - key: ${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} - - name: Install rust - uses: actions-rs/toolchain@v1 + uses: Swatinem/rust-cache@v2 with: - toolchain: stable - profile: minimal - override: true - target: x86_64-apple-darwin + workspaces: | + . + bindings_ffi + - name: Install swift run: brew install swiftformat + - name: Generate bindings working-directory: bindings_ffi run: | make swift + - name: Upload artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: swift path: bindings_ffi/build/swift/ @@ -91,10 +88,10 @@ jobs: runs-on: warp-macos-13-arm64-6x steps: - name: Checkout - uses: actions/checkout@v1 + uses: actions/checkout@v4 - name: Download artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: path: bindings_ffi/build @@ -117,7 +114,7 @@ jobs: id: slug run: echo "::set-output name=sha7::$(echo ${GITHUB_SHA} | cut -c1-7)" - - name: Create Release + - name: Create release id: create_release uses: actions/create-release@v1 env: @@ -129,7 +126,7 @@ jobs: draft: false prerelease: true - - name: Upload Release Asset + - name: Upload release asset uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index d23d0c802..000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: Release - -on: - push: - tags: - - "*" - -jobs: - android: - runs-on: warp-ubuntu-latest-x64-4x - permissions: - contents: write # for the release action - steps: - - uses: actions/checkout@v3 - - name: download package - env: - HEAD_SHA: ${{ github.sha }} - GITHUB_REPO: ${{ github.repository }} - GH_TOKEN: ${{ github.token }} - working-directory: bindings_ffi - run: ./get_android_artifact.sh - - name: create release - uses: ncipollo/release-action@v1 - with: - artifacts: "bindings_ffi/libxmtp-android.zip" - allowUpdates: "true" - - swift: - runs-on: warp-ubuntu-latest-x64-4x - permissions: - contents: write - env: - RELEASE_BRANCH: main - steps: - - uses: actions/checkout@v3 - - name: download artifacts - env: - HEAD_SHA: ${{ github.sha }} - GITHUB_REPO: ${{ github.repository }} - GH_TOKEN: ${{ github.token }} - working-directory: bindings_ffi - run: ./get_swift_artifact.sh - - name: generate access token - id: generate_token - uses: tibdex/github-app-token@v1.8.0 - with: - app_id: ${{ secrets.GH_APP_ID }} - private_key: ${{ secrets.GH_APP_PK }} - installation_id: ${{ secrets.GH_INSTALLATION_ID }} - - name: check out libxmtp-swift - uses: actions/checkout@v3 - with: - repository: xmtp/libxmtp-swift - path: libxmtp-swift - ref: ${{ env.RELEASE_BRANCH }} - token: ${{ steps.generate_token.outputs.token }} - - name: update libxmtp-swift - working-directory: libxmtp-swift - run: | - # set -x - # extract the tag fro GITHUB_REF which has the form refs/tags/... - RELEASE=$(basename $GITHUB_REF) - # Update the version field in the podspec. - # This could be single inlined sed expression in theory, - # but the quoting issues were a pain to debug, - # so instead the sed rule is in a file - # and we need to inject the RELEASE tag into it. - sed -i '' -e "s/VERSION/$RELEASE/" ../bindings_ffi/update_spec_version.sed - sed -i '' -f ../bindings_ffi/update_spec_version.sed LibXMTP.podspec - # unzip the new framework version over the repo - unzip -o ../bindings_ffi/libxmtp-swift.zip - # commit, tag and push - git config user.name libxmtp - git config user.email libxmtp@xmtp.com - git add . - git commit -m "libxmtp release $RELEASE" - git tag $RELEASE - git push --tags origin $RELEASE_BRANCH diff --git a/.github/workflows/test-ffi-bindings.yml b/.github/workflows/test-ffi-bindings.yml new file mode 100644 index 000000000..43b4cc61e --- /dev/null +++ b/.github/workflows/test-ffi-bindings.yml @@ -0,0 +1,53 @@ +name: Test FFI Bindings +on: + push: + branches: + - main + pull_request: + # only run tests when related changes are made + paths: + - ".github/workflows/test-ffi-bindings.yml" + - "bindings_ffi/**" + - "dev/**" + - "mls_validation_service/**" + - "xmtp_api_grpc/**" + - "xmtp_cryptography/**" + - "xmtp_id/**" + - "xmtp_mls/**" + - "xmtp_proto/**" + - "xmtp_v2/**" + - "Cargo.toml" + - "Cargo.lock" + - "rust-toolchain" +env: + CARGO_TERM_COLOR: always +jobs: + test: + name: Test + runs-on: warp-ubuntu-latest-x64-16x + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Update rust toolchains + run: rustup update + - uses: Swatinem/rust-cache@v2 + with: + workspaces: | + . + bindings_ffi + - name: Start Docker containers + run: dev/up + - name: Setup Kotlin + run: | + sudo apt update -q + sudo apt install -y openjdk-11-jdk + sudo snap install --classic kotlin + cd bindings_ffi + make install-jar + echo "$(make echo-jar | tail -n 1 | sed -e 's/\.*export //')" >> "$GITHUB_ENV" + - name: Install nextest + uses: taiki-e/install-action@nextest + - name: Run cargo nextest on FFI bindings + run: | + export CLASSPATH="${{ env.CLASSPATH }}" + cargo nextest run --manifest-path bindings_ffi/Cargo.toml --test-threads 2 diff --git a/.github/workflows/test-http-api.yml b/.github/workflows/test-http-api.yml new file mode 100644 index 000000000..22cf3e268 --- /dev/null +++ b/.github/workflows/test-http-api.yml @@ -0,0 +1,44 @@ +name: Test workspace with HTTP +on: + push: + branches: + - main + pull_request: + # only run tests when related changes are made + paths: + - ".github/workflows/test-workspace.yml" + - "dev/**" + - "mls_validation_service/**" + - "xmtp_api_http/**" + - "xmtp_cryptography/**" + - "xmtp_id/**" + - "xmtp_mls/**" + - "xmtp_proto/**" + - "xmtp_v2/**" + - "Cargo.toml" + - "Cargo.lock" + - "rust-toolchain" +env: + CARGO_TERM_COLOR: always +jobs: + test: + name: Test + runs-on: warp-ubuntu-latest-x64-16x + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Update rust toolchains + run: rustup update + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + - name: Cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + . + - name: Start Docker containers + run: dev/up + - name: Install nextest + uses: taiki-e/install-action@nextest + - name: Run cargo nextest on main workspace + run: cargo nextest run --workspace --exclude xmtp_api_grpc --features http-api --test-threads 2 diff --git a/.github/workflows/test-node-bindings.yml b/.github/workflows/test-node-bindings.yml index 79a013ecb..35e759176 100644 --- a/.github/workflows/test-node-bindings.yml +++ b/.github/workflows/test-node-bindings.yml @@ -25,11 +25,15 @@ on: jobs: test: + name: Test runs-on: warp-ubuntu-latest-x64-16x steps: - name: Checkout uses: actions/checkout@v4 + - name: Update rust toolchains + run: rustup update + - name: Cache uses: Swatinem/rust-cache@v2 with: @@ -37,9 +41,6 @@ jobs: . bindings_node - - name: Update rust toolchain from rust-toolchain file - run: rustup update - - name: Start Docker containers run: dev/up @@ -56,16 +57,16 @@ jobs: run: corepack enable - name: Install dependencies + working-directory: bindings_node run: | - cd bindings_node yarn - name: Build release + working-directory: bindings_node run: | - cd bindings_node yarn build:release - name: Run tests + working-directory: bindings_node run: | - cd bindings_node yarn test diff --git a/.github/workflows/test-workspace.yml b/.github/workflows/test-workspace.yml new file mode 100644 index 000000000..7c006d02f --- /dev/null +++ b/.github/workflows/test-workspace.yml @@ -0,0 +1,44 @@ +name: Test Workspace +on: + push: + branches: + - main + pull_request: + # only run tests when related changes are made + paths: + - ".github/workflows/test-workspace.yml" + - "dev/**" + - "mls_validation_service/**" + - "xmtp_api_grpc/**" + - "xmtp_cryptography/**" + - "xmtp_id/**" + - "xmtp_mls/**" + - "xmtp_proto/**" + - "xmtp_v2/**" + - "Cargo.toml" + - "Cargo.lock" + - "rust-toolchain" +env: + CARGO_TERM_COLOR: always +jobs: + test: + name: Test + runs-on: warp-ubuntu-latest-x64-16x + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Update rust toolchains + run: rustup update + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + - name: Cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + . + - name: Start Docker containers + run: dev/up + - name: Install nextest + uses: taiki-e/install-action@nextest + - name: Run cargo nextest on main workspace + run: cargo nextest run --test-threads 2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 96c3de23d..000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: Test -on: - push: - branches: - - main - pull_request: -jobs: - test: - name: Test - runs-on: warp-ubuntu-latest-x64-16x - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Update rust toolchain from rust-toolchain file - run: rustup update - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - - - uses: Swatinem/rust-cache@v2 - with: - workspaces: | - . - bindings_ffi - bindings_flutter - - - name: Start Docker containers - run: dev/up - - - name: Run cargo test on main workspace - uses: actions-rs/cargo@v1 - timeout-minutes: 20 - with: - command: test - args: -- --test-threads=2 - - - name: Setup Kotlin - run: | - sudo apt update -q - sudo apt install -y openjdk-11-jdk - sudo snap install --classic kotlin - cd bindings_ffi - make install-jar - echo "$(make echo-jar | tail -n 1 | sed -e 's/\.*export //')" >> "$GITHUB_ENV" - - - name: Run cargo test on FFI bindings - run: | - export CLASSPATH="${{ env.CLASSPATH }}" - cargo test --manifest-path bindings_ffi/Cargo.toml -- --test-threads=2 diff --git a/.gitignore b/.gitignore index 9c1d23a64..6dff7d3cd 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ wasm-pack.log # generated bindings_swift/Sources bindings_swift/xmtp-rust-swift.zip +bindings_ffi/jniLibs # Logs logs @@ -121,6 +122,9 @@ dist # Stores VSCode versions used for testing VSCode extensions .vscode-test +# JetBrains IDE Info +.idea/ + # yarn v2 .yarn/cache .yarn/unplugged diff --git a/.vscode/settings.json b/.vscode/settings.json index 4f17ce193..34f9ee388 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,25 +1,20 @@ { - "rust-analyzer": { - "cargo": { - "sysroot": "discover" - }, - "linkedProjects": [ - "bindings_ffi/Cargo.toml", - "bindings_node/Cargo.toml", - "examples/cli/Cargo.toml", - "xmtp_api_grpc_gateway/Cargo.toml" - ], - "procMacro": { - "enable": true, - "attributes.enable": true, - "ignored": { - "async-trait": ["async_trait"], - "napi-derive": ["napi"], - "async-recursion": ["async_recursion"], - "ctor": ["ctor"], - "tokio": ["test"] - } - } + "rust-analyzer.cargo.sysroot": "discover", + "rust-analyzer.linkedProjects": [ + "bindings_ffi/Cargo.toml", + "bindings_node/Cargo.toml", + "bindings_wasm/Cargo.toml", + "examples/cli/Cargo.toml" + ], + "rust-analyzer.procMacro.enable": true, + "rust-analyzer.procMacro.attributes.enable": true, + "rust-analyzer.procMacro.ignored": { + "async-trait": ["async_trait"], + "napi-derive": ["napi"], + "async-recursion": ["async_recursion"], + "ctor": ["ctor"], + "tokio": ["test"], + "async-stream": ["stream", "try_stream"] }, "[toml]": { "editor.defaultFormatter": "tamasfe.even-better-toml" diff --git a/Cargo.lock b/Cargo.lock index a2175ef89..d0e470fd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -103,9 +103,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", @@ -118,33 +118,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -158,9 +158,9 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" [[package]] name = "arrayvec" @@ -225,18 +225,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -264,7 +264,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -275,18 +275,17 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" -version = "0.6.20" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", "axum-core", - "bitflags 1.3.2", "bytes", "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.28", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", "itoa", "matchit", "memchr", @@ -295,7 +294,7 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper", + "sync_wrapper 1.0.1", "tower", "tower-layer", "tower-service", @@ -303,26 +302,29 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" dependencies = [ "async-trait", "bytes", "futures-util", - "http 0.2.12", - "http-body 0.4.6", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", "mime", + "pin-project-lite", "rustversion", + "sync_wrapper 0.1.2", "tower-layer", "tower-service", ] [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -375,6 +377,15 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -398,9 +409,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitvec" @@ -462,9 +473,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" dependencies = [ "serde", ] @@ -530,13 +541,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.98" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549" dependencies = [ "jobserver", "libc", - "once_cell", ] [[package]] @@ -580,7 +590,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -623,9 +633,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36" dependencies = [ "clap_builder", "clap_derive", @@ -633,9 +643,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed" dependencies = [ "anstream", "anstyle", @@ -645,21 +655,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "coins-bip32" @@ -715,9 +725,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "colored" @@ -754,9 +764,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.11.4" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ff96486ccc291d36a958107caf2c0af8c78c0af7d31ae2f35ce055130de1a6" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" dependencies = [ "cfg-if", "cpufeatures", @@ -932,7 +942,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" dependencies = [ "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -946,16 +956,15 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.2" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "platforms", "rustc_version", "subtle", "zeroize", @@ -969,14 +978,14 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] name = "darling" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -984,27 +993,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] name = "darling_macro" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -1045,20 +1054,20 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] name = "diesel" -version = "2.2.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b696af9ff4c0d2a507db2c5faafa8aa0205e297e5f11e203a24226d5355e7a" +checksum = "bf97ee7261bb708fa3402fa9c17a54b70e90e3cb98afb3dc8999d5512cb03f94" dependencies = [ "diesel_derives", "libsqlite3-sys", @@ -1068,15 +1077,15 @@ dependencies = [ [[package]] name = "diesel_derives" -version = "2.2.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6fdd83d5947068817016e939596d246e5367279453f2a3433287894f2f2996" +checksum = "d6ff2be1e7312c858b2ef974f5c7089833ae57b5311b334b30923af58e5718d8" dependencies = [ "diesel_table_macro_syntax", "dsl_auto_type", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -1096,15 +1105,9 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" dependencies = [ - "syn 2.0.66", + "syn 2.0.72", ] -[[package]] -name = "difflib" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" - [[package]] name = "digest" version = "0.9.0" @@ -1176,23 +1179,23 @@ checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "dsl_auto_type" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab32c18ea6760d951659768a3e35ea72fc1ba0916d665a88dfe048b2a41e543f" +checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607" dependencies = [ "darling", "either", - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] name = "dunce" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "ecdsa" @@ -1248,9 +1251,9 @@ dependencies = [ [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elliptic-curve" @@ -1336,19 +1339,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "env_logger" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -1504,14 +1494,14 @@ dependencies = [ "ethers-core", "ethers-etherscan", "eyre", - "prettyplease 0.2.20", + "prettyplease", "proc-macro2", "quote", "regex", "reqwest 0.11.27", "serde", "serde_json", - "syn 2.0.66", + "syn 2.0.72", "toml", "walkdir", ] @@ -1529,7 +1519,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -1555,7 +1545,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.66", + "syn 2.0.72", "tempfile", "thiserror", "tiny-keccak", @@ -1778,35 +1768,14 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" dependencies = [ "crc32fast", "miniz_oxide", ] -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" -dependencies = [ - "num-traits", -] - -[[package]] -name = "flume" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" -dependencies = [ - "futures-core", - "futures-sink", - "nanorand", - "spin 0.9.8", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1925,7 +1894,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -2013,9 +1982,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "glob" @@ -2069,7 +2038,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.2.6", + "indexmap 2.3.0", "slab", "tokio", "tokio-util", @@ -2088,7 +2057,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.1.0", - "indexmap 2.2.6", + "indexmap 2.3.0", "slab", "tokio", "tokio-util", @@ -2150,12 +2119,6 @@ dependencies = [ "http 0.2.12", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -2234,7 +2197,7 @@ dependencies = [ "hpke-rs-crypto", "log", "serde", - "tls_codec 0.4.1", + "tls_codec", "zeroize", ] @@ -2300,9 +2263,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http 1.1.0", @@ -2310,22 +2273,22 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "futures-core", + "futures-util", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -2333,17 +2296,11 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", @@ -2365,17 +2322,18 @@ dependencies = [ [[package]] name = "hyper" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", "futures-util", "h2 0.4.5", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -2391,22 +2349,40 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.28", + "hyper 0.14.30", "rustls 0.21.12", "tokio", "tokio-rustls 0.24.1", ] +[[package]] +name = "hyper-rustls" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.4.1", + "hyper-util", + "rustls 0.23.12", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", +] + [[package]] name = "hyper-timeout" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" dependencies = [ - "hyper 0.14.28", + "hyper 1.4.1", + "hyper-util", "pin-project-lite", "tokio", - "tokio-io-timeout", + "tower-service", ] [[package]] @@ -2417,7 +2393,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.3.1", + "hyper 1.4.1", "hyper-util", "native-tls", "tokio", @@ -2427,16 +2403,16 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d8d52be92d09acc2e01dddb7fde3ad983fc6489c7db4837e605bc3fca4cb63e" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" dependencies = [ "bytes", "futures-channel", "futures-util", "http 1.1.0", - "http-body 1.0.0", - "hyper 1.3.1", + "http-body 1.0.1", + "hyper 1.4.1", "pin-project-lite", "socket2", "tokio", @@ -2540,9 +2516,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown 0.14.5", @@ -2598,9 +2574,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "isolang" @@ -2631,9 +2607,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -2646,9 +2622,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] @@ -2735,7 +2711,7 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", "string_cache", "term", "tiny-keccak", @@ -2749,14 +2725,14 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata 0.4.6", + "regex-automata 0.4.7", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" @@ -2776,7 +2752,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", ] @@ -2830,9 +2806,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +checksum = "d4588d65215825ee71ebff9e1c9982067833b1355d7546845ffdb3165cbd7456" dependencies = [ "cc", "openssl-sys", @@ -2858,9 +2834,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" dependencies = [ "serde", "value-bag", @@ -2893,9 +2869,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "migrations_internals" @@ -2926,9 +2902,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", @@ -2936,47 +2912,48 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.11" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "mls_validation_service" -version = "0.1.0" +version = "0.0.1" dependencies = [ "anyhow", "clap", "ed25519-dalek", - "env_logger", "ethers", "futures", "hex", - "log", "openmls", "openmls_basic_credential", "openmls_rust_crypto", "openmls_traits", - "prost 0.12.6", + "prost", "rand", "serde", "sha2 0.10.8", "thiserror", "tokio", "tonic", + "tracing", + "tracing-subscriber", "warp", "xmtp_id", "xmtp_mls", @@ -2985,14 +2962,13 @@ dependencies = [ [[package]] name = "mockall" -version = "0.11.4" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" +checksum = "d4c28b3fb6d753d28c20e826cd46ee611fda1cf3cde03a443a974043247c065a" dependencies = [ "cfg-if", "downcast", "fragile", - "lazy_static", "mockall_derive", "predicates", "predicates-tree", @@ -3000,26 +2976,31 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.11.4" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" +checksum = "341014e7f530314e9a1fdbc7400b244efea7122662c96bfa248c31da5bfb2020" dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] name = "mockito" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f6e023aa5bdf392aa06c78e4a4e6d498baab5138d0c993503350ebbc37bf1e" +checksum = "09b34bd91b9e5c5b06338d392463e1318d683cf82ec3d3af4014609be6e2108d" dependencies = [ "assert-json-diff", + "bytes", "colored", - "futures-core", - "hyper 0.14.28", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-util", "log", "rand", "regex", @@ -3053,28 +3034,12 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" -[[package]] -name = "multimap" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" - -[[package]] -name = "nanorand" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" -dependencies = [ - "getrandom", -] - [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -3092,12 +3057,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" -[[package]] -name = "normalize-line-endings" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3110,9 +3069,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", @@ -3155,23 +3114,23 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -3182,9 +3141,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.32.2" +version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" dependencies = [ "memchr", ] @@ -3197,9 +3156,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" -version = "11.1.3" +version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "opaque-debug" @@ -3234,12 +3193,13 @@ dependencies = [ [[package]] name = "openmls" -version = "0.5.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.6.0-pre.2" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "backtrace", "itertools 0.10.5", "log", + "once_cell", "openmls_basic_credential", "openmls_memory_storage", "openmls_rust_crypto", @@ -3250,27 +3210,27 @@ dependencies = [ "serde", "serde_json", "thiserror", - "tls_codec 0.4.2-pre.1", + "tls_codec", "wasm-bindgen-test", ] [[package]] name = "openmls_basic_credential" -version = "0.2.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.3.0-pre.1" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "ed25519-dalek", "openmls_traits", "p256", "rand", "serde", - "tls_codec 0.4.2-pre.1", + "tls_codec", ] [[package]] name = "openmls_memory_storage" -version = "0.2.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.3.0-pre.2" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "hex", "log", @@ -3282,8 +3242,8 @@ dependencies = [ [[package]] name = "openmls_rust_crypto" -version = "0.2.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.3.0-pre.1" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "aes-gcm", "chacha20poly1305", @@ -3301,13 +3261,13 @@ dependencies = [ "serde", "sha2 0.10.8", "thiserror", - "tls_codec 0.4.2-pre.1", + "tls_codec", ] [[package]] name = "openmls_test" -version = "0.1.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.1.0-pre.1" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "ansi_term", "openmls_rust_crypto", @@ -3316,25 +3276,25 @@ dependencies = [ "quote", "rstest", "rstest_reuse", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] name = "openmls_traits" -version = "0.2.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.3.0-pre.2" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "serde", - "tls_codec 0.4.2-pre.1", + "tls_codec", ] [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -3351,7 +3311,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -3362,18 +3322,18 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.3.0+3.3.0" +version = "300.3.1+3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eba8804a1c5765b18c4b3f907e6897ebabeedebc9830e1a0046c4a4cf44663e1" +checksum = "7259953d42a81bf137fbbd73bd30a8e1914d6dce43c2b90ed575783a22608b91" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -3462,7 +3422,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -3484,19 +3444,9 @@ checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" [[package]] name = "pbjson" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048f9ac93c1eab514f9470c4bc8d97ca2a0a236b84f45cc19d69a59fc11467f6" -dependencies = [ - "base64 0.13.1", - "serde", -] - -[[package]] -name = "pbjson" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1030c719b0ec2a2d25a5df729d6cff1acf3cc230bf766f4f97833591f7577b90" +checksum = "c7e6349fa080353f4a597daffd05cb81572a9c031a6d4fff7e504947496fcc68" dependencies = [ "base64 0.21.7", "serde", @@ -3504,55 +3454,28 @@ dependencies = [ [[package]] name = "pbjson-build" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdbb7b706f2afc610f3853550cdbbf6372fd324824a087806bd4480ea4996e24" -dependencies = [ - "heck 0.4.1", - "itertools 0.10.5", - "prost 0.11.9", - "prost-types 0.11.9", -] - -[[package]] -name = "pbjson-build" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2580e33f2292d34be285c5bc3dba5259542b083cfad6037b6d70345f24dcb735" -dependencies = [ - "heck 0.4.1", - "itertools 0.11.0", - "prost 0.12.6", - "prost-types 0.12.6", -] - -[[package]] -name = "pbjson-types" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a88c8d87f99a4ac14325e7a4c24af190fca261956e3b82dd7ed67e77e6c7043" +checksum = "6eea3058763d6e656105d1403cb04e0a41b7bbac6362d413e7c33be0c32279c9" dependencies = [ - "bytes", - "chrono", - "pbjson 0.5.1", - "pbjson-build 0.5.1", - "prost 0.11.9", - "prost-build 0.11.9", - "serde", + "heck", + "itertools 0.13.0", + "prost", + "prost-types", ] [[package]] name = "pbjson-types" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18f596653ba4ac51bdecbb4ef6773bc7f56042dc13927910de1684ad3d32aa12" +checksum = "e54e5e7bfb1652f95bc361d76f3c780d8e526b134b85417e774166ee941f0887" dependencies = [ "bytes", "chrono", - "pbjson 0.6.0", - "pbjson-build 0.6.2", - "prost 0.12.6", - "prost-build 0.12.6", + "pbjson", + "pbjson-build", + "prost", + "prost-build", "serde", ] @@ -3609,7 +3532,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.2.6", + "indexmap 2.3.0", ] [[package]] @@ -3652,7 +3575,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -3690,7 +3613,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -3731,12 +3654,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" -[[package]] -name = "platforms" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" - [[package]] name = "plotters" version = "0.3.6" @@ -3790,9 +3707,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" [[package]] name = "powerfmt" @@ -3802,9 +3719,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "precomputed-hash" @@ -3814,44 +3734,30 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "predicates" -version = "2.1.5" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" dependencies = [ - "difflib", - "float-cmp", - "itertools 0.10.5", - "normalize-line-endings", + "anstyle", "predicates-core", - "regex", ] [[package]] name = "predicates-core" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" [[package]] name = "predicates-tree" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" dependencies = [ "predicates-core", "termtree", ] -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2", - "syn 1.0.109", -] - [[package]] name = "prettyplease" version = "0.2.20" @@ -3859,7 +3765,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -3896,134 +3802,80 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", "unarray", ] [[package]] name = "prost" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" -dependencies = [ - "bytes", - "prost-derive 0.11.9", -] - -[[package]] -name = "prost" -version = "0.12.6" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc" dependencies = [ "bytes", - "prost-derive 0.12.6", + "prost-derive", ] [[package]] name = "prost-build" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" -dependencies = [ - "bytes", - "heck 0.4.1", - "itertools 0.10.5", - "lazy_static", - "log", - "multimap 0.8.3", - "petgraph", - "prettyplease 0.1.25", - "prost 0.11.9", - "prost-types 0.11.9", - "regex", - "syn 1.0.109", - "tempfile", - "which", -] - -[[package]] -name = "prost-build" -version = "0.12.6" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" +checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1" dependencies = [ "bytes", - "heck 0.5.0", - "itertools 0.12.1", + "heck", + "itertools 0.13.0", "log", - "multimap 0.10.0", + "multimap", "once_cell", "petgraph", - "prettyplease 0.2.20", - "prost 0.12.6", - "prost-types 0.12.6", + "prettyplease", + "prost", + "prost-types", "regex", - "syn 2.0.66", + "syn 2.0.72", "tempfile", ] [[package]] name = "prost-derive" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" -dependencies = [ - "anyhow", - "itertools 0.10.5", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "prost-derive" -version = "0.12.6" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" -dependencies = [ - "prost 0.11.9", -] - -[[package]] -name = "prost-types" -version = "0.12.6" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2" dependencies = [ - "prost 0.12.6", + "prost", ] [[package]] @@ -4113,11 +3965,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -4133,14 +3985,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -4154,13 +4006,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] @@ -4171,9 +4023,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" @@ -4189,8 +4041,8 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.28", - "hyper-rustls", + "hyper 0.14.30", + "hyper-rustls 0.24.2", "ipnet", "js-sys", "log", @@ -4203,7 +4055,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-rustls 0.24.1", @@ -4218,9 +4070,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ "base64 0.22.1", "bytes", @@ -4229,9 +4081,10 @@ dependencies = [ "futures-util", "h2 0.4.5", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "http-body-util", - "hyper 1.3.1", + "hyper 1.4.1", + "hyper-rustls 0.27.2", "hyper-tls", "hyper-util", "ipnet", @@ -4242,11 +4095,11 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 1.0.1", "system-configuration", "tokio", "tokio-native-tls", @@ -4407,7 +4260,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -4428,26 +4281,27 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.4" +version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ "log", + "once_cell", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.4", + "rustls-webpki 0.102.6", "subtle", "zeroize", ] [[package]] name = "rustls-native-certs" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.2", + "rustls-pemfile 2.1.3", "rustls-pki-types", "schannel", "security-framework", @@ -4464,9 +4318,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ "base64 0.22.1", "rustls-pki-types", @@ -4474,19 +4328,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" - -[[package]] -name = "rustls-webpki" -version = "0.100.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-webpki" @@ -4500,9 +4344,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.4" +version = "0.102.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" dependencies = [ "ring 0.17.8", "rustls-pki-types", @@ -4645,11 +4489,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -4658,9 +4502,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", @@ -4689,22 +4533,22 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -4718,20 +4562,21 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -4823,9 +4668,9 @@ dependencies = [ [[package]] name = "similar" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" [[package]] name = "simple_asn1" @@ -4860,17 +4705,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "smart-default" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - [[package]] name = "socket2" version = "0.5.7" @@ -4906,9 +4740,6 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] [[package]] name = "spki" @@ -4957,31 +4788,31 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", "rustversion", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sval" @@ -5094,9 +4925,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -5109,6 +4940,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "system-configuration" version = "0.5.1" @@ -5138,14 +4975,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5159,15 +4997,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - [[package]] name = "termtree" version = "0.4.1" @@ -5176,22 +5005,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -5266,9 +5095,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -5286,17 +5115,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e78c9c330f8c85b2bae7c8368f2739157db9991235123aa1b15ef9502bfb6a" dependencies = [ "serde", - "tls_codec_derive 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize", -] - -[[package]] -name = "tls_codec" -version = "0.4.2-pre.1" -source = "git+https://github.com/rustcrypto/formats#6c42217ec681a073bfbdfffd78588306f9063de2" -dependencies = [ - "serde", - "tls_codec_derive 0.4.1 (git+https://github.com/rustcrypto/formats)", + "tls_codec_derive", "zeroize", ] @@ -5308,57 +5127,37 @@ checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", -] - -[[package]] -name = "tls_codec_derive" -version = "0.4.1" -source = "git+https://github.com/rustcrypto/formats#6c42217ec681a073bfbdfffd78588306f9063de2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] name = "tokio" -version = "1.37.0" +version = "1.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", + "tracing", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -5383,11 +5182,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.22.4", + "rustls 0.23.12", "rustls-pki-types", "tokio", ] @@ -5401,6 +5200,7 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", + "tokio-util", ] [[package]] @@ -5445,21 +5245,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.13" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.13", + "toml_edit 0.22.20", ] [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -5470,54 +5270,56 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.3.0", "toml_datetime", "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.3.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.8", + "winnow 0.6.18", ] [[package]] name = "tonic" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.21.7", + "base64 0.22.1", "bytes", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.28", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", "hyper-timeout", + "hyper-util", "percent-encoding", "pin-project", - "prost 0.12.6", + "prost", "rustls-native-certs", - "rustls-pemfile 2.1.2", - "rustls-pki-types", + "rustls-pemfile 2.1.3", + "socket2", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.0", "tokio-stream", "tower", "tower-layer", "tower-service", "tracing", - "webpki-roots 0.26.1", + "webpki-roots 0.26.3", ] [[package]] @@ -5572,7 +5374,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -5636,26 +5438,14 @@ dependencies = [ ] [[package]] -name = "tracing-test" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a2c0ff408fe918a94c428a3f2ad04e4afd5c95bbc08fcf868eff750c15728a4" -dependencies = [ - "lazy_static", - "tracing-core", - "tracing-subscriber", - "tracing-test-macro", -] - -[[package]] -name = "tracing-test-macro" -version = "0.2.4" +name = "trait-variant" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bc1c4f8e2e73a977812ab339d503e6feeb92700f6d07a6de4d321522d5c08" +checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" dependencies = [ - "lazy_static", + "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] @@ -5765,9 +5555,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" @@ -5799,9 +5589,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -5816,9 +5606,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" @@ -5832,9 +5622,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom", ] @@ -5889,9 +5679,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" @@ -5923,7 +5713,7 @@ dependencies = [ "futures-util", "headers", "http 0.2.12", - "hyper 0.14.28", + "hyper 0.14.30", "log", "mime", "mime_guess", @@ -5970,7 +5760,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", "wasm-bindgen-shared", ] @@ -6004,7 +5794,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6037,7 +5827,7 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -6063,15 +5853,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-roots" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.3", -] - [[package]] name = "webpki-roots" version = "0.25.4" @@ -6080,25 +5861,13 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.1" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" dependencies = [ "rustls-pki-types", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "winapi" version = "0.3.9" @@ -6117,11 +5886,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6136,7 +5905,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -6154,7 +5923,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -6174,18 +5952,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -6196,9 +5974,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -6208,9 +5986,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -6220,15 +5998,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -6238,9 +6016,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -6250,9 +6028,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -6262,9 +6040,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -6274,9 +6052,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -6289,9 +6067,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -6358,48 +6136,54 @@ dependencies = [ [[package]] name = "xmtp_api_grpc" -version = "0.1.0" +version = "0.0.1" dependencies = [ "async-stream", - "base64 0.21.7", + "base64 0.22.1", "futures", "hex", - "http-body 0.4.6", - "hyper 0.14.28", - "log", - "pbjson 0.5.1", - "pbjson-types 0.5.1", - "prost 0.12.6", + "prost", "serde", "tokio", "tonic", - "tower", "tracing", - "uuid 1.8.0", - "webpki-roots 0.23.1", + "uuid 1.10.0", "xmtp_proto", "xmtp_v2", ] +[[package]] +name = "xmtp_api_http" +version = "0.0.1" +dependencies = [ + "async-stream", + "futures", + "reqwest 0.12.5", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "xmtp_proto", +] + [[package]] name = "xmtp_cli" version = "0.1.0" dependencies = [ "clap", "ethers", - "ethers-core", "femme", "futures", "hex", "kv-log-macro", "log", - "prost 0.12.6", + "prost", "serde", "serde_json", "thiserror", "timeago", "tokio", - "url", "xmtp_api_grpc", "xmtp_cryptography", "xmtp_id", @@ -6409,15 +6193,14 @@ dependencies = [ [[package]] name = "xmtp_cryptography" -version = "0.1.0" +version = "0.0.1" dependencies = [ "curve25519-dalek", - "ecdsa 0.15.1", + "ecdsa 0.16.9", "ethers", - "ethers-core", + "getrandom", "hex", - "k256 0.12.0", - "log", + "k256 0.13.3", "rand", "rand_chacha", "rustc-hex", @@ -6426,26 +6209,22 @@ dependencies = [ "sha3", "thiserror", "tokio", + "tracing", ] [[package]] name = "xmtp_id" -version = "0.1.0" +version = "0.0.1" dependencies = [ "async-trait", "chrono", "ctor", - "ed25519", "ed25519-dalek", "ethers", "futures", "hex", - "log", - "openmls", - "openmls_basic_credential", - "openmls_rust_crypto", "openmls_traits", - "prost 0.12.6", + "prost", "rand", "regex", "rustc-hex", @@ -6462,27 +6241,23 @@ dependencies = [ [[package]] name = "xmtp_mls" -version = "0.1.0" +version = "0.0.1" dependencies = [ - "aes", "aes-gcm", "anyhow", "async-barrier", - "async-trait", - "chrono", + "async-stream", + "bincode", "criterion", "ctor", "diesel", "diesel_migrations", "ed25519-dalek", "ethers", - "ethers-core", - "flume", "futures", "hex", "indicatif", "libsqlite3-sys", - "log", "mockall", "mockito", "once_cell", @@ -6490,56 +6265,52 @@ dependencies = [ "openmls_basic_credential", "openmls_rust_crypto", "openmls_traits", - "prost 0.12.6", + "parking_lot", + "prost", "rand", - "reqwest 0.12.4", - "ring 0.17.8", + "reqwest 0.12.5", "serde", "serde_json", "sha2 0.10.8", - "smart-default", "tempfile", "thiserror", - "tls_codec 0.4.1", + "tls_codec", "tokio", + "tokio-stream", "toml", "tracing", "tracing-flame", - "tracing-log", "tracing-subscriber", - "tracing-test", + "trait-variant", "xmtp_api_grpc", + "xmtp_api_http", "xmtp_cryptography", "xmtp_id", "xmtp_proto", - "xmtp_v2", ] [[package]] name = "xmtp_proto" -version = "0.1.0" +version = "0.0.1" dependencies = [ - "async-trait", "futures", - "futures-core", "openmls", - "openmls_basic_credential", - "pbjson 0.6.0", - "pbjson-types 0.6.0", - "prost 0.12.6", - "prost-types 0.12.6", + "pbjson", + "pbjson-types", + "prost", + "prost-types", "serde", "tonic", + "trait-variant", ] [[package]] name = "xmtp_user_preferences" -version = "0.1.0" +version = "0.0.1" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "libsecp256k1", - "once_cell", - "prost 0.12.6", + "prost", "rand", "xmtp_proto", "xmtp_v2", @@ -6547,7 +6318,7 @@ dependencies = [ [[package]] name = "xmtp_v2" -version = "0.1.0" +version = "0.0.1" dependencies = [ "aes-gcm", "ecdsa 0.15.1", @@ -6568,11 +6339,32 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -6585,7 +6377,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -6629,9 +6421,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index c6130f81c..a54c14d6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "mls_validation_service", "xmtp_cryptography", "xmtp_api_grpc", + "xmtp_api_http", "xmtp_proto", "xmtp_user_preferences", "xmtp_v2", @@ -12,36 +13,37 @@ members = [ "xmtp_id", ] -exclude = [ - "bindings_ffi", - "bindings_wasm", - "xmtp_api_grpc_gateway", - "bindings_node", -] +exclude = ["bindings_ffi", "bindings_wasm", "bindings_node"] # Make the feature resolver explicit. # See https://doc.rust-lang.org/edition-guide/rust-2021/default-cargo-resolver.html#details resolver = "2" +[workspace.package] +version = "0.0.1" + [workspace.dependencies] anyhow = "1.0" +async-stream = "0.3" async-trait = "0.1.77" +trait-variant = "0.1.2" chrono = "0.4.38" ctor = "0.2" ed25519 = "2.2.3" ed25519-dalek = "2.1.1" ethers = "2.0.11" -ethers-core = "2.0.4" futures = "0.3.30" futures-core = "0.3.30" +getrandom = { version = "0.2", default-features = false } hex = "0.4.3" -log = { version = "0.4", features = ["release_max_level_debug"] } -openmls = { git = "https://github.com/xmtp/openmls", rev = "99b2d5e7d0e034ac57644395e2194c5a102afb9a" } -openmls_basic_credential = { git = "https://github.com/xmtp/openmls", rev = "99b2d5e7d0e034ac57644395e2194c5a102afb9a" } -openmls_rust_crypto = { git = "https://github.com/xmtp/openmls", rev = "99b2d5e7d0e034ac57644395e2194c5a102afb9a" } -openmls_traits = { git = "https://github.com/xmtp/openmls", rev = "99b2d5e7d0e034ac57644395e2194c5a102afb9a" } -prost = "^0.12" -prost-types = "^0.12" +openmls = { git = "https://github.com/xmtp/openmls", rev = "87e7e257d8eb15d6662b104518becfc75ef6db76", default-features = false } +openmls_basic_credential = { git = "https://github.com/xmtp/openmls", rev = "87e7e257d8eb15d6662b104518becfc75ef6db76" } +openmls_rust_crypto = { git = "https://github.com/xmtp/openmls", rev = "87e7e257d8eb15d6662b104518becfc75ef6db76" } +openmls_traits = { git = "https://github.com/xmtp/openmls", rev = "87e7e257d8eb15d6662b104518becfc75ef6db76" } +pbjson = "0.7.0" +pbjson-types = "0.7.0" +prost = "^0.13" +prost-types = "^0.13" rand = "0.8.5" regex = "1.10.4" rustc-hex = "2.1.0" @@ -49,10 +51,10 @@ serde = "1.0" serde_json = "1.0" sha2 = "0.10.8" thiserror = "1.0" -tls_codec = "0.4.0" -tokio = { version = "1.35.1", features = ["macros"] } -tonic = "^0.11" -tracing = { version = "0.1", features = ["release_max_level_debug"] } +tls_codec = "0.4.1" +tokio = { version = "1.35.1", default-features = false } +tonic = "^0.12" +tracing = { version = "0.1", features = ["log"] } tracing-subscriber = "0.3" url = "2.5.0" diff --git a/Dockerfile b/Dockerfile index a2744c2d4..1d594a6ce 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,7 @@ COPY --chown=xmtp:xmtp rust-toolchain . ENV RUSTUP_PERMIT_COPY_RENAME "yes" RUN rustup update +RUN curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin LABEL org.label-schema.build-date=$BUILD_DATE \ org.label-schema.name="rustdev" \ diff --git a/README.md b/README.md index 207e5c5c4..a68e886bc 100644 --- a/README.md +++ b/README.md @@ -55,13 +55,13 @@ libxmtp/ ├ examples/ -   ├ [`android/xmtpv3_example`](./examples/android/xmtpv3_example): Example Android app (in progress) +│ ├ [`android/xmtpv3_example`](./examples/android/xmtpv3_example): Example Android app (in progress) -   └ [`cli`](./examples/cli): Example XMTP console client. Use the CLI to try out sending double ratchet messages on the XMTP `dev` network. +│ └ [`cli`](./examples/cli): Example XMTP console client. Use the CLI to try out sending double ratchet messages on the XMTP `dev` network. ├ [`xmtp_api_grpc`](./xmtp_api_grpc): API client for XMTP's gRPC API, using code from `xmtp_proto` -├ [`xmtp_api_grpc_gateway`](./xmtp_api_grpc_gateway): API client for XMTP's gRPC Gateway API, using code from `xmtp_proto` (in progress) +├ [`xmtp_api_http`](./xmtp_api_http): API client for XMTP's gRPC Gateway API, using code from `xmtp_proto` ├ [`xmtp_cryptography`](./xmtp_cryptography): Cryptographic operations @@ -71,7 +71,6 @@ libxmtp/ └ [`xmtp_v2`](./xmtp_v2): Version 2 of XMTP which uses a [user key bundle](https://xmtp.org/docs/concepts/key-generation-and-usage) to encrypt and exchange messages. - ### Run the benchmarks **possible benchmarks include:** diff --git a/bindings_ffi/Cargo.lock b/bindings_ffi/Cargo.lock index 44d71b556..b99ee0775 100644 --- a/bindings_ffi/Cargo.lock +++ b/bindings_ffi/Cargo.lock @@ -205,16 +205,6 @@ dependencies = [ "nom", ] -[[package]] -name = "async-barrier" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04b50fe84d0aea412a4e751cb01b9e26e2be533b2708607c2a2a739b192ee45a" -dependencies = [ - "async-mutex", - "event-listener", -] - [[package]] name = "async-compat" version = "0.2.3" @@ -228,15 +218,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "async-mutex" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" -dependencies = [ - "event-listener", -] - [[package]] name = "async-stream" version = "0.3.5" @@ -307,18 +288,17 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.20" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", "axum-core", - "bitflags 1.3.2", "bytes", "futures-util", - "http 0.2.11", - "http-body 0.4.6", - "hyper 0.14.28", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", "itoa", "matchit", "memchr", @@ -327,7 +307,7 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper", + "sync_wrapper 1.0.1", "tower", "tower-layer", "tower-service", @@ -335,17 +315,20 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" dependencies = [ "async-trait", "bytes", "futures-util", - "http 0.2.11", - "http-body 0.4.6", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", "mime", + "pin-project-lite", "rustversion", + "sync_wrapper 0.1.2", "tower-layer", "tower-service", ] @@ -996,9 +979,9 @@ dependencies = [ [[package]] name = "diesel" -version = "2.2.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b696af9ff4c0d2a507db2c5faafa8aa0205e297e5f11e203a24226d5355e7a" +checksum = "bf97ee7261bb708fa3402fa9c17a54b70e90e3cb98afb3dc8999d5512cb03f94" dependencies = [ "diesel_derives", "libsqlite3-sys", @@ -1255,29 +1238,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "env_filter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "humantime", - "log", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -1313,7 +1273,7 @@ dependencies = [ "sha2", "sha3", "thiserror", - "uuid", + "uuid 0.8.2", ] [[package]] @@ -1423,7 +1383,7 @@ dependencies = [ "ethers-core", "ethers-etherscan", "eyre", - "prettyplease 0.2.16", + "prettyplease", "proc-macro2", "quote", "regex", @@ -1612,12 +1572,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "eyre" version = "0.6.11" @@ -2074,7 +2028,7 @@ dependencies = [ "hpke-rs-crypto", "log", "serde", - "tls_codec 0.4.0", + "tls_codec", "zeroize", ] @@ -2173,12 +2127,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "hyper" version = "0.14.28" @@ -2216,6 +2164,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.0", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -2239,14 +2188,15 @@ dependencies = [ [[package]] name = "hyper-timeout" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" dependencies = [ - "hyper 0.14.28", + "hyper 1.3.1", + "hyper-util", "pin-project-lite", "tokio", - "tokio-io-timeout", + "tower-service", ] [[package]] @@ -2441,6 +2391,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.10" @@ -2575,9 +2534,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +checksum = "d4588d65215825ee71ebff9e1c9982067833b1355d7546845ffdb3165cbd7456" dependencies = [ "cc", "openssl-sys", @@ -2742,6 +2701,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.45" @@ -2841,12 +2806,13 @@ dependencies = [ [[package]] name = "openmls" -version = "0.5.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.6.0-pre.2" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "backtrace", "itertools 0.10.5", "log", + "once_cell", "openmls_basic_credential", "openmls_memory_storage", "openmls_rust_crypto", @@ -2857,27 +2823,27 @@ dependencies = [ "serde", "serde_json", "thiserror", - "tls_codec 0.4.2-pre.1", + "tls_codec", "wasm-bindgen-test", ] [[package]] name = "openmls_basic_credential" -version = "0.2.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.3.0-pre.1" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "ed25519-dalek", "openmls_traits", "p256", "rand", "serde", - "tls_codec 0.4.2-pre.1", + "tls_codec", ] [[package]] name = "openmls_memory_storage" -version = "0.2.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.3.0-pre.2" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "hex", "log", @@ -2889,8 +2855,8 @@ dependencies = [ [[package]] name = "openmls_rust_crypto" -version = "0.2.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.3.0-pre.1" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "aes-gcm", "chacha20poly1305", @@ -2908,13 +2874,13 @@ dependencies = [ "serde", "sha2", "thiserror", - "tls_codec 0.4.2-pre.1", + "tls_codec", ] [[package]] name = "openmls_test" -version = "0.1.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.1.0-pre.1" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "ansi_term", "openmls_rust_crypto", @@ -2928,11 +2894,11 @@ dependencies = [ [[package]] name = "openmls_traits" -version = "0.2.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.3.0-pre.2" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "serde", - "tls_codec 0.4.2-pre.1", + "tls_codec", ] [[package]] @@ -3045,9 +3011,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -3091,19 +3057,9 @@ checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" [[package]] name = "pbjson" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048f9ac93c1eab514f9470c4bc8d97ca2a0a236b84f45cc19d69a59fc11467f6" -dependencies = [ - "base64 0.13.1", - "serde", -] - -[[package]] -name = "pbjson" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1030c719b0ec2a2d25a5df729d6cff1acf3cc230bf766f4f97833591f7577b90" +checksum = "c7e6349fa080353f4a597daffd05cb81572a9c031a6d4fff7e504947496fcc68" dependencies = [ "base64 0.21.7", "serde", @@ -3111,55 +3067,28 @@ dependencies = [ [[package]] name = "pbjson-build" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdbb7b706f2afc610f3853550cdbbf6372fd324824a087806bd4480ea4996e24" -dependencies = [ - "heck 0.4.1", - "itertools 0.10.5", - "prost 0.11.9", - "prost-types 0.11.9", -] - -[[package]] -name = "pbjson-build" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2580e33f2292d34be285c5bc3dba5259542b083cfad6037b6d70345f24dcb735" -dependencies = [ - "heck 0.4.1", - "itertools 0.11.0", - "prost 0.12.3", - "prost-types 0.12.3", -] - -[[package]] -name = "pbjson-types" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a88c8d87f99a4ac14325e7a4c24af190fca261956e3b82dd7ed67e77e6c7043" +checksum = "6eea3058763d6e656105d1403cb04e0a41b7bbac6362d413e7c33be0c32279c9" dependencies = [ - "bytes", - "chrono", - "pbjson 0.5.1", - "pbjson-build 0.5.1", - "prost 0.11.9", - "prost-build 0.11.9", - "serde", + "heck 0.5.0", + "itertools 0.13.0", + "prost", + "prost-types", ] [[package]] name = "pbjson-types" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18f596653ba4ac51bdecbb4ef6773bc7f56042dc13927910de1684ad3d32aa12" +checksum = "e54e5e7bfb1652f95bc361d76f3c780d8e526b134b85417e774166ee941f0887" dependencies = [ "bytes", "chrono", - "pbjson 0.6.0", - "pbjson-build 0.6.2", - "prost 0.12.3", - "prost-build 0.12.3", + "pbjson", + "pbjson-build", + "prost", + "prost-build", "serde", ] @@ -3391,16 +3320,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2", - "syn 1.0.109", -] - [[package]] name = "prettyplease" version = "0.2.16" @@ -3513,89 +3432,43 @@ dependencies = [ [[package]] name = "prost" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" -dependencies = [ - "bytes", - "prost-derive 0.11.9", -] - -[[package]] -name = "prost" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" -dependencies = [ - "bytes", - "prost-derive 0.12.3", -] - -[[package]] -name = "prost-build" -version = "0.11.9" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc" dependencies = [ "bytes", - "heck 0.4.1", - "itertools 0.10.5", - "lazy_static", - "log", - "multimap", - "petgraph", - "prettyplease 0.1.25", - "prost 0.11.9", - "prost-types 0.11.9", - "regex", - "syn 1.0.109", - "tempfile", - "which", + "prost-derive", ] [[package]] name = "prost-build" -version = "0.12.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2" +checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1" dependencies = [ "bytes", - "heck 0.4.1", - "itertools 0.11.0", + "heck 0.5.0", + "itertools 0.13.0", "log", "multimap", "once_cell", "petgraph", - "prettyplease 0.2.16", - "prost 0.12.3", - "prost-types 0.12.3", + "prettyplease", + "prost", + "prost-types", "regex", "syn 2.0.48", "tempfile", - "which", ] [[package]] name = "prost-derive" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" -dependencies = [ - "anyhow", - "itertools 0.10.5", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "prost-derive" -version = "0.12.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" +checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca" dependencies = [ "anyhow", - "itertools 0.11.0", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.48", @@ -3603,20 +3476,11 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" -dependencies = [ - "prost 0.11.9", -] - -[[package]] -name = "prost-types" -version = "0.12.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" +checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2" dependencies = [ - "prost 0.12.3", + "prost", ] [[package]] @@ -3829,7 +3693,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -4011,11 +3875,12 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.2" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +checksum = "ebbbdb961df0ad3f2652da8f3fdc4b36122f568f968f45ad3316f26c025c677b" dependencies = [ "log", + "once_cell", "ring 0.17.8", "rustls-pki-types", "rustls-webpki 0.102.2", @@ -4025,9 +3890,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ "openssl-probe", "rustls-pemfile 2.1.0", @@ -4061,16 +3926,6 @@ version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" -[[package]] -name = "rustls-webpki" -version = "0.100.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] - [[package]] name = "rustls-webpki" version = "0.101.7" @@ -4426,17 +4281,6 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" -[[package]] -name = "smart-default" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - [[package]] name = "smawk" version = "0.3.2" @@ -4606,6 +4450,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "system-configuration" version = "0.5.1" @@ -4664,8 +4514,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" dependencies = [ "smawk", - "unicode-linebreak", - "unicode-width", ] [[package]] @@ -4700,12 +4548,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -4720,10 +4569,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -4753,40 +4603,20 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tls_codec" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38a1d5fcfa859f0ec2b5e111dc903890bd7dac7f34713232bf9aa4fd7cad7b2" +checksum = "b5e78c9c330f8c85b2bae7c8368f2739157db9991235123aa1b15ef9502bfb6a" dependencies = [ "serde", - "tls_codec_derive 0.4.0", + "tls_codec_derive", "zeroize", ] -[[package]] -name = "tls_codec" -version = "0.4.2-pre.1" -source = "git+https://github.com/rustcrypto/formats#ef837bce43803eebc99922c6552b1cb9b2a01a03" -dependencies = [ - "serde", - "tls_codec_derive 0.4.1", - "zeroize", -] - -[[package]] -name = "tls_codec_derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e00e3e7a54e0f1c8834ce72ed49c8487fbd3f801d8cfe1a0ad0640382f8e15" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - [[package]] name = "tls_codec_derive" version = "0.4.1" -source = "git+https://github.com/rustcrypto/formats#ef837bce43803eebc99922c6552b1cb9b2a01a03" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c" dependencies = [ "proc-macro2", "quote", @@ -4809,19 +4639,10 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", + "tracing", "windows-sys 0.48.0", ] -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-macros" version = "2.2.0" @@ -4855,11 +4676,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.22.2", + "rustls 0.23.7", "rustls-pki-types", "tokio", ] @@ -4873,6 +4694,7 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", + "tokio-util", ] [[package]] @@ -4984,28 +4806,30 @@ dependencies = [ [[package]] name = "tonic" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.21.7", + "base64 0.22.1", "bytes", - "h2 0.3.24", - "http 0.2.11", - "http-body 0.4.6", - "hyper 0.14.28", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", "hyper-timeout", + "hyper-util", "percent-encoding", "pin-project", - "prost 0.12.3", + "prost", "rustls-native-certs", "rustls-pemfile 2.1.0", - "rustls-pki-types", + "socket2", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.0", "tokio-stream", "tower", "tower-layer", @@ -5088,6 +4912,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "trait-variant" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -5159,12 +4994,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-linebreak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" - [[package]] name = "unicode-normalization" version = "0.1.22" @@ -5174,12 +5003,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-width" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" - [[package]] name = "unicode-xid" version = "0.2.4" @@ -5188,9 +5011,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "uniffi" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab38ff7ce5037772ca9bf7667e4e8535d110f11c6e2ec8cc9c1a7fc66938650c" +checksum = "f31bff6daf87277a9014bcdefbc2842b0553392919d1096843c5aad899ca4588" dependencies = [ "anyhow", "camino", @@ -5203,19 +5026,18 @@ dependencies = [ [[package]] name = "uniffi_bindgen" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "480597c3b4074ab2faa39158f45f87f3ac33ccfd7bc7943ff0877372d9d8db97" +checksum = "96061d7e01b185aa405f7c9b134741ab3e50cc6796a47d6fd8ab9a5364b5feed" dependencies = [ "anyhow", "askama", "camino", "cargo_metadata 0.15.4", - "clap", "fs-err", "glob", "goblin", - "heck 0.4.1", + "heck 0.5.0", "once_cell", "paste", "serde", @@ -5228,9 +5050,9 @@ dependencies = [ [[package]] name = "uniffi_build" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497391e423074ed5dbd828a2860d6203a333123519a285560c5ae1fd78075de4" +checksum = "9d6b86f9b221046af0c533eafe09ece04e2f1ded04ccdc9bba0ec09aec1c52bd" dependencies = [ "anyhow", "camino", @@ -5239,9 +5061,9 @@ dependencies = [ [[package]] name = "uniffi_checksum_derive" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95e86ccd44c138ba12b9132decbabeed84bf686ebe4b6538a5e489a243a7c2c9" +checksum = "2fcfa22f55829d3aaa7acfb1c5150224188fe0f27c59a8a3eddcaa24d1ffbe58" dependencies = [ "quote", "syn 2.0.48", @@ -5249,9 +5071,9 @@ dependencies = [ [[package]] name = "uniffi_core" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52fcb15ab907c37fe50163f05f97d497bc4400d8bfbdb7ef56b3a9ef777188d4" +checksum = "3210d57d6ab6065ab47a2898dacdb7c606fd6a4156196831fa3bf82e34ac58a6" dependencies = [ "anyhow", "async-compat", @@ -5265,9 +5087,9 @@ dependencies = [ [[package]] name = "uniffi_macros" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "865e2144b19552516c288e7c0425553c64724a8e4862bcb0c169355008e0ff0d" +checksum = "b58691741080935437dc862122e68d7414432a11824ac1137868de46181a0bd2" dependencies = [ "bincode", "camino", @@ -5283,9 +5105,9 @@ dependencies = [ [[package]] name = "uniffi_meta" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7968bda370d74b9bffb9af1e9cdc9a354ce027dc313963860f26dcf6c8efcecf" +checksum = "7663eacdbd9fbf4a88907ddcfe2e6fa85838eb6dc2418a7d91eebb3786f8e20b" dependencies = [ "anyhow", "bytes", @@ -5295,9 +5117,9 @@ dependencies = [ [[package]] name = "uniffi_testing" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfe857c83a2655412745e31929c05486d02b340336b595b7044eff342cf6c91" +checksum = "f922465f7566f25f8fe766920205fdfa9a3fcdc209c6bfb7557f0b5bf45b04dd" dependencies = [ "anyhow", "camino", @@ -5308,9 +5130,9 @@ dependencies = [ [[package]] name = "uniffi_udl" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af11dd5dd1a60d9af5ef30cd37f37090999d998be0c9d34d5ddaf6cee138ed4a" +checksum = "cef408229a3a407fafa4c36dc4f6ece78a6fb258ab28d2b64bddd49c8cb680f6" dependencies = [ "anyhow", "textwrap", @@ -5374,6 +5196,16 @@ dependencies = [ "serde", ] +[[package]] +name = "uuid" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" +dependencies = [ + "getrandom", + "rand", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -5525,15 +5357,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-roots" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.3", -] - [[package]] name = "webpki-roots" version = "0.25.3" @@ -5558,18 +5381,6 @@ dependencies = [ "nom", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "winapi" version = "0.3.9" @@ -5813,39 +5624,31 @@ dependencies = [ [[package]] name = "xmtp_api_grpc" -version = "0.1.0" +version = "0.0.1" dependencies = [ "async-stream", - "base64 0.21.7", + "base64 0.22.1", "futures", "hex", - "http-body 0.4.6", - "hyper 0.14.28", - "log", - "pbjson 0.5.1", - "pbjson-types 0.5.1", - "prost 0.12.3", + "prost", "serde", "tokio", "tonic", - "tower", "tracing", - "webpki-roots 0.23.1", "xmtp_proto", "xmtp_v2", ] [[package]] name = "xmtp_cryptography" -version = "0.1.0" +version = "0.0.1" dependencies = [ "curve25519-dalek", - "ecdsa 0.15.1", + "ecdsa 0.16.9", "ethers", - "ethers-core", + "getrandom", "hex", - "k256 0.12.0", - "log", + "k256 0.13.3", "rand", "rand_chacha", "rustc-hex", @@ -5853,25 +5656,21 @@ dependencies = [ "sha2", "sha3", "thiserror", + "tracing", ] [[package]] name = "xmtp_id" -version = "0.1.0" +version = "0.0.1" dependencies = [ "async-trait", "chrono", - "ed25519", "ed25519-dalek", "ethers", "futures", "hex", - "log", - "openmls", - "openmls_basic_credential", - "openmls_rust_crypto", "openmls_traits", - "prost 0.12.3", + "prost", "rand", "regex", "rustc-hex", @@ -5887,75 +5686,69 @@ dependencies = [ [[package]] name = "xmtp_mls" -version = "0.1.0" +version = "0.0.1" dependencies = [ - "aes", "aes-gcm", - "async-trait", - "chrono", + "async-stream", + "bincode", "diesel", "diesel_migrations", "ed25519-dalek", - "ethers", - "ethers-core", "futures", "hex", "libsqlite3-sys", - "log", "openmls", "openmls_basic_credential", "openmls_rust_crypto", "openmls_traits", - "prost 0.12.3", + "parking_lot", + "prost", "rand", "reqwest 0.12.4", - "ring 0.17.8", "serde", "serde_json", "sha2", - "smart-default", "thiserror", - "tls_codec 0.4.0", + "tls_codec", "tokio", + "tokio-stream", "toml 0.8.8", "tracing", + "trait-variant", + "xmtp_api_grpc", "xmtp_cryptography", "xmtp_id", "xmtp_proto", - "xmtp_v2", ] [[package]] name = "xmtp_proto" -version = "0.1.0" +version = "0.0.1" dependencies = [ - "async-trait", "futures", - "futures-core", "openmls", - "openmls_basic_credential", - "pbjson 0.6.0", - "pbjson-types 0.6.0", - "prost 0.12.3", - "prost-types 0.12.3", + "pbjson", + "pbjson-types", + "prost", + "prost-types", "serde", "tonic", + "trait-variant", ] [[package]] name = "xmtp_user_preferences" -version = "0.1.0" +version = "0.0.1" dependencies = [ - "base64 0.21.7", - "once_cell", - "prost 0.12.3", + "base64 0.22.1", + "prost", "xmtp_proto", "xmtp_v2", ] [[package]] name = "xmtp_v2" -version = "0.1.0" +version = "0.0.1" dependencies = [ "aes-gcm", "ecdsa 0.15.1", @@ -5972,21 +5765,20 @@ dependencies = [ [[package]] name = "xmtpv3" -version = "0.1.0" +version = "0.0.1" dependencies = [ - "async-barrier", - "env_logger", "ethers", - "ethers-core", "futures", "log", + "parking_lot", + "rand", "tempfile", "thiserror", "thread-id", "tokio", "tokio-test", "uniffi", - "uniffi_macros", + "uuid 1.9.1", "xmtp_api_grpc", "xmtp_cryptography", "xmtp_id", diff --git a/bindings_ffi/Cargo.toml b/bindings_ffi/Cargo.toml index e0c392a3c..bedcdc1ce 100644 --- a/bindings_ffi/Cargo.toml +++ b/bindings_ffi/Cargo.toml @@ -1,29 +1,28 @@ [package] edition = "2021" name = "xmtpv3" -version = "0.1.0" +version = "0.0.1" [lib] crate-type = ["lib", "cdylib", "staticlib"] [dependencies] -env_logger = "0.11.3" futures = "0.3.28" log = { version = "0.4", features = ["std"] } +parking_lot = "0.12.3" thiserror = "1.0" thread-id = "4.2.1" tokio = { version = "1.28.1", features = ["macros"] } -uniffi = { version = "0.27.2", features = ["tokio", "cli"] } -uniffi_macros = "0.27.2" +uniffi = { version = "0.28.0", features = ["tokio", "cli"] } xmtp_api_grpc = { path = "../xmtp_api_grpc" } xmtp_cryptography = { path = "../xmtp_cryptography" } xmtp_id = { path = "../xmtp_id" } -xmtp_mls = { path = "../xmtp_mls", features = ["grpc", "native"] } -xmtp_proto = { path = "../xmtp_proto", features = ["proto_full", "grpc"] } +xmtp_mls = { path = "../xmtp_mls", features = ["native", "message-history"] } +xmtp_proto = { path = "../xmtp_proto", features = ["proto_full"] } xmtp_user_preferences = { path = "../xmtp_user_preferences" } xmtp_v2 = { path = "../xmtp_v2" } -# NOTE: A regression in openssl-sys exists where libatomic is dynamically linked +# NOTE: A regression in openssl-sys exists where libatomic is dynamically linked # for i686-linux-android targets. https://github.com/sfackler/rust-openssl/issues/2163 # # This is fixed in the openssl-sys fork at @@ -33,20 +32,21 @@ xmtp_v2 = { path = "../xmtp_v2" } openssl-sys = { git = "https://github.com/xmtp/rust-openssl.git", branch = "clone-v0.9.92" } [build-dependencies] -uniffi = { version = "0.27.2", features = ["build"] } +uniffi = { version = "0.28.0", features = ["build"] } [[bin]] name = "ffi-uniffi-bindgen" path = "src/bin.rs" [dev-dependencies] -async-barrier = "1.1" ethers = "2.0.13" -ethers-core = "2.0.13" +rand = "0.8.5" tempfile = "3.5.0" tokio = { version = "1.28.1", features = ["full"] } tokio-test = "0.4" -uniffi = { version = "0.27.2", features = ["bindgen-tests"] } +uniffi = { version = "0.28.0", features = ["bindgen-tests"] } +uuid = { version = "1.9", features = ["v4", "fast-rng"] } +xmtp_mls = { path = "../xmtp_mls", features = ["native", "test-utils"] } # NOTE: The release profile reduces bundle size from 230M to 41M - may have performance impliciations # https://stackoverflow.com/a/54842093 diff --git a/bindings_ffi/Makefile b/bindings_ffi/Makefile index 848c9df24..16c354562 100644 --- a/bindings_ffi/Makefile +++ b/bindings_ffi/Makefile @@ -3,10 +3,10 @@ PROJECT_NAME = xmtpv3 # Simulator config ARCHS_IOS = x86_64-apple-ios aarch64-apple-ios-sim ARCHS_MAC = x86_64-apple-darwin aarch64-apple-darwin -# Not used -# ARCHS_MACCATALYST = x86_64-apple-ios-macabi aarch64-apple-ios-macabi LIB=libxmtpv3.a JAR_DIR=$(shell pwd)/tests/jar +SQLCIPHER_DIR=$(shell pwd)/sqlcipher +SQLCIPHER_LIB=$(SQLCIPHER_DIR)/.libs/libsqlcipher.a GIT_BRANCH=$(shell git rev-parse --abbrev-ref HEAD) GIT_COMMIT_HASH=$(shell git log -1 --pretty=format:"%h") GIT_COMMIT_DATE=$(shell TZ=UTC git log -1 --date=iso-local --pretty=format:"%ad") @@ -28,22 +28,30 @@ download-toolchains: rustup target add $(ARCHS_MAC) rustup target add aarch64-apple-ios +download-sqlcipher: + git clone https://github.com/sqlcipher/sqlcipher.git $(SQLCIPHER_DIR) + +build-sqlcipher: + cd $(SQLCIPHER_DIR) && \ + ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" && \ + make + all: framework libxmtp-version: echo "Version: $(GIT_COMMIT_HASH)\nBranch: $(GIT_BRANCH)\nDate: $(GIT_COMMIT_DATE)" > libxmtp-version.txt -$(ARCHS_IOS): %: +$(ARCHS_IOS): %: build-sqlcipher cross build --target $@ --target-dir ./target --release --no-default-features mkdir -p build/$@ mv target/$@/release/$(LIB) build/$@/$(LIB) -$(ARCHS_MAC): %: +$(ARCHS_MAC): %: build-sqlcipher cross build --target $@ --target-dir ./target --release --no-default-features mkdir -p build/$@ mv target/$@/release/$(LIB) build/$@/$(LIB) -aarch64-apple-ios: +aarch64-apple-ios: build-sqlcipher cross build --target $@ --target-dir ./target --release mkdir -p build/$@ mv target/$@/release/$(LIB) build/$@/$(LIB) @@ -85,4 +93,4 @@ swift: libxmtp-version swiftlocal: libxmtpv3.a swift framework -.PHONY: $(ARCHS_IOS) $(ARCHS_MAC) framework all aarch64-apple-ios install-jar echo-jar download-toolchains swift lipo +.PHONY: $(ARCHS_IOS) $(ARCHS_MAC) framework all aarch64-apple-ios install-jar echo-jar download-toolchains swift lipo download-sqlcipher build-sqlcipher diff --git a/bindings_ffi/gen_kotlin.sh b/bindings_ffi/gen_kotlin.sh index a28b92a94..2904853b0 100755 --- a/bindings_ffi/gen_kotlin.sh +++ b/bindings_ffi/gen_kotlin.sh @@ -11,3 +11,7 @@ bindings_ffi/target/release/ffi-uniffi-bindgen generate \ popd > /dev/null make libxmtp-version cp libxmtp-version.txt src/uniffi/$PROJECT_NAME/ + +# Assumes libxmtp is in a peer directory of xmtp-android +cp src/uniffi/xmtpv3/xmtpv3.kt ../../xmtp-android/library/src/main/java/xmtpv3.kt +cp src/uniffi/xmtpv3/libxmtp-version.txt ../../xmtp-android/library/src/main/java/libxmtp-version.txt diff --git a/bindings_ffi/jniLibs/arm64-v8a/libuniffi_xmtpv3.so b/bindings_ffi/jniLibs/arm64-v8a/libuniffi_xmtpv3.so deleted file mode 100755 index f476d01d9..000000000 Binary files a/bindings_ffi/jniLibs/arm64-v8a/libuniffi_xmtpv3.so and /dev/null differ diff --git a/bindings_ffi/jniLibs/armeabi-v7a/libuniffi_xmtpv3.so b/bindings_ffi/jniLibs/armeabi-v7a/libuniffi_xmtpv3.so deleted file mode 100755 index b67ce8a64..000000000 Binary files a/bindings_ffi/jniLibs/armeabi-v7a/libuniffi_xmtpv3.so and /dev/null differ diff --git a/bindings_ffi/jniLibs/x86/libuniffi_xmtpv3.so b/bindings_ffi/jniLibs/x86/libuniffi_xmtpv3.so deleted file mode 100755 index a79942095..000000000 Binary files a/bindings_ffi/jniLibs/x86/libuniffi_xmtpv3.so and /dev/null differ diff --git a/bindings_ffi/jniLibs/x86_64/libuniffi_xmtpv3.so b/bindings_ffi/jniLibs/x86_64/libuniffi_xmtpv3.so deleted file mode 100755 index df1d27578..000000000 Binary files a/bindings_ffi/jniLibs/x86_64/libuniffi_xmtpv3.so and /dev/null differ diff --git a/bindings_ffi/src/inbox_owner.rs b/bindings_ffi/src/inbox_owner.rs index c8c2ee298..075772950 100644 --- a/bindings_ffi/src/inbox_owner.rs +++ b/bindings_ffi/src/inbox_owner.rs @@ -1,3 +1,5 @@ +use xmtp_cryptography::signature::{RecoverableSignature, SignatureError}; + // TODO proper error handling #[derive(Debug, thiserror::Error)] pub enum SigningError { @@ -32,17 +34,11 @@ impl xmtp_mls::InboxOwner for RustInboxOwner { self.ffi_inbox_owner.get_address().to_lowercase() } - fn sign( - &self, - text: &str, - ) -> Result< - xmtp_cryptography::signature::RecoverableSignature, - xmtp_cryptography::signature::SignatureError, - > { + fn sign(&self, text: &str) -> Result { let bytes = self .ffi_inbox_owner .sign(text.to_string()) - .map_err(|_flat_err| xmtp_cryptography::signature::SignatureError::Unknown)?; - Ok(xmtp_cryptography::signature::RecoverableSignature::Eip191Signature(bytes)) + .map_err(|_flat_err| SignatureError::Unknown)?; + Ok(RecoverableSignature::Eip191Signature(bytes)) } } diff --git a/bindings_ffi/src/lib.rs b/bindings_ffi/src/lib.rs index 5df8d2918..2faef1ae2 100755 --- a/bindings_ffi/src/lib.rs +++ b/bindings_ffi/src/lib.rs @@ -1,4 +1,5 @@ #![recursion_limit = "256"] +#![warn(clippy::unwrap_used)] pub mod inbox_owner; pub mod logger; pub mod mls; diff --git a/bindings_ffi/src/logger.rs b/bindings_ffi/src/logger.rs index 7f5c45f9d..5e9d0c623 100644 --- a/bindings_ffi/src/logger.rs +++ b/bindings_ffi/src/logger.rs @@ -6,7 +6,7 @@ pub trait FfiLogger: Send + Sync { } struct RustLogger { - logger: std::sync::Mutex>, + logger: parking_lot::Mutex>, } impl log::Log for RustLogger { @@ -17,10 +17,10 @@ impl log::Log for RustLogger { fn log(&self, record: &Record) { if self.enabled(record.metadata()) { // TODO handle errors - self.logger.lock().expect("Logger mutex is poisoned!").log( + self.logger.lock().log( record.level() as u32, record.level().to_string(), - format!("[libxmtp] {}", record.args()), + format!("[libxmtp][t:{}] {}", thread_id::get(), record.args()), ); } } @@ -33,7 +33,7 @@ pub fn init_logger(logger: Box) { // TODO handle errors LOGGER_INIT.call_once(|| { let logger = RustLogger { - logger: std::sync::Mutex::new(logger), + logger: parking_lot::Mutex::new(logger), }; log::set_boxed_logger(Box::new(logger)) .map(|()| log::set_max_level(LevelFilter::Info)) diff --git a/bindings_ffi/src/mls.rs b/bindings_ffi/src/mls.rs index c4700f4ed..b081cf455 100644 --- a/bindings_ffi/src/mls.rs +++ b/bindings_ffi/src/mls.rs @@ -4,24 +4,39 @@ use crate::logger::FfiLogger; use crate::GenericError; use std::collections::HashMap; use std::convert::TryInto; -use std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, Mutex, -}; -use tokio::sync::oneshot::Sender; +use std::sync::Arc; +use tokio::{sync::Mutex, task::AbortHandle}; use xmtp_api_grpc::grpc_api_helper::Client as TonicApiClient; +use xmtp_id::associations::unverified::UnverifiedSignature; +use xmtp_id::associations::AccountId; +use xmtp_id::associations::AssociationState; +use xmtp_id::associations::MemberIdentifier; +use xmtp_id::scw_verifier::RpcSmartContractWalletVerifier; +use xmtp_id::scw_verifier::SmartContractSignatureVerifier; use xmtp_id::{ - associations::{ - builder::SignatureRequest, generate_inbox_id as xmtp_id_generate_inbox_id, - RecoverableEcdsaSignature, SmartContractWalletSignature, - }, + associations::{builder::SignatureRequest, generate_inbox_id as xmtp_id_generate_inbox_id}, InboxId, }; +use xmtp_mls::groups::group_mutable_metadata::MetadataField; +use xmtp_mls::groups::group_permissions::BasePolicies; +use xmtp_mls::groups::group_permissions::GroupMutablePermissionsError; +use xmtp_mls::groups::group_permissions::MembershipPolicies; +use xmtp_mls::groups::group_permissions::MetadataBasePolicies; +use xmtp_mls::groups::group_permissions::MetadataPolicies; +use xmtp_mls::groups::group_permissions::PermissionsBasePolicies; +use xmtp_mls::groups::group_permissions::PermissionsPolicies; +use xmtp_mls::groups::group_permissions::PolicySet; +use xmtp_mls::groups::intents::PermissionPolicyOption; +use xmtp_mls::groups::intents::PermissionUpdateType; use xmtp_mls::groups::GroupMetadataOptions; +use xmtp_mls::storage::consent_record::ConsentState; +use xmtp_mls::storage::consent_record::ConsentType; +use xmtp_mls::storage::consent_record::StoredConsentRecord; use xmtp_mls::{ api::ApiClientWrapper, builder::ClientBuilder, client::Client as MlsClient, + client::ClientError, groups::{ group_metadata::{ConversationType, GroupMetadata}, group_permissions::GroupMutablePermissions, @@ -34,6 +49,7 @@ use xmtp_mls::{ group_message::{DeliveryStatus, GroupMessageKind, StoredGroupMessage}, EncryptedMessageStore, EncryptionKey, StorageOption, }, + subscriptions::StreamHandle, }; pub type RustXmtpClient = MlsClient; @@ -58,7 +74,6 @@ pub type RustXmtpClient = MlsClient; /// xmtp.create_client(account_address, nonce, inbox_id, Option) /// ``` #[allow(clippy::too_many_arguments)] -#[allow(unused)] #[uniffi::export(async_runtime = "tokio")] pub async fn create_client( logger: Box, @@ -70,6 +85,7 @@ pub async fn create_client( account_address: String, nonce: u64, legacy_signed_private_key_proto: Option>, + history_sync_url: Option, ) -> Result, GenericError> { init_logger(logger); log::info!( @@ -80,9 +96,10 @@ pub async fn create_client( let api_client = TonicApiClient::create(host.clone(), is_secure).await?; log::info!( - "Creating message store with path: {:?} and encryption key: {}", + "Creating message store with path: {:?} and encryption key: {} of length {:?}", db, - encryption_key.is_some() + encryption_key.is_some(), + encryption_key.as_ref().map(|k| k.len()) ); let storage_option = match db { @@ -106,11 +123,24 @@ pub async fn create_client( nonce, legacy_signed_private_key_proto, ); - let xmtp_client: RustXmtpClient = ClientBuilder::new(identity_strategy) - .api_client(api_client) - .store(store) - .build() - .await?; + + let xmtp_client: RustXmtpClient = match history_sync_url { + Some(url) => { + ClientBuilder::new(identity_strategy) + .api_client(api_client) + .store(store) + .history_sync_url(&url) + .build() + .await? + } + None => { + ClientBuilder::new(identity_strategy) + .api_client(api_client) + .store(store) + .build() + .await? + } + }; log::info!( "Created XMTP client for inbox_id: {}", @@ -151,8 +181,12 @@ pub fn generate_inbox_id(account_address: String, nonce: u64) -> String { #[derive(uniffi::Object)] pub struct FfiSignatureRequest { - // Using `tokio::sync::Mutex`bc rust MutexGuard cannot be sent between threads. - inner: Arc>, + inner: Arc>, +} + +// TODO:nm store the verifier on the request from the client +fn signature_verifier() -> impl SmartContractSignatureVerifier { + RpcSmartContractWalletVerifier::new("http://www.fake.com".to_string()) } #[uniffi::export(async_runtime = "tokio")] @@ -160,12 +194,11 @@ impl FfiSignatureRequest { // Signature that's signed by EOA wallet pub async fn add_ecdsa_signature(&self, signature_bytes: Vec) -> Result<(), GenericError> { let mut inner = self.inner.lock().await; - let signature_text = inner.signature_text(); inner - .add_signature(Box::new(RecoverableEcdsaSignature::new( - signature_text, - signature_bytes, - ))) + .add_signature( + UnverifiedSignature::new_recoverable_ecdsa(signature_bytes), + &signature_verifier(), + ) .await?; Ok(()) @@ -176,17 +209,20 @@ impl FfiSignatureRequest { &self, signature_bytes: Vec, address: String, - chain_rpc_url: String, + chain_id: u64, + block_number: u64, ) -> Result<(), GenericError> { let mut inner = self.inner.lock().await; - let signature = SmartContractWalletSignature::new_with_rpc( - inner.signature_text(), + let account_id = AccountId::new_evm(chain_id, address); + + let signature = UnverifiedSignature::new_smart_contract_wallet( signature_bytes, - address, - chain_rpc_url, - ) - .await?; - inner.add_signature(Box::new(signature)).await?; + account_id, + block_number, + ); + inner + .add_signature(signature, &signature_verifier()) + .await?; Ok(()) } @@ -228,6 +264,20 @@ impl FfiXmtpClient { }) } + pub fn group(&self, group_id: Vec) -> Result { + let convo = self.inner_client.group(group_id)?; + Ok(FfiGroup { + inner_client: self.inner_client.clone(), + group_id: convo.group_id, + created_at_ns: convo.created_at_ns, + }) + } + + pub fn message(&self, message_id: Vec) -> Result { + let message = self.inner_client.message(message_id)?; + Ok(message.into()) + } + pub async fn can_message( &self, account_addresses: Vec, @@ -250,6 +300,58 @@ impl FfiXmtpClient { pub async fn db_reconnect(&self) -> Result<(), GenericError> { Ok(self.inner_client.reconnect_db()?) } + + pub async fn find_inbox_id(&self, address: String) -> Result, GenericError> { + let inner = self.inner_client.as_ref(); + + let result = inner.find_inbox_id_from_address(address).await?; + Ok(result) + } + + /** + * Get the client's inbox state. + * + * If `refresh_from_network` is true, the client will go to the network first to refresh the state. + * Otherwise, the state will be read from the local database. + */ + pub async fn inbox_state( + &self, + refresh_from_network: bool, + ) -> Result { + let state = self.inner_client.inbox_state(refresh_from_network).await?; + Ok(state.into()) + } + + pub async fn get_latest_inbox_state( + &self, + inbox_id: String, + ) -> Result { + let state = self + .inner_client + .get_latest_association_state(&self.inner_client.store().conn()?, &inbox_id) + .await?; + Ok(state.into()) + } + + pub async fn set_consent_states(&self, records: Vec) -> Result<(), GenericError> { + let inner = self.inner_client.as_ref(); + let stored_records: Vec = + records.into_iter().map(StoredConsentRecord::from).collect(); + + inner.set_consent_states(stored_records).await?; + Ok(()) + } + + pub async fn get_consent_state( + &self, + entity_type: FfiConsentEntityType, + entity: String, + ) -> Result { + let inner = self.inner_client.as_ref(); + let result = inner.get_consent_state(entity_type.into(), entity).await?; + + Ok(result.into()) + } } #[uniffi::export(async_runtime = "tokio")] @@ -260,7 +362,7 @@ impl FfiXmtpClient { .signature_request() .map(|request| { Arc::new(FfiSignatureRequest { - inner: Arc::new(tokio::sync::Mutex::new(request)), + inner: Arc::new(Mutex::new(request)), }) }) } @@ -276,6 +378,119 @@ impl FfiXmtpClient { Ok(()) } + + pub async fn request_history_sync(&self) -> Result<(), GenericError> { + self.inner_client + .send_history_request() + .await + .map_err(GenericError::from_error)?; + Ok(()) + } + + /// Adds an identity - really a wallet address - to the existing client + pub async fn add_wallet( + &self, + existing_wallet_address: &str, + new_wallet_address: &str, + ) -> Result, GenericError> { + let signature_request = self + .inner_client + .associate_wallet(existing_wallet_address.into(), new_wallet_address.into())?; + + let request = Arc::new(FfiSignatureRequest { + inner: Arc::new(tokio::sync::Mutex::new(signature_request)), + }); + + Ok(request) + } + + pub async fn apply_signature_request( + &self, + signature_request: Arc, + ) -> Result<(), GenericError> { + let signature_request = signature_request.inner.lock().await; + self.inner_client + .apply_signature_request(signature_request.clone()) + .await?; + + Ok(()) + } + + /// Revokes or removes an identity - really a wallet address - from the existing client + pub async fn revoke_wallet( + &self, + wallet_address: &str, + ) -> Result, GenericError> { + let signature_request = self + .inner_client + .revoke_wallets(vec![wallet_address.into()]) + .await?; + + let request = Arc::new(FfiSignatureRequest { + inner: Arc::new(tokio::sync::Mutex::new(signature_request)), + }); + + Ok(request) + } + + /** + * Revokes all installations except the one the client is currently using + */ + pub async fn revoke_all_other_installations( + &self, + ) -> Result, GenericError> { + let installation_id = self.inner_client.installation_public_key(); + let inbox_state = self.inner_client.inbox_state(true).await?; + let other_installation_ids = inbox_state + .installation_ids() + .into_iter() + .filter(|id| id != &installation_id) + .collect(); + + let signature_request = self + .inner_client + .revoke_installations(other_installation_ids) + .await?; + + Ok(Arc::new(FfiSignatureRequest { + inner: Arc::new(tokio::sync::Mutex::new(signature_request)), + })) + } +} + +#[derive(uniffi::Record)] +pub struct FfiInboxState { + pub inbox_id: String, + pub recovery_address: String, + pub installations: Vec, + pub account_addresses: Vec, +} + +#[derive(uniffi::Record)] +pub struct FfiInstallation { + pub id: Vec, + pub client_timestamp_ns: Option, +} + +impl From for FfiInboxState { + fn from(state: AssociationState) -> Self { + Self { + inbox_id: state.inbox_id().to_string(), + recovery_address: state.recovery_address().to_string(), + installations: state + .members() + .into_iter() + .filter_map(|m| match m.identifier { + MemberIdentifier::Address(_) => None, + MemberIdentifier::Installation(inst) => Some(FfiInstallation { + id: inst, + client_timestamp_ns: m.client_timestamp_ns, + }), + }) + .collect(), + account_addresses: state.account_addresses(), + } + } } #[derive(uniffi::Record, Default)] @@ -290,17 +505,216 @@ pub struct FfiConversations { inner_client: Arc, } -#[derive(uniffi::Enum, Debug)] -pub enum GroupPermissions { +#[derive(uniffi::Enum, Clone, Debug)] +pub enum FfiGroupPermissionsOptions { AllMembers, AdminOnly, + CustomPolicy, +} + +#[derive(uniffi::Enum, Debug)] +pub enum FfiPermissionUpdateType { + AddMember, + RemoveMember, + AddAdmin, + RemoveAdmin, + UpdateMetadata, +} + +impl From<&FfiPermissionUpdateType> for PermissionUpdateType { + fn from(update_type: &FfiPermissionUpdateType) -> Self { + match update_type { + FfiPermissionUpdateType::AddMember => PermissionUpdateType::AddMember, + FfiPermissionUpdateType::RemoveMember => PermissionUpdateType::RemoveMember, + FfiPermissionUpdateType::AddAdmin => PermissionUpdateType::AddAdmin, + FfiPermissionUpdateType::RemoveAdmin => PermissionUpdateType::RemoveAdmin, + FfiPermissionUpdateType::UpdateMetadata => PermissionUpdateType::UpdateMetadata, + } + } +} + +#[derive(uniffi::Enum, Clone, Debug, PartialEq, Eq)] +pub enum FfiPermissionPolicy { + Allow, + Deny, + Admin, + SuperAdmin, + DoesNotExist, + Other, +} + +impl TryInto for FfiPermissionPolicy { + type Error = GroupMutablePermissionsError; + + fn try_into(self) -> Result { + match self { + FfiPermissionPolicy::Allow => Ok(PermissionPolicyOption::Allow), + FfiPermissionPolicy::Deny => Ok(PermissionPolicyOption::Deny), + FfiPermissionPolicy::Admin => Ok(PermissionPolicyOption::AdminOnly), + FfiPermissionPolicy::SuperAdmin => Ok(PermissionPolicyOption::SuperAdminOnly), + _ => Err(GroupMutablePermissionsError::InvalidPermissionPolicyOption), + } + } +} + +impl TryInto for FfiPermissionPolicy { + type Error = GroupMutablePermissionsError; + + fn try_into(self) -> Result { + match self { + FfiPermissionPolicy::Allow => Ok(MembershipPolicies::allow()), + FfiPermissionPolicy::Deny => Ok(MembershipPolicies::deny()), + FfiPermissionPolicy::Admin => Ok(MembershipPolicies::allow_if_actor_admin()), + FfiPermissionPolicy::SuperAdmin => Ok(MembershipPolicies::allow_if_actor_super_admin()), + _ => Err(GroupMutablePermissionsError::InvalidPermissionPolicyOption), + } + } +} + +impl TryInto for FfiPermissionPolicy { + type Error = GroupMutablePermissionsError; + + fn try_into(self) -> Result { + match self { + FfiPermissionPolicy::Allow => Ok(MetadataPolicies::allow()), + FfiPermissionPolicy::Deny => Ok(MetadataPolicies::deny()), + FfiPermissionPolicy::Admin => Ok(MetadataPolicies::allow_if_actor_admin()), + FfiPermissionPolicy::SuperAdmin => Ok(MetadataPolicies::allow_if_actor_super_admin()), + _ => Err(GroupMutablePermissionsError::InvalidPermissionPolicyOption), + } + } +} + +impl TryInto for FfiPermissionPolicy { + type Error = GroupMutablePermissionsError; + + fn try_into(self) -> Result { + match self { + FfiPermissionPolicy::Deny => Ok(PermissionsPolicies::deny()), + FfiPermissionPolicy::Admin => Ok(PermissionsPolicies::allow_if_actor_admin()), + FfiPermissionPolicy::SuperAdmin => { + Ok(PermissionsPolicies::allow_if_actor_super_admin()) + } + _ => Err(GroupMutablePermissionsError::InvalidPermissionPolicyOption), + } + } +} + +impl From<&MembershipPolicies> for FfiPermissionPolicy { + fn from(policies: &MembershipPolicies) -> Self { + if let MembershipPolicies::Standard(base_policy) = policies { + match base_policy { + BasePolicies::Allow => FfiPermissionPolicy::Allow, + BasePolicies::Deny => FfiPermissionPolicy::Deny, + BasePolicies::AllowSameMember => FfiPermissionPolicy::Other, + BasePolicies::AllowIfAdminOrSuperAdmin => FfiPermissionPolicy::Admin, + BasePolicies::AllowIfSuperAdmin => FfiPermissionPolicy::SuperAdmin, + } + } else { + FfiPermissionPolicy::Other + } + } +} + +impl From<&MetadataPolicies> for FfiPermissionPolicy { + fn from(policies: &MetadataPolicies) -> Self { + if let MetadataPolicies::Standard(base_policy) = policies { + match base_policy { + MetadataBasePolicies::Allow => FfiPermissionPolicy::Allow, + MetadataBasePolicies::Deny => FfiPermissionPolicy::Deny, + MetadataBasePolicies::AllowIfActorAdminOrSuperAdmin => FfiPermissionPolicy::Admin, + MetadataBasePolicies::AllowIfActorSuperAdmin => FfiPermissionPolicy::SuperAdmin, + } + } else { + FfiPermissionPolicy::Other + } + } +} + +impl From<&PermissionsPolicies> for FfiPermissionPolicy { + fn from(policies: &PermissionsPolicies) -> Self { + if let PermissionsPolicies::Standard(base_policy) = policies { + match base_policy { + PermissionsBasePolicies::Deny => FfiPermissionPolicy::Deny, + PermissionsBasePolicies::AllowIfActorAdminOrSuperAdmin => { + FfiPermissionPolicy::Admin + } + PermissionsBasePolicies::AllowIfActorSuperAdmin => FfiPermissionPolicy::SuperAdmin, + } + } else { + FfiPermissionPolicy::Other + } + } } -impl From for GroupPermissions { +#[derive(uniffi::Record, Clone, Debug, PartialEq, Eq)] +pub struct FfiPermissionPolicySet { + pub add_member_policy: FfiPermissionPolicy, + pub remove_member_policy: FfiPermissionPolicy, + pub add_admin_policy: FfiPermissionPolicy, + pub remove_admin_policy: FfiPermissionPolicy, + pub update_group_name_policy: FfiPermissionPolicy, + pub update_group_description_policy: FfiPermissionPolicy, + pub update_group_image_url_square_policy: FfiPermissionPolicy, + pub update_group_pinned_frame_url_policy: FfiPermissionPolicy, +} + +impl From for FfiGroupPermissionsOptions { fn from(policy: PreconfiguredPolicies) -> Self { match policy { - PreconfiguredPolicies::AllMembers => GroupPermissions::AllMembers, - PreconfiguredPolicies::AdminsOnly => GroupPermissions::AdminOnly, + PreconfiguredPolicies::AllMembers => FfiGroupPermissionsOptions::AllMembers, + PreconfiguredPolicies::AdminsOnly => FfiGroupPermissionsOptions::AdminOnly, + } + } +} + +impl TryFrom for PolicySet { + type Error = GroupMutablePermissionsError; + fn try_from(policy_set: FfiPermissionPolicySet) -> Result { + let mut metadata_permissions_map: HashMap = HashMap::new(); + metadata_permissions_map.insert( + MetadataField::GroupName.to_string(), + policy_set.update_group_name_policy.try_into()?, + ); + metadata_permissions_map.insert( + MetadataField::Description.to_string(), + policy_set.update_group_description_policy.try_into()?, + ); + metadata_permissions_map.insert( + MetadataField::GroupImageUrlSquare.to_string(), + policy_set.update_group_image_url_square_policy.try_into()?, + ); + metadata_permissions_map.insert( + MetadataField::GroupPinnedFrameUrl.to_string(), + policy_set.update_group_pinned_frame_url_policy.try_into()?, + ); + + Ok(PolicySet { + add_member_policy: policy_set.add_member_policy.try_into()?, + remove_member_policy: policy_set.remove_member_policy.try_into()?, + add_admin_policy: policy_set.add_admin_policy.try_into()?, + remove_admin_policy: policy_set.remove_admin_policy.try_into()?, + update_metadata_policy: metadata_permissions_map, + update_permissions_policy: PermissionsPolicies::allow_if_actor_super_admin(), + }) + } +} + +#[derive(uniffi::Enum, Debug)] +pub enum FfiMetadataField { + GroupName, + Description, + ImageUrlSquare, + PinnedFrameUrl, +} + +impl From<&FfiMetadataField> for MetadataField { + fn from(field: &FfiMetadataField) -> Self { + match field { + FfiMetadataField::GroupName => MetadataField::GroupName, + FfiMetadataField::Description => MetadataField::Description, + FfiMetadataField::ImageUrlSquare => MetadataField::GroupImageUrlSquare, + FfiMetadataField::PinnedFrameUrl => MetadataField::GroupPinnedFrameUrl, } } } @@ -317,24 +731,45 @@ impl FfiConversations { account_addresses.join(", ") ); + if let Some(FfiGroupPermissionsOptions::CustomPolicy) = opts.permissions { + if opts.custom_permission_policy_set.is_none() { + return Err(GenericError::Generic { + err: "CustomPolicy must include policy set".to_string(), + }); + } + } else if opts.custom_permission_policy_set.is_some() { + return Err(GenericError::Generic { + err: "Only CustomPolicy may specify a policy set".to_string(), + }); + } + + let metadata_options = opts.clone().into_group_metadata_options(); + let group_permissions = match opts.permissions { - Some(GroupPermissions::AllMembers) => { - Some(xmtp_mls::groups::PreconfiguredPolicies::AllMembers) + Some(FfiGroupPermissionsOptions::AllMembers) => { + Some(xmtp_mls::groups::PreconfiguredPolicies::AllMembers.to_policy_set()) } - Some(GroupPermissions::AdminOnly) => { - Some(xmtp_mls::groups::PreconfiguredPolicies::AdminsOnly) + Some(FfiGroupPermissionsOptions::AdminOnly) => { + Some(xmtp_mls::groups::PreconfiguredPolicies::AdminsOnly.to_policy_set()) + } + Some(FfiGroupPermissionsOptions::CustomPolicy) => { + if let Some(policy_set) = opts.custom_permission_policy_set { + Some(policy_set.try_into()?) + } else { + None + } } _ => None, }; - let convo = self - .inner_client - .create_group(group_permissions, opts.into_group_metadata_options())?; - if !account_addresses.is_empty() { - convo - .add_members(&self.inner_client, account_addresses) - .await?; - } + let convo = if account_addresses.is_empty() { + self.inner_client + .create_group(group_permissions, metadata_options)? + } else { + self.inner_client + .create_group_with_members(account_addresses, group_permissions, metadata_options) + .await? + }; let out = Arc::new(FfiGroup { inner_client: self.inner_client.clone(), @@ -367,6 +802,22 @@ impl FfiConversations { Ok(()) } + pub async fn sync_all_groups(&self) -> Result { + let inner = self.inner_client.as_ref(); + let groups = inner.find_groups(None, None, None, None)?; + + let num_groups_synced: usize = inner.sync_all_groups(groups).await?; + // Uniffi does not work with usize, so we need to convert to u32 + let num_groups_synced: u32 = + num_groups_synced + .try_into() + .map_err(|_| GenericError::Generic { + err: "Failed to convert the number of synced groups from usize to u32" + .to_string(), + })?; + Ok(num_groups_synced) + } + pub async fn list( &self, opts: FfiListConversationsOptions, @@ -392,43 +843,30 @@ impl FfiConversations { Ok(convo_list) } - pub async fn stream( - &self, - callback: Box, - ) -> Result, GenericError> { + pub async fn stream(&self, callback: Box) -> FfiStreamCloser { let client = self.inner_client.clone(); - let stream_closer = RustXmtpClient::stream_conversations_with_callback( - client.clone(), - move |convo| { + let handle = + RustXmtpClient::stream_conversations_with_callback(client.clone(), move |convo| { callback.on_conversation(Arc::new(FfiGroup { inner_client: client.clone(), group_id: convo.group_id, created_at_ns: convo.created_at_ns, })) - }, - || {}, // on_close_callback - )?; + }); - Ok(Arc::new(FfiStreamCloser { - close_fn: stream_closer.close_fn, - is_closed_atomic: stream_closer.is_closed_atomic, - })) + FfiStreamCloser::new(handle) } pub async fn stream_all_messages( &self, message_callback: Box, - ) -> Result, GenericError> { - let stream_closer = RustXmtpClient::stream_all_messages_with_callback( + ) -> FfiStreamCloser { + let handle = RustXmtpClient::stream_all_messages_with_callback( self.inner_client.clone(), move |message| message_callback.on_message(message.into()), - ) - .await?; + ); - Ok(Arc::new(FfiStreamCloser { - close_fn: stream_closer.close_fn, - is_closed_atomic: stream_closer.is_closed_atomic, - })) + FfiStreamCloser::new(handle) } } @@ -445,6 +883,7 @@ pub struct FfiGroupMember { pub account_addresses: Vec, pub installation_ids: Vec>, pub permission_level: FfiPermissionLevel, + pub consent_state: FfiConsentState, } #[derive(uniffi::Enum)] @@ -454,6 +893,50 @@ pub enum FfiPermissionLevel { SuperAdmin, } +#[derive(uniffi::Enum, PartialEq, Debug)] +pub enum FfiConsentState { + Unknown, + Allowed, + Denied, +} + +impl From for FfiConsentState { + fn from(state: ConsentState) -> Self { + match state { + ConsentState::Unknown => FfiConsentState::Unknown, + ConsentState::Allowed => FfiConsentState::Allowed, + ConsentState::Denied => FfiConsentState::Denied, + } + } +} + +impl From for ConsentState { + fn from(state: FfiConsentState) -> Self { + match state { + FfiConsentState::Unknown => ConsentState::Unknown, + FfiConsentState::Allowed => ConsentState::Allowed, + FfiConsentState::Denied => ConsentState::Denied, + } + } +} + +#[derive(uniffi::Enum)] +pub enum FfiConsentEntityType { + GroupId, + InboxId, + Address, +} + +impl From for ConsentType { + fn from(entity_type: FfiConsentEntityType) -> Self { + match entity_type { + FfiConsentEntityType::GroupId => ConsentType::GroupId, + FfiConsentEntityType::InboxId => ConsentType::InboxId, + FfiConsentEntityType::Address => ConsentType::Address, + } + } +} + #[derive(uniffi::Record, Clone, Default)] pub struct FfiListMessagesOptions { pub sent_before_ns: Option, @@ -462,11 +945,14 @@ pub struct FfiListMessagesOptions { pub delivery_status: Option, } -#[derive(uniffi::Record, Default)] +#[derive(uniffi::Record, Clone, Default)] pub struct FfiCreateGroupOptions { - pub permissions: Option, + pub permissions: Option, pub group_name: Option, pub group_image_url_square: Option, + pub group_description: Option, + pub group_pinned_frame_url: Option, + pub custom_permission_policy_set: Option, } impl FfiCreateGroupOptions { @@ -474,6 +960,8 @@ impl FfiCreateGroupOptions { GroupMetadataOptions { name: self.group_name, image_url_square: self.group_image_url_square, + description: self.group_description, + pinned_frame_url: self.group_pinned_frame_url, } } } @@ -493,6 +981,30 @@ impl FfiGroup { Ok(message_id) } + /// send a message without immediately publishing to the delivery service. + pub fn send_optimistic(&self, content_bytes: Vec) -> Result, GenericError> { + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + + let id = group.send_message_optimistic(content_bytes.as_slice())?; + + Ok(id) + } + + /// Publish all unpublished messages + pub async fn publish_messages(&self) -> Result<(), GenericError> { + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + group.publish_messages(&self.inner_client).await?; + Ok(()) + } + pub async fn sync(&self) -> Result<(), GenericError> { let group = MlsGroup::new( self.inner_client.context().clone(), @@ -542,14 +1054,14 @@ impl FfiGroup { self.created_at_ns, ); let message = group - .process_streamed_group_message(envelope_bytes, self.inner_client.clone()) + .process_streamed_group_message(envelope_bytes, &self.inner_client) .await?; let ffi_message = message.into(); Ok(ffi_message) } - pub fn list_members(&self) -> Result, GenericError> { + pub async fn list_members(&self) -> Result, GenericError> { let group = MlsGroup::new( self.inner_client.context().clone(), self.group_id.clone(), @@ -557,7 +1069,8 @@ impl FfiGroup { ); let members: Vec = group - .members()? + .members(&self.inner_client) + .await? .into_iter() .map(|member| FfiGroupMember { inbox_id: member.inbox_id, @@ -568,6 +1081,7 @@ impl FfiGroup { PermissionLevel::Admin => FfiPermissionLevel::Admin, PermissionLevel::SuperAdmin => FfiPermissionLevel::SuperAdmin, }, + consent_state: member.consent_state.into(), }) .collect(); @@ -661,7 +1175,8 @@ impl FfiGroup { self.created_at_ns, ); - let group_name = group.group_name()?; + let provider = group.mls_provider()?; + let group_name = group.group_name(&provider)?; Ok(group_name) } @@ -690,80 +1205,138 @@ impl FfiGroup { self.created_at_ns, ); - let group_image_url_square = group.group_image_url_square()?; + let group_image_url_square = group.group_image_url_square(group.mls_provider()?)?; Ok(group_image_url_square) } - pub fn admin_list(&self) -> Result, GenericError> { + pub async fn update_group_description( + &self, + group_description: String, + ) -> Result<(), GenericError> { let group = MlsGroup::new( self.inner_client.context().clone(), self.group_id.clone(), self.created_at_ns, ); - let admin_list = group.admin_list()?; + group + .update_group_description(&self.inner_client, group_description) + .await?; - Ok(admin_list) + Ok(()) } - pub fn super_admin_list(&self) -> Result, GenericError> { + pub fn group_description(&self) -> Result { let group = MlsGroup::new( self.inner_client.context().clone(), self.group_id.clone(), self.created_at_ns, ); - let super_admin_list = group.super_admin_list()?; - - Ok(super_admin_list) - } - - pub fn is_admin(&self, inbox_id: &String) -> Result { - let admin_list = self.admin_list()?; - Ok(admin_list.contains(inbox_id)) - } + let group_description = group.group_description(group.mls_provider()?)?; - pub fn is_super_admin(&self, inbox_id: &String) -> Result { - let super_admin_list = self.super_admin_list()?; - Ok(super_admin_list.contains(inbox_id)) + Ok(group_description) } - pub async fn add_admin(&self, inbox_id: String) -> Result<(), GenericError> { + pub async fn update_group_pinned_frame_url( + &self, + pinned_frame_url: String, + ) -> Result<(), GenericError> { let group = MlsGroup::new( self.inner_client.context().clone(), self.group_id.clone(), self.created_at_ns, ); + group - .update_admin_list(&self.inner_client, UpdateAdminListType::Add, inbox_id) + .update_group_pinned_frame_url(&self.inner_client, pinned_frame_url) .await?; Ok(()) } - pub async fn remove_admin(&self, inbox_id: String) -> Result<(), GenericError> { + pub fn group_pinned_frame_url(&self) -> Result { let group = MlsGroup::new( self.inner_client.context().clone(), self.group_id.clone(), self.created_at_ns, ); - group - .update_admin_list(&self.inner_client, UpdateAdminListType::Remove, inbox_id) - .await?; - Ok(()) + let group_pinned_frame_url = group.group_pinned_frame_url(group.mls_provider()?)?; + + Ok(group_pinned_frame_url) } - pub async fn add_super_admin(&self, inbox_id: String) -> Result<(), GenericError> { + pub fn admin_list(&self) -> Result, GenericError> { let group = MlsGroup::new( self.inner_client.context().clone(), self.group_id.clone(), self.created_at_ns, ); - group - .update_admin_list(&self.inner_client, UpdateAdminListType::AddSuper, inbox_id) - .await?; + + let admin_list = group.admin_list(group.mls_provider()?)?; + + Ok(admin_list) + } + + pub fn super_admin_list(&self) -> Result, GenericError> { + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + + let super_admin_list = group.super_admin_list(group.mls_provider()?)?; + + Ok(super_admin_list) + } + + pub fn is_admin(&self, inbox_id: &String) -> Result { + let admin_list = self.admin_list()?; + Ok(admin_list.contains(inbox_id)) + } + + pub fn is_super_admin(&self, inbox_id: &String) -> Result { + let super_admin_list = self.super_admin_list()?; + Ok(super_admin_list.contains(inbox_id)) + } + + pub async fn add_admin(&self, inbox_id: String) -> Result<(), GenericError> { + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + group + .update_admin_list(&self.inner_client, UpdateAdminListType::Add, inbox_id) + .await?; + + Ok(()) + } + + pub async fn remove_admin(&self, inbox_id: String) -> Result<(), GenericError> { + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + group + .update_admin_list(&self.inner_client, UpdateAdminListType::Remove, inbox_id) + .await?; + + Ok(()) + } + + pub async fn add_super_admin(&self, inbox_id: String) -> Result<(), GenericError> { + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + group + .update_admin_list(&self.inner_client, UpdateAdminListType::AddSuper, inbox_id) + .await?; Ok(()) } @@ -798,22 +1371,39 @@ impl FfiGroup { })) } - pub async fn stream( + pub async fn update_permission_policy( &self, - message_callback: Box, - ) -> Result, GenericError> { + permission_update_type: FfiPermissionUpdateType, + permission_policy_option: FfiPermissionPolicy, + metadata_field: Option, + ) -> Result<(), GenericError> { + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + group + .update_permission_policy( + &self.inner_client, + PermissionUpdateType::from(&permission_update_type), + permission_policy_option.try_into()?, + metadata_field.map(|field| MetadataField::from(&field)), + ) + .await + .map_err(|e| GenericError::from(e.to_string()))?; + Ok(()) + } + + pub async fn stream(&self, message_callback: Box) -> FfiStreamCloser { let inner_client = Arc::clone(&self.inner_client); - let stream_closer = MlsGroup::stream_with_callback( + let handle = MlsGroup::stream_with_callback( inner_client, self.group_id.clone(), self.created_at_ns, move |message| message_callback.on_message(message.into()), - )?; + ); - Ok(Arc::new(FfiStreamCloser { - close_fn: stream_closer.close_fn, - is_closed_atomic: stream_closer.is_closed_atomic, - })) + FfiStreamCloser::new(handle) } pub fn created_at_ns(&self) -> i64 { @@ -827,7 +1417,31 @@ impl FfiGroup { self.created_at_ns, ); - Ok(group.is_active()?) + Ok(group.is_active(group.mls_provider()?)?) + } + + pub fn consent_state(&self) -> Result { + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + + let state = group.consent_state()?; + + Ok(state.into()) + } + + pub fn update_consent_state(&self, state: FfiConsentState) -> Result<(), GenericError> { + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + + group.update_consent_state(state.into())?; + + Ok(()) } pub fn added_by_inbox_id(&self) -> Result { @@ -847,7 +1461,7 @@ impl FfiGroup { self.created_at_ns, ); - let metadata = group.metadata()?; + let metadata = group.metadata(group.mls_provider()?)?; Ok(Arc::new(FfiGroupMetadata { inner: Arc::new(metadata), })) @@ -861,7 +1475,7 @@ impl FfiGroup { } } -#[derive(uniffi::Enum)] +#[derive(uniffi::Enum, PartialEq)] pub enum FfiGroupMessageKind { Application, MembershipChange, @@ -928,27 +1542,84 @@ impl From for FfiMessage { } } -#[derive(uniffi::Object)] +#[derive(uniffi::Record)] +pub struct FfiConsent { + pub entity_type: FfiConsentEntityType, + pub state: FfiConsentState, + pub entity: String, +} + +impl From for StoredConsentRecord { + fn from(consent: FfiConsent) -> Self { + Self { + entity_type: consent.entity_type.into(), + state: consent.state.into(), + entity: consent.entity, + } + } +} + +#[derive(uniffi::Object, Clone, Debug)] pub struct FfiStreamCloser { - close_fn: Arc>>>, - is_closed_atomic: Arc, + #[allow(clippy::type_complexity)] + stream_handle: Arc>>>>, + // for convenience, does not require locking mutex. + abort_handle: Arc, } -#[uniffi::export] impl FfiStreamCloser { + pub fn new(stream_handle: StreamHandle>) -> Self { + Self { + abort_handle: Arc::new(stream_handle.handle.abort_handle()), + stream_handle: Arc::new(Mutex::new(Some(stream_handle))), + } + } + + #[cfg(test)] + pub async fn wait_for_ready(&self) { + let mut handle = self.stream_handle.lock().await; + if let Some(ref mut h) = &mut *handle { + h.wait_for_ready().await; + } + } +} + +#[uniffi::export(async_runtime = "tokio")] +impl FfiStreamCloser { + /// Signal the stream to end + /// Does not wait for the stream to end. pub fn end(&self) { - match self.close_fn.lock() { - Ok(mut close_fn_option) => { - let _ = close_fn_option.take().map(|close_fn| close_fn.send(())); - } - _ => { - log::warn!("close_fn already closed"); + self.abort_handle.abort(); + } + + /// End the stream and asyncronously wait for it to shutdown + pub async fn end_and_wait(&self) -> Result<(), GenericError> { + if self.abort_handle.is_finished() { + return Ok(()); + } + + let mut stream_handle = self.stream_handle.lock().await; + let stream_handle = stream_handle.take(); + if let Some(h) = stream_handle { + h.handle.abort(); + match h.handle.await { + Err(e) if !e.is_cancelled() => Err(GenericError::Generic { + err: format!("subscription event loop join error {}", e), + }), + Err(e) if e.is_cancelled() => Ok(()), + Ok(t) => t.map_err(|e| GenericError::Generic { err: e.to_string() }), + Err(e) => Err(GenericError::Generic { + err: format!("error joining task {}", e), + }), } + } else { + log::warn!("subscription already closed"); + Ok(()) } } pub fn is_closed(&self) -> bool { - self.is_closed_atomic.load(Ordering::Relaxed) + self.abort_handle.is_finished() } } @@ -989,35 +1660,70 @@ pub struct FfiGroupPermissions { #[uniffi::export] impl FfiGroupPermissions { - pub fn policy_type(&self) -> Result { - Ok(self.inner.preconfigured_policy()?.into()) + pub fn policy_type(&self) -> Result { + if let Ok(preconfigured_policy) = self.inner.preconfigured_policy() { + Ok(preconfigured_policy.into()) + } else { + Ok(FfiGroupPermissionsOptions::CustomPolicy) + } + } + + pub fn policy_set(&self) -> Result { + let policy_set = &self.inner.policies; + let metadata_policy_map = &policy_set.update_metadata_policy; + let get_policy = |field: &str| { + metadata_policy_map + .get(field) + .map(FfiPermissionPolicy::from) + .unwrap_or(FfiPermissionPolicy::DoesNotExist) + }; + Ok(FfiPermissionPolicySet { + add_member_policy: FfiPermissionPolicy::from(&policy_set.add_member_policy), + remove_member_policy: FfiPermissionPolicy::from(&policy_set.remove_member_policy), + add_admin_policy: FfiPermissionPolicy::from(&policy_set.add_admin_policy), + remove_admin_policy: FfiPermissionPolicy::from(&policy_set.remove_admin_policy), + update_group_name_policy: get_policy(MetadataField::GroupName.as_str()), + update_group_description_policy: get_policy(MetadataField::Description.as_str()), + update_group_image_url_square_policy: get_policy( + MetadataField::GroupImageUrlSquare.as_str(), + ), + update_group_pinned_frame_url_policy: get_policy( + MetadataField::GroupPinnedFrameUrl.as_str(), + ), + }) } } #[cfg(test)] mod tests { + use super::{create_client, signature_verifier, FfiMessage, FfiMessageCallback, FfiXmtpClient}; use crate::{ - get_inbox_id_for_address, inbox_owner::SigningError, logger::FfiLogger, - FfiConversationCallback, FfiCreateGroupOptions, FfiInboxOwner, FfiListConversationsOptions, - FfiListMessagesOptions, GroupPermissions, + get_inbox_id_for_address, inbox_owner::SigningError, logger::FfiLogger, FfiConsent, + FfiConsentEntityType, FfiConsentState, FfiConversationCallback, FfiCreateGroupOptions, + FfiGroup, FfiGroupMessageKind, FfiGroupPermissionsOptions, FfiInboxOwner, + FfiListConversationsOptions, FfiListMessagesOptions, FfiMetadataField, FfiPermissionPolicy, + FfiPermissionPolicySet, FfiPermissionUpdateType, }; + use ethers::utils::hex; + use rand::distributions::{Alphanumeric, DistString}; use std::{ env, sync::{ atomic::{AtomicU32, Ordering}, - Arc, + Arc, Mutex, }, }; - - use super::{create_client, FfiMessage, FfiMessageCallback, FfiXmtpClient}; - use ethers::utils::hex; - use ethers_core::rand::{ - self, - distributions::{Alphanumeric, DistString}, - }; + use tokio::{sync::Notify, time::error::Elapsed}; use xmtp_cryptography::{signature::RecoverableSignature, utils::rng}; - use xmtp_id::associations::generate_inbox_id; - use xmtp_mls::{storage::EncryptionKey, InboxOwner}; + use xmtp_id::associations::{ + generate_inbox_id, + unverified::{UnverifiedRecoverableEcdsaSignature, UnverifiedSignature}, + }; + use xmtp_mls::{ + groups::{GroupError, MlsGroup}, + storage::EncryptionKey, + InboxOwner, + }; #[derive(Clone)] pub struct LocalWalletInboxOwner { @@ -1025,6 +1731,10 @@ mod tests { } impl LocalWalletInboxOwner { + pub fn with_wallet(wallet: xmtp_cryptography::utils::LocalWallet) -> Self { + Self { wallet } + } + pub fn new() -> Self { Self { wallet: xmtp_cryptography::utils::LocalWallet::new(&mut rng()), @@ -1050,40 +1760,52 @@ mod tests { impl FfiLogger for MockLogger { fn log(&self, _level: u32, level_label: String, message: String) { - println!("[{}][t:{}]: {}", level_label, thread_id::get(), message) + println!("[{}]{}", level_label, message) } } - #[derive(Clone)] + #[derive(Default, Clone)] struct RustStreamCallback { num_messages: Arc, + messages: Arc>>, + conversations: Arc>>>, + notify: Arc, } impl RustStreamCallback { - pub fn new() -> Self { - Self { - num_messages: Arc::new(AtomicU32::new(0)), - } - } - pub fn message_count(&self) -> u32 { self.num_messages.load(Ordering::SeqCst) } + + pub async fn wait_for_delivery(&self) -> Result<(), Elapsed> { + tokio::time::timeout(std::time::Duration::from_secs(60), async { + self.notify.notified().await + }) + .await?; + Ok(()) + } } impl FfiMessageCallback for RustStreamCallback { fn on_message(&self, message: FfiMessage) { - println!("Got a message"); - let message = String::from_utf8(message.content).unwrap_or("".to_string()); - log::info!("Received: {}", message); + let mut messages = self.messages.lock().unwrap(); + log::info!( + "ON MESSAGE Received\n-------- \n{}\n----------", + String::from_utf8_lossy(&message.content) + ); + messages.push(message); let _ = self.num_messages.fetch_add(1, Ordering::SeqCst); + self.notify.notify_one(); } } impl FfiConversationCallback for RustStreamCallback { - fn on_conversation(&self, _: Arc) { - println!("received new conversation"); + fn on_conversation(&self, group: Arc) { + log::debug!("received conversation"); let _ = self.num_messages.fetch_add(1, Ordering::SeqCst); + let mut convos = self.conversations.lock().unwrap(); + convos.push(group); + self.notify.notify_one(); } } @@ -1113,8 +1835,11 @@ mod tests { client.register_identity(signature_request).await.unwrap(); } - async fn new_test_client() -> Arc { - let ffi_inbox_owner = LocalWalletInboxOwner::new(); + /// Create a new test client with a given wallet. + async fn new_test_client_with_wallet( + wallet: xmtp_cryptography::utils::LocalWallet, + ) -> Arc { + let ffi_inbox_owner = LocalWalletInboxOwner::with_wallet(wallet); let nonce = 1; let inbox_id = generate_inbox_id(&ffi_inbox_owner.get_address(), &nonce); @@ -1123,18 +1848,39 @@ mod tests { xmtp_api_grpc::LOCALHOST_ADDRESS.to_string(), false, Some(tmp_path()), - None, + Some(xmtp_mls::storage::EncryptedMessageStore::generate_enc_key().into()), &inbox_id, ffi_inbox_owner.get_address(), nonce, None, + None, ) .await .unwrap(); + register_client(&ffi_inbox_owner, &client).await; client } + async fn new_test_client() -> Arc { + let wallet = xmtp_cryptography::utils::LocalWallet::new(&mut rng()); + new_test_client_with_wallet(wallet).await + } + + impl FfiGroup { + #[cfg(test)] + async fn update_installations(&self) -> Result<(), GroupError> { + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + + group.update_installations(&self.inner_client).await?; + Ok(()) + } + } + #[tokio::test] async fn get_inbox_id() { let client = new_test_client().await; @@ -1153,13 +1899,6 @@ mod tests { assert_eq!(real_inbox_id, from_network); } - // Try a query on a test topic, and make sure we get a response - #[tokio::test] - async fn test_client_creation() { - let client = new_test_client().await; - assert!(client.signature_request().is_some()); - } - #[tokio::test] #[ignore] async fn test_legacy_identity() { @@ -1178,6 +1917,7 @@ mod tests { account_address.to_string(), nonce, Some(legacy_keys), + None, ) .await .unwrap(); @@ -1203,6 +1943,7 @@ mod tests { ffi_inbox_owner.get_address(), nonce, None, + None, ) .await .unwrap(); @@ -1221,6 +1962,7 @@ mod tests { ffi_inbox_owner.get_address(), nonce, None, + None, ) .await .unwrap(); @@ -1254,6 +1996,7 @@ mod tests { ffi_inbox_owner.get_address(), nonce, None, + None, ) .await .unwrap(); @@ -1273,6 +2016,7 @@ mod tests { ffi_inbox_owner.get_address(), nonce, None, + None, ) .await .is_err(); @@ -1280,92 +2024,229 @@ mod tests { assert!(result_errored, "did not error on wrong encryption key") } - #[tokio::test(flavor = "multi_thread", worker_threads = 1)] - async fn test_create_group_with_members() { - let amal = new_test_client().await; - let bola = new_test_client().await; + use super::FfiSignatureRequest; + async fn sign_with_wallet( + wallet: &xmtp_cryptography::utils::LocalWallet, + signature_request: &FfiSignatureRequest, + ) { + let signature_text = signature_request.inner.lock().await.signature_text(); + let wallet_signature: Vec = wallet.sign(&signature_text.clone()).unwrap().into(); - let group = amal - .conversations() - .create_group( - vec![bola.account_address.clone()], - FfiCreateGroupOptions::default(), - ) + signature_request + .inner + .lock() .await - .unwrap(); - - let members = group.list_members().unwrap(); - assert_eq!(members.len(), 2); - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 1)] - async fn test_create_group_with_metadata() { - let amal = new_test_client().await; - let bola = new_test_client().await; - - let group = amal - .conversations() - .create_group( - vec![bola.account_address.clone()], - FfiCreateGroupOptions { - permissions: Some(GroupPermissions::AdminOnly), - group_name: Some("Group Name".to_string()), - group_image_url_square: Some("url".to_string()), - }, + .add_signature( + UnverifiedSignature::RecoverableEcdsa(UnverifiedRecoverableEcdsaSignature::new( + wallet_signature, + )), + &signature_verifier(), ) .await .unwrap(); - - let members = group.list_members().unwrap(); - assert_eq!(members.len(), 2); - assert_eq!(group.group_name().unwrap(), "Group Name"); - assert_eq!(group.group_image_url_square().unwrap(), "url"); } + use xmtp_cryptography::utils::generate_local_wallet; + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] - async fn test_invalid_external_signature() { - let inbox_owner = LocalWalletInboxOwner::new(); + async fn test_can_add_wallet_to_inbox() { + // Setup the initial first client + let ffi_inbox_owner = LocalWalletInboxOwner::new(); let nonce = 1; - let inbox_id = generate_inbox_id(&inbox_owner.get_address(), &nonce); - let path = tmp_path(); + let inbox_id = generate_inbox_id(&ffi_inbox_owner.get_address(), &nonce); + let path = tmp_path(); + let key = static_enc_key().to_vec(); let client = create_client( Box::new(MockLogger {}), xmtp_api_grpc::LOCALHOST_ADDRESS.to_string(), false, Some(path.clone()), - None, // encryption_key + Some(key), &inbox_id, - inbox_owner.get_address(), + ffi_inbox_owner.get_address(), nonce, - None, // v2_signed_private_key_proto + None, + None, ) .await .unwrap(); - let signature_request = client.signature_request().unwrap(); - assert!(client.register_identity(signature_request).await.is_err()); + let signature_request = client.signature_request().unwrap().clone(); + register_client(&ffi_inbox_owner, &client).await; + + sign_with_wallet(&ffi_inbox_owner.wallet, &signature_request).await; + + let conn = client.inner_client.store().conn().unwrap(); + let state = client + .inner_client + .get_latest_association_state(&conn, &inbox_id) + .await + .expect("could not get state"); + + assert_eq!(state.members().len(), 2); + + // Now, add the second wallet to the client + + let wallet_to_add = generate_local_wallet(); + let new_account_address = wallet_to_add.get_address(); + println!("second address: {}", new_account_address); + + let signature_request = client + .add_wallet(&ffi_inbox_owner.get_address(), &new_account_address) + .await + .expect("could not add wallet"); + + sign_with_wallet(&ffi_inbox_owner.wallet, &signature_request).await; + sign_with_wallet(&wallet_to_add, &signature_request).await; + + client + .apply_signature_request(signature_request) + .await + .unwrap(); + + let updated_state = client + .inner_client + .get_latest_association_state(&conn, &inbox_id) + .await + .expect("could not get state"); + + assert_eq!(updated_state.members().len(), 3); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] - async fn test_can_message() { - let amal = LocalWalletInboxOwner::new(); + async fn test_can_revoke_wallet() { + // Setup the initial first client + let ffi_inbox_owner = LocalWalletInboxOwner::new(); let nonce = 1; - let amal_inbox_id = generate_inbox_id(&amal.get_address(), &nonce); - let bola = LocalWalletInboxOwner::new(); - let bola_inbox_id = generate_inbox_id(&bola.get_address(), &nonce); - let path = tmp_path(); + let inbox_id = generate_inbox_id(&ffi_inbox_owner.get_address(), &nonce); - let client_amal = create_client( + let path = tmp_path(); + let key = static_enc_key().to_vec(); + let client = create_client( Box::new(MockLogger {}), xmtp_api_grpc::LOCALHOST_ADDRESS.to_string(), false, Some(path.clone()), - None, - &amal_inbox_id, - amal.get_address(), + Some(key), + &inbox_id, + ffi_inbox_owner.get_address(), nonce, None, + None, + ) + .await + .unwrap(); + + let signature_request = client.signature_request().unwrap().clone(); + register_client(&ffi_inbox_owner, &client).await; + + sign_with_wallet(&ffi_inbox_owner.wallet, &signature_request).await; + + let conn = client.inner_client.store().conn().unwrap(); + let state = client + .inner_client + .get_latest_association_state(&conn, &inbox_id) + .await + .expect("could not get state"); + + assert_eq!(state.members().len(), 2); + + // Now, add the second wallet to the client + + let wallet_to_add = generate_local_wallet(); + let new_account_address = wallet_to_add.get_address(); + println!("second address: {}", new_account_address); + + let signature_request = client + .add_wallet(&ffi_inbox_owner.get_address(), &new_account_address) + .await + .expect("could not add wallet"); + + sign_with_wallet(&ffi_inbox_owner.wallet, &signature_request).await; + sign_with_wallet(&wallet_to_add, &signature_request).await; + + client + .apply_signature_request(signature_request.clone()) + .await + .unwrap(); + + let updated_state = client + .inner_client + .get_latest_association_state(&conn, &inbox_id) + .await + .expect("could not get state"); + + assert_eq!(updated_state.members().len(), 3); + + // Now, revoke the second wallet + let signature_request = client + .revoke_wallet(&new_account_address) + .await + .expect("could not revoke wallet"); + + sign_with_wallet(&ffi_inbox_owner.wallet, &signature_request).await; + + client + .apply_signature_request(signature_request) + .await + .unwrap(); + + let revoked_state = client + .inner_client + .get_latest_association_state(&conn, &inbox_id) + .await + .expect("could not get state"); + + assert_eq!(revoked_state.members().len(), 2); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn test_invalid_external_signature() { + let inbox_owner = LocalWalletInboxOwner::new(); + let nonce = 1; + let inbox_id = generate_inbox_id(&inbox_owner.get_address(), &nonce); + let path = tmp_path(); + + let client = create_client( + Box::new(MockLogger {}), + xmtp_api_grpc::LOCALHOST_ADDRESS.to_string(), + false, + Some(path.clone()), + None, // encryption_key + &inbox_id, + inbox_owner.get_address(), + nonce, + None, // v2_signed_private_key_proto + None, + ) + .await + .unwrap(); + + let signature_request = client.signature_request().unwrap(); + assert!(client.register_identity(signature_request).await.is_err()); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn test_can_message() { + let amal = LocalWalletInboxOwner::new(); + let nonce = 1; + let amal_inbox_id = generate_inbox_id(&amal.get_address(), &nonce); + let bola = LocalWalletInboxOwner::new(); + let bola_inbox_id = generate_inbox_id(&bola.get_address(), &nonce); + let path = tmp_path(); + + let client_amal = create_client( + Box::new(MockLogger {}), + xmtp_api_grpc::LOCALHOST_ADDRESS.to_string(), + false, + Some(path.clone()), + None, + &amal_inbox_id, + amal.get_address(), + nonce, + None, + None, ) .await .unwrap(); @@ -1392,6 +2273,7 @@ mod tests { bola.get_address(), nonce, None, + None, ) .await .unwrap(); @@ -1411,26 +2293,67 @@ mod tests { ); } + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn test_create_group_with_members() { + let amal = new_test_client().await; + let bola = new_test_client().await; + + let group = amal + .conversations() + .create_group( + vec![bola.account_address.clone()], + FfiCreateGroupOptions::default(), + ) + .await + .unwrap(); + + let members = group.list_members().await.unwrap(); + assert_eq!(members.len(), 2); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn test_create_group_with_metadata() { + let amal = new_test_client().await; + let bola = new_test_client().await; + + let group = amal + .conversations() + .create_group( + vec![bola.account_address.clone()], + FfiCreateGroupOptions { + permissions: Some(FfiGroupPermissionsOptions::AdminOnly), + group_name: Some("Group Name".to_string()), + group_image_url_square: Some("url".to_string()), + group_description: Some("group description".to_string()), + group_pinned_frame_url: Some("pinned frame".to_string()), + custom_permission_policy_set: None, + }, + ) + .await + .unwrap(); + + let members = group.list_members().await.unwrap(); + assert_eq!(members.len(), 2); + assert_eq!(group.group_name().unwrap(), "Group Name"); + assert_eq!(group.group_image_url_square().unwrap(), "url"); + assert_eq!(group.group_description().unwrap(), "group description"); + assert_eq!(group.group_pinned_frame_url().unwrap(), "pinned frame"); + } + // Looks like this test might be a separate issue #[tokio::test(flavor = "multi_thread", worker_threads = 5)] #[ignore] async fn test_can_stream_group_messages_for_updates() { - let _ = env_logger::builder() - .is_test(true) - .filter_level(log::LevelFilter::Info) - .try_init(); - let alix = new_test_client().await; let bo = new_test_client().await; // Stream all group messages - let message_callbacks = RustStreamCallback::new(); + let message_callbacks = RustStreamCallback::default(); let stream_messages = bo .conversations() .stream_all_messages(Box::new(message_callbacks.clone())) - .await - .unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + .await; + stream_messages.wait_for_ready().await; // Create group and send first message let alix_group = alix @@ -1442,12 +2365,11 @@ mod tests { .await .unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; - alix_group .update_group_name("Old Name".to_string()) .await .unwrap(); + message_callbacks.wait_for_delivery().await.unwrap(); let bo_groups = bo .conversations() @@ -1460,44 +2382,123 @@ mod tests { .update_group_name("Old Name2".to_string()) .await .unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + message_callbacks.wait_for_delivery().await.unwrap(); // Uncomment the following lines to add more group name updates - // alix_group.update_group_name("Again Name".to_string()).await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; - bo_group - .update_group_name("Old Name2".to_string()) + .update_group_name("Old Name3".to_string()) .await .unwrap(); + message_callbacks.wait_for_delivery().await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; assert_eq!(message_callbacks.message_count(), 3); - stream_messages.end(); - tokio::time::sleep(tokio::time::Duration::from_millis(5)).await; + stream_messages.end_and_wait().await.unwrap(); + assert!(stream_messages.is_closed()); } #[tokio::test(flavor = "multi_thread", worker_threads = 5)] - async fn test_can_stream_and_update_name_without_forking_group() { + async fn test_can_sync_all_groups() { let alix = new_test_client().await; let bo = new_test_client().await; - // Stream all group messages - let message_callbacks = RustStreamCallback::new(); - let stream_messages = bo + for _i in 0..30 { + alix.conversations() + .create_group( + vec![bo.account_address.clone()], + FfiCreateGroupOptions::default(), + ) + .await + .unwrap(); + } + + bo.conversations().sync().await.unwrap(); + let alix_groups = alix .conversations() - .stream_all_messages(Box::new(message_callbacks.clone())) + .list(FfiListConversationsOptions::default()) .await .unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + let alix_group1 = alix_groups[0].clone(); + let alix_group5 = alix_groups[5].clone(); + let bo_group1 = bo.group(alix_group1.id()).unwrap(); + let bo_group5 = bo.group(alix_group5.id()).unwrap(); - let first_msg_check = 2; - let second_msg_check = 5; + alix_group1.send("alix1".as_bytes().to_vec()).await.unwrap(); + alix_group5.send("alix1".as_bytes().to_vec()).await.unwrap(); + + let bo_messages1 = bo_group1 + .find_messages(FfiListMessagesOptions::default()) + .unwrap(); + let bo_messages5 = bo_group5 + .find_messages(FfiListMessagesOptions::default()) + .unwrap(); + assert_eq!(bo_messages1.len(), 0); + assert_eq!(bo_messages5.len(), 0); + + bo.conversations().sync_all_groups().await.unwrap(); + + let bo_messages1 = bo_group1 + .find_messages(FfiListMessagesOptions::default()) + .unwrap(); + let bo_messages5 = bo_group5 + .find_messages(FfiListMessagesOptions::default()) + .unwrap(); + assert_eq!(bo_messages1.len(), 1); + assert_eq!(bo_messages5.len(), 1); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_can_sync_all_groups_active_only() { + let alix = new_test_client().await; + let bo = new_test_client().await; + + // Create 30 groups with alix and bo and sync them + for _i in 0..30 { + alix.conversations() + .create_group( + vec![bo.account_address.clone()], + FfiCreateGroupOptions::default(), + ) + .await + .unwrap(); + } + bo.conversations().sync().await.unwrap(); + let num_groups_synced_1: u32 = bo.conversations().sync_all_groups().await.unwrap(); + assert!(num_groups_synced_1 == 30); + + // Remove bo from all groups and sync + for group in alix + .conversations() + .list(FfiListConversationsOptions::default()) + .await + .unwrap() + { + group + .remove_members(vec![bo.account_address.clone()]) + .await + .unwrap(); + } + + // First sync after removal needs to process all groups and set them to inactive + let num_groups_synced_2: u32 = bo.conversations().sync_all_groups().await.unwrap(); + assert!(num_groups_synced_2 == 30); + + // Second sync after removal will not process inactive groups + let num_groups_synced_3: u32 = bo.conversations().sync_all_groups().await.unwrap(); + assert!(num_groups_synced_3 == 0); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_can_send_message_when_out_of_sync() { + let alix = new_test_client().await; + let bo = new_test_client().await; + let caro = new_test_client().await; + let davon = new_test_client().await; + let eri = new_test_client().await; + let frankie = new_test_client().await; - // Create group and send first message let alix_group = alix .conversations() .create_group( @@ -1507,129 +2508,571 @@ mod tests { .await .unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + bo.conversations().sync().await.unwrap(); + let bo_group = bo.group(alix_group.id()).unwrap(); + + bo_group.send("bo1".as_bytes().to_vec()).await.unwrap(); + alix_group.send("alix1".as_bytes().to_vec()).await.unwrap(); + // Move the group forward by 3 epochs (as Alix's max_past_epochs is + // configured to 3) without Bo syncing alix_group - .update_group_name("hello".to_string()) + .add_members(vec![ + caro.account_address.clone(), + davon.account_address.clone(), + ]) .await .unwrap(); - alix_group.send("hello1".as_bytes().to_vec()).await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; - bo.conversations().sync().await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; - - let bo_groups = bo - .conversations() - .list(FfiListConversationsOptions::default()) + alix_group + .remove_members(vec![ + caro.account_address.clone(), + davon.account_address.clone(), + ]) .await .unwrap(); - assert_eq!(bo_groups.len(), 1); - let bo_group = bo_groups[0].clone(); - bo_group.sync().await.unwrap(); - - let bo_messages1 = bo_group - .find_messages(FfiListMessagesOptions::default()) + alix_group + .add_members(vec![ + eri.account_address.clone(), + frankie.account_address.clone(), + ]) + .await .unwrap(); - assert_eq!(bo_messages1.len(), first_msg_check); - bo_group.send("hello2".as_bytes().to_vec()).await.unwrap(); - bo_group.send("hello3".as_bytes().to_vec()).await.unwrap(); - alix_group.sync().await.unwrap(); + // Bo sends messages to Alix while 3 epochs behind + bo_group.send("bo3".as_bytes().to_vec()).await.unwrap(); + alix_group.send("alix3".as_bytes().to_vec()).await.unwrap(); + bo_group.send("bo4".as_bytes().to_vec()).await.unwrap(); + bo_group.send("bo5".as_bytes().to_vec()).await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + alix_group.sync().await.unwrap(); let alix_messages = alix_group .find_messages(FfiListMessagesOptions::default()) .unwrap(); - assert_eq!(alix_messages.len(), second_msg_check); - alix_group.send("hello4".as_bytes().to_vec()).await.unwrap(); bo_group.sync().await.unwrap(); - - let bo_messages2 = bo_group + let bo_messages = bo_group .find_messages(FfiListMessagesOptions::default()) .unwrap(); - assert_eq!(bo_messages2.len(), second_msg_check); + assert_eq!(bo_messages.len(), 9); + assert_eq!(alix_messages.len(), 10); - // tokio::time::sleep(tokio::time::Duration::from_millis(10000)).await; - // assert_eq!(message_callbacks.message_count(), 5); - - stream_messages.end(); - tokio::time::sleep(tokio::time::Duration::from_millis(5)).await; - assert!(stream_messages.is_closed()); + assert_eq!( + bo_messages[bo_messages.len() - 1].id, + alix_messages[alix_messages.len() - 1].id + ); } #[tokio::test(flavor = "multi_thread", worker_threads = 5)] - // This one is flaky for me. Passes reliably locally and fails on CI - #[ignore] - async fn test_conversation_streaming() { - let amal = new_test_client().await; - let bola = new_test_client().await; - - let stream_callback = RustStreamCallback::new(); - - let stream = bola + async fn test_create_new_installation_without_breaking_group() { + let wallet1_key = &mut rng(); + let wallet1 = xmtp_cryptography::utils::LocalWallet::new(wallet1_key); + let wallet2_key = &mut rng(); + let wallet2 = xmtp_cryptography::utils::LocalWallet::new(wallet2_key); + + // Create clients + let client1 = new_test_client_with_wallet(wallet1).await; + let client2 = new_test_client_with_wallet(wallet2.clone()).await; + // Create a new group with client1 including wallet2 + + let group = client1 .conversations() - .stream(Box::new(stream_callback.clone())) - .await - .unwrap(); - - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - - amal.conversations() .create_group( - vec![bola.account_address.clone()], + vec![client2.account_address.clone()], FfiCreateGroupOptions::default(), ) .await .unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + // Sync groups + client1.conversations().sync().await.unwrap(); + client2.conversations().sync().await.unwrap(); - assert_eq!(stream_callback.message_count(), 1); - // Create another group and add bola - amal.conversations() - .create_group( - vec![bola.account_address.clone()], - FfiCreateGroupOptions::default(), - ) + // Find groups for both clients + let client1_group = client1.group(group.id()).unwrap(); + let client2_group = client2.group(group.id()).unwrap(); + + // Sync both groups + client1_group.sync().await.unwrap(); + client2_group.sync().await.unwrap(); + + // Assert both clients see 2 members + let client1_members = client1_group.list_members().await.unwrap(); + assert_eq!(client1_members.len(), 2); + + let client2_members = client2_group.list_members().await.unwrap(); + assert_eq!(client2_members.len(), 2); + + // Drop and delete local database for client2 + client2.release_db_connection().unwrap(); + + // Recreate client2 (new installation) + let client2 = new_test_client_with_wallet(wallet2).await; + + client1_group.update_installations().await.unwrap(); + + // Send a message that will break the group + client1_group + .send("This message will break the group".as_bytes().to_vec()) .await .unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - assert_eq!(stream_callback.message_count(), 2); + // Assert client1 still sees 2 members + let client1_members = client1_group.list_members().await.unwrap(); + assert_eq!(client1_members.len(), 2); - stream.end(); - tokio::time::sleep(tokio::time::Duration::from_millis(5)).await; - assert!(stream.is_closed()); + client2.conversations().sync().await.unwrap(); + let client2_group = client2.group(group.id()).unwrap(); + let client2_members = client2_group.list_members().await.unwrap(); + assert_eq!(client2_members.len(), 2); } #[tokio::test(flavor = "multi_thread", worker_threads = 5)] - async fn test_stream_all_messages() { + async fn test_create_new_installations_does_not_fork_group() { + let bo_wallet_key = &mut rng(); + let bo_wallet = xmtp_cryptography::utils::LocalWallet::new(bo_wallet_key); + + // Create clients let alix = new_test_client().await; - let bo = new_test_client().await; + let bo = new_test_client_with_wallet(bo_wallet.clone()).await; let caro = new_test_client().await; - let alix_group = alix + // Alix begins a stream for all messages + let message_callbacks = RustStreamCallback::default(); + let stream_messages = alix + .conversations() + .stream_all_messages(Box::new(message_callbacks.clone())) + .await; + stream_messages.wait_for_ready().await; + + // Alix creates a group with Bo and Caro + let group = alix .conversations() .create_group( - vec![caro.account_address.clone()], + vec![bo.account_address.clone(), caro.account_address.clone()], FfiCreateGroupOptions::default(), ) .await .unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - let stream_callback = RustStreamCallback::new(); + // Alix and Caro Sync groups + alix.conversations().sync().await.unwrap(); + bo.conversations().sync().await.unwrap(); + caro.conversations().sync().await.unwrap(); + + // Alix and Caro find the group + let alix_group = alix.group(group.id()).unwrap(); + let bo_group = bo.group(group.id()).unwrap(); + let caro_group = caro.group(group.id()).unwrap(); - let stream = caro - .conversations() - .stream_all_messages(Box::new(stream_callback.clone())) + alix_group.update_installations().await.unwrap(); + log::info!("Alix sending first message"); + // Alix sends a message in the group + alix_group + .send("First message".as_bytes().to_vec()) .await .unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - alix_group.send("first".as_bytes().to_vec()).await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + log::info!("Caro sending second message"); + caro_group.update_installations().await.unwrap(); + // Caro sends a message in the group + caro_group + .send("Second message".as_bytes().to_vec()) + .await + .unwrap(); + + // Bo logs back in with a new installation + let bo2 = new_test_client_with_wallet(bo_wallet).await; + + // Bo begins a stream for all messages + let bo_message_callbacks = RustStreamCallback::default(); + let bo_stream_messages = bo2 + .conversations() + .stream_all_messages(Box::new(bo_message_callbacks.clone())) + .await; + bo_stream_messages.wait_for_ready().await; + + alix_group.update_installations().await.unwrap(); + + log::info!("Alix sending third message after Bo's second installation added"); + // Alix sends a message to the group + alix_group + .send("Third message".as_bytes().to_vec()) + .await + .unwrap(); + + // New installation of bo finds the group + bo2.conversations().sync().await.unwrap(); + let bo2_group = bo2.group(group.id()).unwrap(); + + log::info!("Bo sending fourth message"); + // Bo sends a message to the group + bo2_group.update_installations().await.unwrap(); + bo2_group + .send("Fourth message".as_bytes().to_vec()) + .await + .unwrap(); + + log::info!("Caro sending fifth message"); + // Caro sends a message in the group + caro_group.update_installations().await.unwrap(); + caro_group + .send("Fifth message".as_bytes().to_vec()) + .await + .unwrap(); + + log::info!("Syncing alix"); + alix_group.sync().await.unwrap(); + log::info!("Syncing bo 1"); + bo_group.sync().await.unwrap(); + log::info!("Syncing bo 2"); + bo2_group.sync().await.unwrap(); + log::info!("Syncing caro"); + caro_group.sync().await.unwrap(); + + // Get the message count for all the clients + let caro_messages = caro_group + .find_messages(FfiListMessagesOptions::default()) + .unwrap(); + let alix_messages = alix_group + .find_messages(FfiListMessagesOptions::default()) + .unwrap(); + let bo_messages = bo_group + .find_messages(FfiListMessagesOptions::default()) + .unwrap(); + let bo2_messages = bo2_group + .find_messages(FfiListMessagesOptions::default()) + .unwrap(); + + assert_eq!(caro_messages.len(), 5); + assert_eq!(alix_messages.len(), 6); + assert_eq!(bo_messages.len(), 5); + // Bo 2 only sees three messages since it joined after the first 2 were sent + assert_eq!(bo2_messages.len(), 3); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_can_send_messages_when_epochs_behind() { + let alix = new_test_client().await; + let bo = new_test_client().await; + + let alix_group = alix + .conversations() + .create_group( + vec![bo.account_address.clone()], + FfiCreateGroupOptions::default(), + ) + .await + .unwrap(); + + bo.conversations().sync().await.unwrap(); + + let bo_group = bo.group(alix_group.id()).unwrap(); + + // Move forward 4 epochs + alix_group + .update_group_description("change 1".to_string()) + .await + .unwrap(); + alix_group + .update_group_description("change 2".to_string()) + .await + .unwrap(); + alix_group + .update_group_description("change 3".to_string()) + .await + .unwrap(); + alix_group + .update_group_description("change 4".to_string()) + .await + .unwrap(); + + bo_group + .send("bo message 1".as_bytes().to_vec()) + .await + .unwrap(); + + alix_group.sync().await.unwrap(); + bo_group.sync().await.unwrap(); + + let alix_messages = alix_group + .find_messages(FfiListMessagesOptions::default()) + .unwrap(); + let bo_messages = bo_group + .find_messages(FfiListMessagesOptions::default()) + .unwrap(); + + let alix_can_see_bo_message = alix_messages + .iter() + .any(|message| message.content == "bo message 1".as_bytes()); + assert!( + alix_can_see_bo_message, + "\"bo message 1\" not found in alix's messages" + ); + + let bo_can_see_bo_message = bo_messages + .iter() + .any(|message| message.content == "bo message 1".as_bytes()); + assert!( + bo_can_see_bo_message, + "\"bo message 1\" not found in bo's messages" + ); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_can_add_members_when_out_of_sync() { + let alix = new_test_client().await; + let bo = new_test_client().await; + let caro = new_test_client().await; + let davon = new_test_client().await; + let eri = new_test_client().await; + let frankie = new_test_client().await; + + let alix_group = alix + .conversations() + .create_group( + vec![bo.account_address.clone()], + FfiCreateGroupOptions::default(), + ) + .await + .unwrap(); + + bo.conversations().sync().await.unwrap(); + let bo_group = bo.group(alix_group.id()).unwrap(); + + bo_group.send("bo1".as_bytes().to_vec()).await.unwrap(); + alix_group.send("alix1".as_bytes().to_vec()).await.unwrap(); + + // Move the group forward by 3 epochs (as Alix's max_past_epochs is + // configured to 3) without Bo syncing + alix_group + .add_members(vec![ + caro.account_address.clone(), + davon.account_address.clone(), + ]) + .await + .unwrap(); + alix_group + .remove_members(vec![ + caro.account_address.clone(), + davon.account_address.clone(), + ]) + .await + .unwrap(); + alix_group + .add_members(vec![eri.account_address.clone()]) + .await + .unwrap(); + + // Bo adds a member while 3 epochs behind + bo_group + .add_members(vec![frankie.account_address.clone()]) + .await + .unwrap(); + + bo_group.sync().await.unwrap(); + let bo_members = bo_group.list_members().await.unwrap(); + assert_eq!(bo_members.len(), 4); + + alix_group.sync().await.unwrap(); + let alix_members = alix_group.list_members().await.unwrap(); + assert_eq!(alix_members.len(), 4); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_removed_members_no_longer_update() { + let alix = new_test_client().await; + let bo = new_test_client().await; + + let alix_group = alix + .conversations() + .create_group( + vec![bo.account_address.clone()], + FfiCreateGroupOptions::default(), + ) + .await + .unwrap(); + + bo.conversations().sync().await.unwrap(); + let bo_group = bo.group(alix_group.id()).unwrap(); + + alix_group.sync().await.unwrap(); + let alix_members = alix_group.list_members().await.unwrap(); + assert_eq!(alix_members.len(), 2); + + bo_group.sync().await.unwrap(); + let bo_members = bo_group.list_members().await.unwrap(); + assert_eq!(bo_members.len(), 2); + + let bo_messages = bo_group + .find_messages(FfiListMessagesOptions::default()) + .unwrap(); + assert_eq!(bo_messages.len(), 0); + + alix_group + .remove_members(vec![bo.account_address.clone()]) + .await + .unwrap(); + + alix_group.send("hello".as_bytes().to_vec()).await.unwrap(); + + bo_group.sync().await.unwrap(); + assert!(!bo_group.is_active().unwrap()); + + let bo_messages = bo_group + .find_messages(FfiListMessagesOptions::default()) + .unwrap(); + assert!(bo_messages.first().unwrap().kind == FfiGroupMessageKind::MembershipChange); + assert_eq!(bo_messages.len(), 1); + + let bo_members = bo_group.list_members().await.unwrap(); + assert_eq!(bo_members.len(), 1); + + alix_group.sync().await.unwrap(); + let alix_members = alix_group.list_members().await.unwrap(); + assert_eq!(alix_members.len(), 1); + } + + // test is also showing intermittent failures with database locked msg + #[ignore] + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_can_stream_and_update_name_without_forking_group() { + let alix = new_test_client().await; + let bo = new_test_client().await; + + // Stream all group messages + let message_callbacks = RustStreamCallback::default(); + let stream_messages = bo + .conversations() + .stream_all_messages(Box::new(message_callbacks.clone())) + .await; + stream_messages.wait_for_ready().await; + + let first_msg_check = 2; + let second_msg_check = 5; + + // Create group and send first message + let alix_group = alix + .conversations() + .create_group( + vec![bo.account_address.clone()], + FfiCreateGroupOptions::default(), + ) + .await + .unwrap(); + + alix_group + .update_group_name("hello".to_string()) + .await + .unwrap(); + message_callbacks.wait_for_delivery().await.unwrap(); + alix_group.send("hello1".as_bytes().to_vec()).await.unwrap(); + message_callbacks.wait_for_delivery().await.unwrap(); + + let bo_groups = bo + .conversations() + .list(FfiListConversationsOptions::default()) + .await + .unwrap(); + assert_eq!(bo_groups.len(), 1); + let bo_group = bo_groups[0].clone(); + bo_group.sync().await.unwrap(); + + let bo_messages1 = bo_group + .find_messages(FfiListMessagesOptions::default()) + .unwrap(); + assert_eq!(bo_messages1.len(), first_msg_check); + + bo_group.send("hello2".as_bytes().to_vec()).await.unwrap(); + message_callbacks.wait_for_delivery().await.unwrap(); + bo_group.send("hello3".as_bytes().to_vec()).await.unwrap(); + message_callbacks.wait_for_delivery().await.unwrap(); + + alix_group.sync().await.unwrap(); + + let alix_messages = alix_group + .find_messages(FfiListMessagesOptions::default()) + .unwrap(); + assert_eq!(alix_messages.len(), second_msg_check); + + alix_group.send("hello4".as_bytes().to_vec()).await.unwrap(); + message_callbacks.wait_for_delivery().await.unwrap(); + bo_group.sync().await.unwrap(); + + let bo_messages2 = bo_group + .find_messages(FfiListMessagesOptions::default()) + .unwrap(); + assert_eq!(bo_messages2.len(), second_msg_check); + assert_eq!(message_callbacks.message_count(), second_msg_check as u32); + + stream_messages.end_and_wait().await.unwrap(); + assert!(stream_messages.is_closed()); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_conversation_streaming() { + let amal = new_test_client().await; + let bola = new_test_client().await; + + let stream_callback = RustStreamCallback::default(); + + let stream = bola + .conversations() + .stream(Box::new(stream_callback.clone())) + .await; + + amal.conversations() + .create_group( + vec![bola.account_address.clone()], + FfiCreateGroupOptions::default(), + ) + .await + .unwrap(); + + stream_callback.wait_for_delivery().await.unwrap(); + + assert_eq!(stream_callback.message_count(), 1); + // Create another group and add bola + amal.conversations() + .create_group( + vec![bola.account_address.clone()], + FfiCreateGroupOptions::default(), + ) + .await + .unwrap(); + stream_callback.wait_for_delivery().await.unwrap(); + + assert_eq!(stream_callback.message_count(), 2); + + stream.end_and_wait().await.unwrap(); + assert!(stream.is_closed()); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_stream_all_messages() { + let alix = new_test_client().await; + let bo = new_test_client().await; + let caro = new_test_client().await; + + let alix_group = alix + .conversations() + .create_group( + vec![caro.account_address.clone()], + FfiCreateGroupOptions::default(), + ) + .await + .unwrap(); + + let stream_callback = RustStreamCallback::default(); + + let stream = caro + .conversations() + .stream_all_messages(Box::new(stream_callback.clone())) + .await; + stream.wait_for_ready().await; + + alix_group.send("first".as_bytes().to_vec()).await.unwrap(); + stream_callback.wait_for_delivery().await.unwrap(); + let bo_group = bo .conversations() .create_group( @@ -1638,26 +3081,26 @@ mod tests { ) .await .unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; + let _ = caro.inner_client.sync_welcomes().await.unwrap(); + bo_group.send("second".as_bytes().to_vec()).await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + stream_callback.wait_for_delivery().await.unwrap(); alix_group.send("third".as_bytes().to_vec()).await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + stream_callback.wait_for_delivery().await.unwrap(); bo_group.send("fourth".as_bytes().to_vec()).await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(300)).await; + stream_callback.wait_for_delivery().await.unwrap(); assert_eq!(stream_callback.message_count(), 4); - stream.end(); - tokio::time::sleep(tokio::time::Duration::from_millis(5)).await; + stream.end_and_wait().await.unwrap(); assert!(stream.is_closed()); } - #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + #[tokio::test(flavor = "multi_thread")] async fn test_message_streaming() { let amal = new_test_client().await; let bola = new_test_client().await; - let group = amal + let amal_group: Arc = amal .conversations() .create_group( vec![bola.account_address.clone()], @@ -1666,23 +3109,25 @@ mod tests { .await .unwrap(); - let stream_callback = RustStreamCallback::new(); - let stream_closer = group - .stream(Box::new(stream_callback.clone())) + bola.inner_client.sync_welcomes().await.unwrap(); + let bola_group = bola.group(amal_group.group_id.clone()).unwrap(); + + let stream_callback = RustStreamCallback::default(); + let stream_closer = bola_group.stream(Box::new(stream_callback.clone())).await; + + stream_closer.wait_for_ready().await; + + amal_group.send("hello".as_bytes().to_vec()).await.unwrap(); + stream_callback.wait_for_delivery().await.unwrap(); + + amal_group + .send("goodbye".as_bytes().to_vec()) .await .unwrap(); + stream_callback.wait_for_delivery().await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - group.send("hello".as_bytes().to_vec()).await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - group.send("goodbye".as_bytes().to_vec()).await.unwrap(); - // Because of the event loop, I need to make the test give control - // back to the stream before it can process each message. Using sleep to do that. - // I think this will work fine in practice - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; assert_eq!(stream_callback.message_count(), 2); - - stream_closer.end(); + stream_closer.end_and_wait().await.unwrap(); } #[tokio::test(flavor = "multi_thread", worker_threads = 5)] @@ -1703,21 +3148,19 @@ mod tests { ) .await .unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - let stream_callback = RustStreamCallback::new(); + let stream_callback = RustStreamCallback::default(); let stream_closer = bola .conversations() .stream_all_messages(Box::new(stream_callback.clone())) - .await - .unwrap(); + .await; + stream_closer.wait_for_ready().await; - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; - - amal_group.send("hello1".as_bytes().to_vec()).await.unwrap(); - amal_group.send("hello2".as_bytes().to_vec()).await.unwrap(); + amal_group.send(b"hello1".to_vec()).await.unwrap(); + stream_callback.wait_for_delivery().await.unwrap(); + amal_group.send(b"hello2".to_vec()).await.unwrap(); + stream_callback.wait_for_delivery().await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; assert_eq!(stream_callback.message_count(), 2); assert!(!stream_closer.is_closed()); @@ -1725,29 +3168,30 @@ mod tests { .remove_members_by_inbox_id(vec![bola.inbox_id().clone()]) .await .unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(2000)).await; + stream_callback.wait_for_delivery().await.unwrap(); assert_eq!(stream_callback.message_count(), 3); // Member removal transcript message - - amal_group.send("hello3".as_bytes().to_vec()).await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; + // + amal_group.send(b"hello3".to_vec()).await.unwrap(); + //TODO: could verify with a log message + tokio::time::sleep(std::time::Duration::from_millis(200)).await; assert_eq!(stream_callback.message_count(), 3); // Don't receive messages while removed assert!(!stream_closer.is_closed()); - tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; amal_group .add_members(vec![bola.account_address.clone()]) .await .unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(500)).await; + + // TODO: could check for LOG message with a Eviction error on receive + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; assert_eq!(stream_callback.message_count(), 3); // Don't receive transcript messages while removed amal_group.send("hello4".as_bytes().to_vec()).await.unwrap(); - tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; + stream_callback.wait_for_delivery().await.unwrap(); assert_eq!(stream_callback.message_count(), 4); // Receiving messages again assert!(!stream_closer.is_closed()); - stream_closer.end(); - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + stream_closer.end_and_wait().await.unwrap(); assert!(stream_closer.is_closed()); } @@ -1794,4 +3238,543 @@ mod tests { "The Inviter and added_by_address do not match!" ); } + + // TODO: Test current fails 50% of the time with db locking messages + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_stream_groups_gets_callback_when_streaming_messages() { + let alix = new_test_client().await; + let bo = new_test_client().await; + + // Stream all group messages + let message_callback = RustStreamCallback::default(); + let group_callback = RustStreamCallback::default(); + let stream_groups = bo + .conversations() + .stream(Box::new(group_callback.clone())) + .await; + + let stream_messages = bo + .conversations() + .stream_all_messages(Box::new(message_callback.clone())) + .await; + stream_messages.wait_for_ready().await; + + // Create group and send first message + let alix_group = alix + .conversations() + .create_group( + vec![bo.account_address.clone()], + FfiCreateGroupOptions::default(), + ) + .await + .unwrap(); + group_callback.wait_for_delivery().await.unwrap(); + + alix_group.send("hello1".as_bytes().to_vec()).await.unwrap(); + message_callback.wait_for_delivery().await.unwrap(); + + assert_eq!(group_callback.message_count(), 1); + assert_eq!(message_callback.message_count(), 1); + + stream_messages.end_and_wait().await.unwrap(); + assert!(stream_messages.is_closed()); + + stream_groups.end_and_wait().await.unwrap(); + assert!(stream_groups.is_closed()); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_permissions_show_expected_values() { + let alix = new_test_client().await; + let bo = new_test_client().await; + // Create admin_only group + let admin_only_options = FfiCreateGroupOptions { + permissions: Some(FfiGroupPermissionsOptions::AdminOnly), + ..Default::default() + }; + let alix_group_admin_only = alix + .conversations() + .create_group(vec![bo.account_address.clone()], admin_only_options) + .await + .unwrap(); + + // Verify we can read the expected permissions + let alix_permission_policy_set = alix_group_admin_only + .group_permissions() + .unwrap() + .policy_set() + .unwrap(); + let expected_permission_policy_set = FfiPermissionPolicySet { + add_member_policy: FfiPermissionPolicy::Admin, + remove_member_policy: FfiPermissionPolicy::Admin, + add_admin_policy: FfiPermissionPolicy::SuperAdmin, + remove_admin_policy: FfiPermissionPolicy::SuperAdmin, + update_group_name_policy: FfiPermissionPolicy::Admin, + update_group_description_policy: FfiPermissionPolicy::Admin, + update_group_image_url_square_policy: FfiPermissionPolicy::Admin, + update_group_pinned_frame_url_policy: FfiPermissionPolicy::Admin, + }; + assert_eq!(alix_permission_policy_set, expected_permission_policy_set); + + // Create all_members group + let all_members_options = FfiCreateGroupOptions { + permissions: Some(FfiGroupPermissionsOptions::AllMembers), + ..Default::default() + }; + let alix_group_all_members = alix + .conversations() + .create_group(vec![bo.account_address.clone()], all_members_options) + .await + .unwrap(); + + // Verify we can read the expected permissions + let alix_permission_policy_set = alix_group_all_members + .group_permissions() + .unwrap() + .policy_set() + .unwrap(); + let expected_permission_policy_set = FfiPermissionPolicySet { + add_member_policy: FfiPermissionPolicy::Allow, + remove_member_policy: FfiPermissionPolicy::Admin, + add_admin_policy: FfiPermissionPolicy::SuperAdmin, + remove_admin_policy: FfiPermissionPolicy::SuperAdmin, + update_group_name_policy: FfiPermissionPolicy::Allow, + update_group_description_policy: FfiPermissionPolicy::Allow, + update_group_image_url_square_policy: FfiPermissionPolicy::Allow, + update_group_pinned_frame_url_policy: FfiPermissionPolicy::Allow, + }; + assert_eq!(alix_permission_policy_set, expected_permission_policy_set); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_permissions_updates() { + let alix = new_test_client().await; + let bola = new_test_client().await; + + let admin_only_options = FfiCreateGroupOptions { + permissions: Some(FfiGroupPermissionsOptions::AdminOnly), + ..Default::default() + }; + let alix_group = alix + .conversations() + .create_group(vec![bola.account_address.clone()], admin_only_options) + .await + .unwrap(); + + let alix_group_permissions = alix_group + .group_permissions() + .unwrap() + .policy_set() + .unwrap(); + let expected_permission_policy_set = FfiPermissionPolicySet { + add_member_policy: FfiPermissionPolicy::Admin, + remove_member_policy: FfiPermissionPolicy::Admin, + add_admin_policy: FfiPermissionPolicy::SuperAdmin, + remove_admin_policy: FfiPermissionPolicy::SuperAdmin, + update_group_name_policy: FfiPermissionPolicy::Admin, + update_group_description_policy: FfiPermissionPolicy::Admin, + update_group_image_url_square_policy: FfiPermissionPolicy::Admin, + update_group_pinned_frame_url_policy: FfiPermissionPolicy::Admin, + }; + assert_eq!(alix_group_permissions, expected_permission_policy_set); + + // Let's update the group so that the image url can be updated by anyone + alix_group + .update_permission_policy( + FfiPermissionUpdateType::UpdateMetadata, + FfiPermissionPolicy::Allow, + Some(FfiMetadataField::ImageUrlSquare), + ) + .await + .unwrap(); + alix_group.sync().await.unwrap(); + let alix_group_permissions = alix_group + .group_permissions() + .unwrap() + .policy_set() + .unwrap(); + let new_expected_permission_policy_set = FfiPermissionPolicySet { + add_member_policy: FfiPermissionPolicy::Admin, + remove_member_policy: FfiPermissionPolicy::Admin, + add_admin_policy: FfiPermissionPolicy::SuperAdmin, + remove_admin_policy: FfiPermissionPolicy::SuperAdmin, + update_group_name_policy: FfiPermissionPolicy::Admin, + update_group_description_policy: FfiPermissionPolicy::Admin, + update_group_image_url_square_policy: FfiPermissionPolicy::Allow, + update_group_pinned_frame_url_policy: FfiPermissionPolicy::Admin, + }; + assert_eq!(alix_group_permissions, new_expected_permission_policy_set); + + // Verify that bo can not update the group name + let bola_conversations = bola.conversations(); + let _ = bola_conversations.sync().await; + let bola_groups = bola_conversations + .list(crate::FfiListConversationsOptions { + created_after_ns: None, + created_before_ns: None, + limit: None, + }) + .await + .unwrap(); + + let bola_group = bola_groups.first().unwrap(); + bola_group + .update_group_name("new_name".to_string()) + .await + .unwrap_err(); + + // Verify that bo CAN update the image url + bola_group + .update_group_image_url_square("https://example.com/image.png".to_string()) + .await + .unwrap(); + + // Verify we can read the correct values from the group + bola_group.sync().await.unwrap(); + alix_group.sync().await.unwrap(); + assert_eq!( + bola_group.group_image_url_square().unwrap(), + "https://example.com/image.png" + ); + assert_eq!(bola_group.group_name().unwrap(), ""); + assert_eq!( + alix_group.group_image_url_square().unwrap(), + "https://example.com/image.png" + ); + assert_eq!(alix_group.group_name().unwrap(), ""); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_group_creation_custom_permissions() { + let alix = new_test_client().await; + let bola = new_test_client().await; + + let custom_permissions = FfiPermissionPolicySet { + add_admin_policy: FfiPermissionPolicy::Admin, + remove_admin_policy: FfiPermissionPolicy::Admin, + update_group_name_policy: FfiPermissionPolicy::Admin, + update_group_description_policy: FfiPermissionPolicy::Allow, + update_group_image_url_square_policy: FfiPermissionPolicy::Admin, + update_group_pinned_frame_url_policy: FfiPermissionPolicy::Admin, + add_member_policy: FfiPermissionPolicy::Allow, + remove_member_policy: FfiPermissionPolicy::Deny, + }; + + let create_group_options = FfiCreateGroupOptions { + permissions: Some(FfiGroupPermissionsOptions::CustomPolicy), + group_name: Some("Test Group".to_string()), + group_image_url_square: Some("https://example.com/image.png".to_string()), + group_description: Some("A test group".to_string()), + group_pinned_frame_url: Some("https://example.com/frame.png".to_string()), + custom_permission_policy_set: Some(custom_permissions), + }; + + let alix_group = alix + .conversations() + .create_group(vec![bola.account_address.clone()], create_group_options) + .await + .unwrap(); + + // Verify the group was created with the correct permissions + let group_permissions_policy_set = alix_group + .group_permissions() + .unwrap() + .policy_set() + .unwrap(); + assert_eq!( + group_permissions_policy_set.add_admin_policy, + FfiPermissionPolicy::Admin + ); + assert_eq!( + group_permissions_policy_set.remove_admin_policy, + FfiPermissionPolicy::Admin + ); + assert_eq!( + group_permissions_policy_set.update_group_name_policy, + FfiPermissionPolicy::Admin + ); + assert_eq!( + group_permissions_policy_set.update_group_description_policy, + FfiPermissionPolicy::Allow + ); + assert_eq!( + group_permissions_policy_set.update_group_image_url_square_policy, + FfiPermissionPolicy::Admin + ); + assert_eq!( + group_permissions_policy_set.update_group_pinned_frame_url_policy, + FfiPermissionPolicy::Admin + ); + assert_eq!( + group_permissions_policy_set.add_member_policy, + FfiPermissionPolicy::Allow + ); + assert_eq!( + group_permissions_policy_set.remove_member_policy, + FfiPermissionPolicy::Deny + ); + + // Verify that Bola can not update the group name + let bola_conversations = bola.conversations(); + let _ = bola_conversations.sync().await; + let bola_groups = bola_conversations + .list(crate::FfiListConversationsOptions { + created_after_ns: None, + created_before_ns: None, + limit: None, + }) + .await + .unwrap(); + + let bola_group = bola_groups.first().unwrap(); + bola_group + .update_group_name("new_name".to_string()) + .await + .unwrap_err(); + let result = bola_group + .update_group_name("New Group Name".to_string()) + .await; + assert!(result.is_err()); + + // Verify that Alix can update the group name + let result = alix_group + .update_group_name("New Group Name".to_string()) + .await; + assert!(result.is_ok()); + + // Verify that Bola can update the group description + let result = bola_group + .update_group_description("New Description".to_string()) + .await; + assert!(result.is_ok()); + + // Verify that Alix can not remove bola even though they are a super admin + let result = alix_group + .remove_members(vec![bola.account_address.clone()]) + .await; + assert!(result.is_err()); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_group_creation_custom_permissions_fails_when_invalid() { + let alix = new_test_client().await; + let bola = new_test_client().await; + + // Add / Remove Admin must be Admin or Super Admin or Deny + let custom_permissions_invalid_1 = FfiPermissionPolicySet { + add_admin_policy: FfiPermissionPolicy::Allow, + remove_admin_policy: FfiPermissionPolicy::Admin, + update_group_name_policy: FfiPermissionPolicy::Admin, + update_group_description_policy: FfiPermissionPolicy::Allow, + update_group_image_url_square_policy: FfiPermissionPolicy::Admin, + update_group_pinned_frame_url_policy: FfiPermissionPolicy::Admin, + add_member_policy: FfiPermissionPolicy::Allow, + remove_member_policy: FfiPermissionPolicy::Deny, + }; + + let custom_permissions_valid = FfiPermissionPolicySet { + add_admin_policy: FfiPermissionPolicy::Admin, + remove_admin_policy: FfiPermissionPolicy::Admin, + update_group_name_policy: FfiPermissionPolicy::Admin, + update_group_description_policy: FfiPermissionPolicy::Allow, + update_group_image_url_square_policy: FfiPermissionPolicy::Admin, + update_group_pinned_frame_url_policy: FfiPermissionPolicy::Admin, + add_member_policy: FfiPermissionPolicy::Allow, + remove_member_policy: FfiPermissionPolicy::Deny, + }; + + let create_group_options_invalid_1 = FfiCreateGroupOptions { + permissions: Some(FfiGroupPermissionsOptions::CustomPolicy), + group_name: Some("Test Group".to_string()), + group_image_url_square: Some("https://example.com/image.png".to_string()), + group_description: Some("A test group".to_string()), + group_pinned_frame_url: Some("https://example.com/frame.png".to_string()), + custom_permission_policy_set: Some(custom_permissions_invalid_1), + }; + + let results_1 = alix + .conversations() + .create_group( + vec![bola.account_address.clone()], + create_group_options_invalid_1, + ) + .await; + + assert!(results_1.is_err()); + + let create_group_options_invalid_2 = FfiCreateGroupOptions { + permissions: Some(FfiGroupPermissionsOptions::AllMembers), + group_name: Some("Test Group".to_string()), + group_image_url_square: Some("https://example.com/image.png".to_string()), + group_description: Some("A test group".to_string()), + group_pinned_frame_url: Some("https://example.com/frame.png".to_string()), + custom_permission_policy_set: Some(custom_permissions_valid.clone()), + }; + + let results_2 = alix + .conversations() + .create_group( + vec![bola.account_address.clone()], + create_group_options_invalid_2, + ) + .await; + + assert!(results_2.is_err()); + + let create_group_options_invalid_3 = FfiCreateGroupOptions { + permissions: None, + group_name: Some("Test Group".to_string()), + group_image_url_square: Some("https://example.com/image.png".to_string()), + group_description: Some("A test group".to_string()), + group_pinned_frame_url: Some("https://example.com/frame.png".to_string()), + custom_permission_policy_set: Some(custom_permissions_valid.clone()), + }; + + let results_3 = alix + .conversations() + .create_group( + vec![bola.account_address.clone()], + create_group_options_invalid_3, + ) + .await; + + assert!(results_3.is_err()); + + let create_group_options_valid = FfiCreateGroupOptions { + permissions: Some(FfiGroupPermissionsOptions::CustomPolicy), + group_name: Some("Test Group".to_string()), + group_image_url_square: Some("https://example.com/image.png".to_string()), + group_description: Some("A test group".to_string()), + group_pinned_frame_url: Some("https://example.com/frame.png".to_string()), + custom_permission_policy_set: Some(custom_permissions_valid), + }; + + let results_4 = alix + .conversations() + .create_group( + vec![bola.account_address.clone()], + create_group_options_valid, + ) + .await; + + assert!(results_4.is_ok()); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_revoke_all_installations() { + let wallet = xmtp_cryptography::utils::LocalWallet::new(&mut rng()); + let client_1 = new_test_client_with_wallet(wallet.clone()).await; + let client_2 = new_test_client_with_wallet(wallet.clone()).await; + + let client_1_state = client_1.inbox_state(true).await.unwrap(); + let client_2_state = client_2.inbox_state(true).await.unwrap(); + assert_eq!(client_1_state.installations.len(), 2); + assert_eq!(client_2_state.installations.len(), 2); + + let signature_request = client_1.revoke_all_other_installations().await.unwrap(); + sign_with_wallet(&wallet, &signature_request).await; + client_1 + .apply_signature_request(signature_request) + .await + .unwrap(); + + let client_1_state_after_revoke = client_1.inbox_state(true).await.unwrap(); + let client_2_state_after_revoke = client_2.inbox_state(true).await.unwrap(); + assert_eq!(client_1_state_after_revoke.installations.len(), 1); + assert_eq!(client_2_state_after_revoke.installations.len(), 1); + assert_eq!( + client_1_state_after_revoke + .installations + .first() + .unwrap() + .id, + client_1.installation_id() + ); + assert_eq!( + client_2_state_after_revoke + .installations + .first() + .unwrap() + .id, + client_1.installation_id() + ); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_set_and_get_group_consent() { + let alix = new_test_client().await; + let bo = new_test_client().await; + + let alix_group = alix + .conversations() + .create_group( + vec![bo.account_address.clone()], + FfiCreateGroupOptions::default(), + ) + .await + .unwrap(); + + let alix_initial_consent = alix_group.consent_state().unwrap(); + assert_eq!(alix_initial_consent, FfiConsentState::Allowed); + + bo.conversations().sync().await.unwrap(); + let bo_group = bo.group(alix_group.id()).unwrap(); + + let bo_initial_consent = bo_group.consent_state().unwrap(); + assert_eq!(bo_initial_consent, FfiConsentState::Unknown); + + alix_group + .update_consent_state(FfiConsentState::Denied) + .unwrap(); + let alix_updated_consent = alix_group.consent_state().unwrap(); + assert_eq!(alix_updated_consent, FfiConsentState::Denied); + bo.set_consent_states(vec![FfiConsent { + state: FfiConsentState::Allowed, + entity_type: FfiConsentEntityType::GroupId, + entity: hex::encode(bo_group.id()), + }]) + .await + .unwrap(); + let bo_updated_consent = bo_group.consent_state().unwrap(); + assert_eq!(bo_updated_consent, FfiConsentState::Allowed); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_set_and_get_member_consent() { + let alix = new_test_client().await; + let bo = new_test_client().await; + + let alix_group = alix + .conversations() + .create_group( + vec![bo.account_address.clone()], + FfiCreateGroupOptions::default(), + ) + .await + .unwrap(); + alix.set_consent_states(vec![FfiConsent { + state: FfiConsentState::Allowed, + entity_type: FfiConsentEntityType::Address, + entity: bo.account_address.clone(), + }]) + .await + .unwrap(); + let bo_consent = alix + .get_consent_state(FfiConsentEntityType::Address, bo.account_address.clone()) + .await + .unwrap(); + assert_eq!(bo_consent, FfiConsentState::Allowed); + + if let Some(member) = alix_group + .list_members() + .await + .unwrap() + .iter() + .find(|&m| m.inbox_id == bo.inbox_id()) + { + assert_eq!(member.consent_state, FfiConsentState::Allowed); + } else { + panic!("Error: No member found with the given inbox_id."); + } + } } diff --git a/bindings_ffi/src/v2.rs b/bindings_ffi/src/v2.rs index 0e7562741..f98a9a803 100644 --- a/bindings_ffi/src/v2.rs +++ b/bindings_ffi/src/v2.rs @@ -8,7 +8,11 @@ use xmtp_proto::api_client::{ use xmtp_proto::xmtp::message_api::v1::IndexCursor; use xmtp_v2::{hashes, k256_helper}; -use xmtp_api_grpc::grpc_api_helper::Client as GrpcClient; +use tokio::{ + sync::{mpsc, Mutex}, + task::{AbortHandle, JoinHandle}, +}; +use xmtp_api_grpc::grpc_api_helper::{Client as GrpcClient, GrpcMutableSubscription}; use xmtp_proto::xmtp::message_api::v1::{ cursor::Cursor as InnerCursor, BatchQueryRequest, Cursor, Envelope, PublishRequest, QueryRequest, SortDirection, @@ -67,7 +71,7 @@ pub struct FfiV2QueryRequest { paging_info: Option, } -#[derive(uniffi::Record)] +#[derive(uniffi::Record, Debug)] pub struct FfiEnvelope { content_topic: String, timestamp_ns: u64, @@ -279,46 +283,96 @@ impl From for SubscribeRequest { } } -#[derive(uniffi::Object)] -pub struct FfiV2Subscription { - inner_subscription: - Arc>, +#[uniffi::export(callback_interface)] +pub trait FfiV2SubscriptionCallback: Send + Sync { + fn on_message(&self, message: FfiEnvelope); } -impl From for FfiV2Subscription { - fn from(subscription: xmtp_api_grpc::grpc_api_helper::GrpcMutableSubscription) -> Self { - Self { - inner_subscription: Arc::new(futures::lock::Mutex::new(subscription)), - } - } +/// Subscription to a stream of V2 Messages +#[derive(uniffi::Object)] +pub struct FfiV2Subscription { + tx: mpsc::Sender, + abort: AbortHandle, + // we require Arc> here because uniffi doesn't like &mut self, or the owned version of self on exported methods + #[allow(clippy::type_complexity)] + handle: Arc>>>, } #[uniffi::export(async_runtime = "tokio")] impl FfiV2Subscription { - pub async fn next(&self) -> Result { - let mut sub = self.inner_subscription.lock().await; - - let result = sub.next().await; - match result { - Some(Ok(envelope)) => Ok(envelope.into()), - Some(Err(err)) => Err(GenericError::Generic { - err: err.to_string(), - }), - None => Err(GenericError::Generic { - err: "stream closed".to_string(), - }), + /// End the subscription, waiting for the subscription to close entirely. + /// # Errors + /// * Errors if subscription event task encounters join error + pub async fn end(&self) -> Result<(), GenericError> { + if self.abort.is_finished() { + return Ok(()); } + + let mut handle = self.handle.lock().await; + let handle = handle.take(); + if let Some(h) = handle { + h.abort(); + let join_result = h.await; + if matches!(join_result, Err(ref e) if !e.is_cancelled()) { + return Err(GenericError::Generic { + err: format!( + "subscription event loop join error {}", + join_result.expect_err("checked for err") + ), + }); + } + } + Ok(()) + } + + /// Check if the subscription is closed + pub fn is_closed(&self) -> bool { + self.abort.is_finished() } + /// Update subscription with new topics pub async fn update(&self, req: FfiV2SubscribeRequest) -> Result<(), GenericError> { - let mut sub = self.inner_subscription.lock().await; - sub.update(req.into()).await?; + self.tx.send(req).await.map_err(|_| GenericError::Generic { + err: "stream closed".into(), + })?; Ok(()) } +} - pub async fn end(&self) { - let sub = self.inner_subscription.lock().await; - sub.close(); +impl FfiV2Subscription { + async fn subscribe( + mut subscription: GrpcMutableSubscription, + callback: Box, + ) -> Self { + let (tx, mut rx): (_, mpsc::Receiver) = mpsc::channel(10); + + let handle = tokio::spawn(async move { + loop { + tokio::select! { + item = subscription.next() => { + match item { + Some(Ok(envelope)) => callback.on_message(envelope.into()), + Some(Err(e)) => log::error!("Stream error {}", e), + None => { + log::debug!("stream closed"); + break; + } + } + }, + update = rx.recv() => { + if let Some(update) = update { + let _ = subscription.update(update.into()).await.map_err(|e| log::error!("{}", e)).ok(); + } + }, + } + } + }); + + Self { + tx, + abort: handle.abort_handle(), + handle: Arc::new(Mutex::new(Some(handle))), + } } } @@ -366,9 +420,10 @@ impl FfiV2ApiClient { pub async fn subscribe( &self, request: FfiV2SubscribeRequest, - ) -> Result, GenericError> { - let result = self.inner_client.subscribe2(request.into()).await?; - Ok(Arc::new(result.into())) + callback: Box, + ) -> Result { + let subscription = self.inner_client.subscribe2(request.into()).await?; + Ok(FfiV2Subscription::subscribe(subscription, callback).await) } } @@ -497,7 +552,11 @@ pub fn verify_k256_sha256( #[cfg(test)] mod tests { - use std::sync::Arc; + use std::sync::{ + atomic::{AtomicU32, Ordering}, + Arc, Mutex, + }; + use tokio::sync::Notify; use futures::stream; use xmtp_proto::api_client::{Envelope, Error as ApiError}; @@ -506,6 +565,24 @@ mod tests { create_v2_client, FfiEnvelope, FfiPublishRequest, FfiV2SubscribeRequest, FfiV2Subscription, }; + use super::FfiV2SubscriptionCallback; + + #[derive(Default, Clone)] + pub struct TestStreamCallback { + message_count: Arc, + messages: Arc>>, + notify: Arc, + } + + impl FfiV2SubscriptionCallback for TestStreamCallback { + fn on_message(&self, message: FfiEnvelope) { + self.message_count.fetch_add(1, Ordering::SeqCst); + let mut messages = self.messages.lock().unwrap(); + messages.push(message); + self.notify.notify_one(); + } + } + // Try a query on a test topic, and make sure we get a response #[tokio::test] async fn test_recover_public_key_keccak256() { @@ -514,7 +591,7 @@ mod tests { let msg = "TestVector1"; let sig_hash = "19d6bec562518e365d07ba3cce26d08a5fffa2cbb1e7fe03c1f2d6a722fd3a5e544097b91f8f8cd11d43b032659f30529139ab1a9ecb6c81ed4a762179e87db81c"; - let sig_bytes = ethers_core::utils::hex::decode(sig_hash).unwrap(); + let sig_bytes = ethers::core::utils::hex::decode(sig_hash).unwrap(); let recovered_addr = crate::v2::recover_address(sig_bytes, msg.to_string()).unwrap(); assert_eq!(recovered_addr, addr.to_lowercase()); } @@ -535,18 +612,24 @@ mod tests { ]; let stream = stream::iter(items); let (tx, _) = futures::channel::mpsc::unbounded(); - let stream_handler = FfiV2Subscription { - inner_subscription: Arc::new(futures::lock::Mutex::new( - xmtp_api_grpc::grpc_api_helper::GrpcMutableSubscription::new(Box::pin(stream), tx), - )), - }; - let first = stream_handler.next().await.unwrap(); - assert_eq!(first.content_topic, "test1"); - let second = stream_handler.next().await.unwrap(); - assert_eq!(second.content_topic, "test2"); - let third = stream_handler.next().await; - assert!(third.is_err()); + let callback = TestStreamCallback::default(); + let local_data = callback.clone(); + FfiV2Subscription::subscribe( + xmtp_api_grpc::grpc_api_helper::GrpcMutableSubscription::new(Box::pin(stream), tx), + Box::new(callback), + ) + .await; + + for _ in 0..2 { + local_data.notify.notified().await; + } + + let messages = local_data.messages.lock().unwrap(); + let message_count = local_data.message_count.clone(); + assert_eq!(message_count.load(Ordering::SeqCst), 2); + assert_eq!(messages[0].content_topic, "test1"); + assert_eq!(messages[1].content_topic, "test2"); } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] @@ -565,19 +648,26 @@ mod tests { ]; let stream = stream::iter(items); let (tx, _) = futures::channel::mpsc::unbounded(); - let stream_handler = FfiV2Subscription { - inner_subscription: Arc::new(futures::lock::Mutex::new( - xmtp_api_grpc::grpc_api_helper::GrpcMutableSubscription::new(Box::pin(stream), tx), - )), - }; - let first = stream_handler.next().await.unwrap(); - assert_eq!(first.content_topic, "test1"); + let callback = TestStreamCallback::default(); + let local_data = callback.clone(); + let sub = FfiV2Subscription::subscribe( + xmtp_api_grpc::grpc_api_helper::GrpcMutableSubscription::new(Box::pin(stream), tx), + Box::new(callback), + ) + .await; + for _ in 0..2 { + local_data.notify.notified().await; + } + + { + let messages = local_data.messages.lock().unwrap(); + assert_eq!(messages[0].content_topic, "test1"); + } // Close the subscription - stream_handler.end().await; - let second = stream_handler.next().await; - assert!(second.is_err()); + sub.end().await.unwrap(); + assert!(sub.is_closed()); } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] @@ -585,12 +675,65 @@ mod tests { let client = create_v2_client("http://localhost:5556".to_string(), false) .await .unwrap(); - let content_topic = "/xmtp/0/foo"; + let content_topic = format!("/xmtp/0/{}", uuid::Uuid::new_v4()); + let callback = TestStreamCallback::default(); + let local_data = callback.clone(); let subscription = client - .subscribe(FfiV2SubscribeRequest { - content_topics: vec![content_topic.to_string()], - }) + .subscribe( + FfiV2SubscribeRequest { + content_topics: vec![content_topic.to_string()], + }, + Box::new(callback), + ) + .await + .unwrap(); + + client + .publish( + FfiPublishRequest { + envelopes: vec![FfiEnvelope { + content_topic: content_topic.to_string(), + timestamp_ns: 3, + message: vec![1, 2, 3], + }], + }, + "".to_string(), + ) + .await + .unwrap(); + + local_data.notify.notified().await; + { + let messages = local_data.messages.lock().unwrap(); + let message_count = local_data.message_count.load(Ordering::SeqCst); + assert_eq!(message_count, 1); + assert_eq!(messages[0].content_topic, content_topic); + assert_eq!(messages[0].timestamp_ns, 3); + assert_eq!(messages[0].message, vec![1, 2, 3]); + } + println!("ENDING SUB"); + let _ = subscription.end().await; + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] + async fn test_update_e2e() { + let client = create_v2_client("http://localhost:5556".to_string(), false) + .await + .unwrap(); + + let content_topic = format!("/xmtp/0/{}", uuid::Uuid::new_v4()); + let other_topic = format!("/xmtp/0/{}", uuid::Uuid::new_v4()); + + let callback = TestStreamCallback::default(); + let local_data = callback.clone(); + let sub = client + .subscribe( + FfiV2SubscribeRequest { + content_topics: vec![content_topic.to_string()], + }, + Box::new(callback), + ) .await .unwrap(); @@ -608,7 +751,46 @@ mod tests { .await .unwrap(); - let sub_result = subscription.next().await.unwrap(); - assert_eq!(sub_result.content_topic, content_topic.to_string()); + local_data.notify.notified().await; + + { + let messages = local_data.messages.lock().unwrap(); + let message_count = local_data.message_count.load(Ordering::SeqCst); + assert_eq!(message_count, 1); + assert_eq!(messages[0].content_topic, content_topic); + assert_eq!(messages[0].timestamp_ns, 3); + assert_eq!(messages[0].message, vec![1, 2, 3]); + } + + sub.update(FfiV2SubscribeRequest { + content_topics: vec![other_topic.to_string()], + }) + .await + .unwrap(); + + client + .publish( + FfiPublishRequest { + envelopes: vec![FfiEnvelope { + content_topic: other_topic.to_string(), + timestamp_ns: 3, + message: vec![1, 2, 3], + }], + }, + "".to_string(), + ) + .await + .unwrap(); + + local_data.notify.notified().await; + + { + let messages = local_data.messages.lock().unwrap(); + let message_count = local_data.message_count.load(Ordering::SeqCst); + assert_eq!(message_count, 2); + assert_eq!(messages[1].content_topic, other_topic); + assert_eq!(messages[1].timestamp_ns, 3); + assert_eq!(messages[1].message, vec![1, 2, 3]); + } } } diff --git a/bindings_node/CHANGELOG.md b/bindings_node/CHANGELOG.md index 45ea538d8..3bc0347e1 100644 --- a/bindings_node/CHANGELOG.md +++ b/bindings_node/CHANGELOG.md @@ -1,5 +1,37 @@ # @xmtp/mls-client-bindings-node +## 0.0.11 + +- Added `inbox_state` to client +- Skip duplicate message processing when streaming + +## 0.0.10 + +- Fixed several group syncing issues +- Improved performance + +## 0.0.9 + +- Added optimistic sending +- Added `policySet` to group permissions +- Added pinned frame url to group metadata + +## 0.0.8 + +- Added description option when creating groups +- Added description getter and setter to group instances +- Fixed DB locking issues +- Fixed invalid policy error +- Removed Admin status from group creators (Super Admin only) + +## 0.0.7 + +- Improved streaming welcomes +- Improved DB retries +- Changed encoding of the MLS database to `bincode` for performance +- Added `find_inbox_id_by_address` to client +- Added `find_group_by_id` and `find_message_by_id` to conversations + ## 0.0.6 - Fixed some group syncing issues diff --git a/bindings_node/Cargo.lock b/bindings_node/Cargo.lock index 6b6e19386..3e95fcbd6 100644 --- a/bindings_node/Cargo.lock +++ b/bindings_node/Cargo.lock @@ -185,18 +185,17 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" -version = "0.6.20" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", "axum-core", - "bitflags 1.3.2", "bytes", "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.28", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", "itoa", "matchit", "memchr", @@ -205,7 +204,7 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper", + "sync_wrapper 1.0.1", "tower", "tower-layer", "tower-service", @@ -213,17 +212,20 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" dependencies = [ "async-trait", "bytes", "futures-util", - "http 0.2.12", - "http-body 0.4.6", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", "mime", + "pin-project-lite", "rustversion", + "sync_wrapper 0.1.2", "tower-layer", "tower-service", ] @@ -285,6 +287,15 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindings_node" version = "0.0.1" @@ -294,10 +305,11 @@ dependencies = [ "napi", "napi-build", "napi-derive", - "prost 0.12.6", + "prost", "rand", "tokio", "tonic", + "tracing", "xmtp_api_grpc", "xmtp_cryptography", "xmtp_id", @@ -836,9 +848,9 @@ dependencies = [ [[package]] name = "diesel" -version = "2.2.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b696af9ff4c0d2a507db2c5faafa8aa0205e297e5f11e203a24226d5355e7a" +checksum = "bf97ee7261bb708fa3402fa9c17a54b70e90e3cb98afb3dc8999d5512cb03f94" dependencies = [ "diesel_derives", "libsqlite3-sys", @@ -1234,7 +1246,7 @@ dependencies = [ "ethers-core", "ethers-etherscan", "eyre", - "prettyplease 0.2.20", + "prettyplease", "proc-macro2", "quote", "regex", @@ -1859,7 +1871,7 @@ dependencies = [ "hpke-rs-crypto", "log", "serde", - "tls_codec 0.4.1", + "tls_codec", "zeroize", ] @@ -1995,6 +2007,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.0", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -2018,14 +2031,15 @@ dependencies = [ [[package]] name = "hyper-timeout" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" dependencies = [ - "hyper 0.14.28", + "hyper 1.3.1", + "hyper-util", "pin-project-lite", "tokio", - "tokio-io-timeout", + "tower-service", ] [[package]] @@ -2211,9 +2225,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -2363,9 +2377,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +checksum = "d4588d65215825ee71ebff9e1c9982067833b1355d7546845ffdb3165cbd7456" dependencies = [ "cc", "openssl-sys", @@ -2464,12 +2478,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - [[package]] name = "multimap" version = "0.10.0" @@ -2671,12 +2679,13 @@ dependencies = [ [[package]] name = "openmls" -version = "0.5.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.6.0-pre.2" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "backtrace", "itertools 0.10.5", "log", + "once_cell", "openmls_basic_credential", "openmls_memory_storage", "openmls_rust_crypto", @@ -2687,27 +2696,27 @@ dependencies = [ "serde", "serde_json", "thiserror", - "tls_codec 0.4.2-pre.1", + "tls_codec", "wasm-bindgen-test", ] [[package]] name = "openmls_basic_credential" -version = "0.2.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.3.0-pre.1" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "ed25519-dalek", "openmls_traits", "p256", "rand", "serde", - "tls_codec 0.4.2-pre.1", + "tls_codec", ] [[package]] name = "openmls_memory_storage" -version = "0.2.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.3.0-pre.2" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "hex", "log", @@ -2719,8 +2728,8 @@ dependencies = [ [[package]] name = "openmls_rust_crypto" -version = "0.2.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.3.0-pre.1" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "aes-gcm", "chacha20poly1305", @@ -2738,13 +2747,13 @@ dependencies = [ "serde", "sha2", "thiserror", - "tls_codec 0.4.2-pre.1", + "tls_codec", ] [[package]] name = "openmls_test" -version = "0.1.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.1.0-pre.1" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "ansi_term", "openmls_rust_crypto", @@ -2758,11 +2767,11 @@ dependencies = [ [[package]] name = "openmls_traits" -version = "0.2.0" -source = "git+https://github.com/xmtp/openmls?rev=99b2d5e7d0e034ac57644395e2194c5a102afb9a#99b2d5e7d0e034ac57644395e2194c5a102afb9a" +version = "0.3.0-pre.2" +source = "git+https://github.com/xmtp/openmls?rev=87e7e257d8eb15d6662b104518becfc75ef6db76#87e7e257d8eb15d6662b104518becfc75ef6db76" dependencies = [ "serde", - "tls_codec 0.4.2-pre.1", + "tls_codec", ] [[package]] @@ -2875,9 +2884,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -2915,19 +2924,9 @@ checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" [[package]] name = "pbjson" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048f9ac93c1eab514f9470c4bc8d97ca2a0a236b84f45cc19d69a59fc11467f6" -dependencies = [ - "base64 0.13.1", - "serde", -] - -[[package]] -name = "pbjson" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1030c719b0ec2a2d25a5df729d6cff1acf3cc230bf766f4f97833591f7577b90" +checksum = "c7e6349fa080353f4a597daffd05cb81572a9c031a6d4fff7e504947496fcc68" dependencies = [ "base64 0.21.7", "serde", @@ -2935,55 +2934,28 @@ dependencies = [ [[package]] name = "pbjson-build" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdbb7b706f2afc610f3853550cdbbf6372fd324824a087806bd4480ea4996e24" -dependencies = [ - "heck 0.4.1", - "itertools 0.10.5", - "prost 0.11.9", - "prost-types 0.11.9", -] - -[[package]] -name = "pbjson-build" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2580e33f2292d34be285c5bc3dba5259542b083cfad6037b6d70345f24dcb735" -dependencies = [ - "heck 0.4.1", - "itertools 0.11.0", - "prost 0.12.6", - "prost-types 0.12.6", -] - -[[package]] -name = "pbjson-types" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a88c8d87f99a4ac14325e7a4c24af190fca261956e3b82dd7ed67e77e6c7043" +checksum = "6eea3058763d6e656105d1403cb04e0a41b7bbac6362d413e7c33be0c32279c9" dependencies = [ - "bytes", - "chrono", - "pbjson 0.5.1", - "pbjson-build 0.5.1", - "prost 0.11.9", - "prost-build 0.11.9", - "serde", + "heck 0.5.0", + "itertools 0.13.0", + "prost", + "prost-types", ] [[package]] name = "pbjson-types" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18f596653ba4ac51bdecbb4ef6773bc7f56042dc13927910de1684ad3d32aa12" +checksum = "e54e5e7bfb1652f95bc361d76f3c780d8e526b134b85417e774166ee941f0887" dependencies = [ "bytes", "chrono", - "pbjson 0.6.0", - "pbjson-build 0.6.2", - "prost 0.12.6", - "prost-build 0.12.6", + "pbjson", + "pbjson-build", + "prost", + "prost-build", "serde", ] @@ -3209,16 +3181,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2", - "syn 1.0.109", -] - [[package]] name = "prettyplease" version = "0.2.20" @@ -3288,62 +3250,30 @@ dependencies = [ [[package]] name = "prost" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" -dependencies = [ - "bytes", - "prost-derive 0.11.9", -] - -[[package]] -name = "prost" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" -dependencies = [ - "bytes", - "prost-derive 0.12.6", -] - -[[package]] -name = "prost-build" -version = "0.11.9" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc" dependencies = [ "bytes", - "heck 0.4.1", - "itertools 0.10.5", - "lazy_static", - "log", - "multimap 0.8.3", - "petgraph", - "prettyplease 0.1.25", - "prost 0.11.9", - "prost-types 0.11.9", - "regex", - "syn 1.0.109", - "tempfile", - "which", + "prost-derive", ] [[package]] name = "prost-build" -version = "0.12.6" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" +checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1" dependencies = [ "bytes", "heck 0.5.0", - "itertools 0.12.1", + "itertools 0.13.0", "log", - "multimap 0.10.0", + "multimap", "once_cell", "petgraph", - "prettyplease 0.2.20", - "prost 0.12.6", - "prost-types 0.12.6", + "prettyplease", + "prost", + "prost-types", "regex", "syn 2.0.64", "tempfile", @@ -3351,25 +3281,12 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" -dependencies = [ - "anyhow", - "itertools 0.10.5", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "prost-derive" -version = "0.12.6" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.64", @@ -3377,20 +3294,11 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" -dependencies = [ - "prost 0.11.9", -] - -[[package]] -name = "prost-types" -version = "0.12.6" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +checksum = "cee5168b05f49d4b0ca581206eb14a7b22fafd963efe729ac48eb03266e25cc2" dependencies = [ - "prost 0.12.6", + "prost", ] [[package]] @@ -3555,7 +3463,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-rustls 0.24.1", @@ -3598,7 +3506,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -3780,23 +3688,24 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.4" +version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ "log", + "once_cell", "ring 0.17.8", "rustls-pki-types", - "rustls-webpki 0.102.4", + "rustls-webpki 0.102.7", "subtle", "zeroize", ] [[package]] name = "rustls-native-certs" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ "openssl-probe", "rustls-pemfile 2.1.2", @@ -3830,16 +3739,6 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" -[[package]] -name = "rustls-webpki" -version = "0.100.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" -dependencies = [ - "ring 0.16.20", - "untrusted 0.7.1", -] - [[package]] name = "rustls-webpki" version = "0.101.7" @@ -3852,9 +3751,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.4" +version = "0.102.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56" dependencies = [ "ring 0.17.8", "rustls-pki-types", @@ -4166,17 +4065,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "smart-default" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.64", -] - [[package]] name = "socket2" version = "0.5.7" @@ -4334,6 +4222,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "system-configuration" version = "0.5.1" @@ -4466,17 +4360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e78c9c330f8c85b2bae7c8368f2739157db9991235123aa1b15ef9502bfb6a" dependencies = [ "serde", - "tls_codec_derive 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize", -] - -[[package]] -name = "tls_codec" -version = "0.4.2-pre.1" -source = "git+https://github.com/rustcrypto/formats#cf967322b60b14a0db800c9c2652afdb6964453c" -dependencies = [ - "serde", - "tls_codec_derive 0.4.1 (git+https://github.com/rustcrypto/formats)", + "tls_codec_derive", "zeroize", ] @@ -4491,16 +4375,6 @@ dependencies = [ "syn 2.0.64", ] -[[package]] -name = "tls_codec_derive" -version = "0.4.1" -source = "git+https://github.com/rustcrypto/formats#cf967322b60b14a0db800c9c2652afdb6964453c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.64", -] - [[package]] name = "tokio" version = "1.37.0" @@ -4515,19 +4389,10 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", + "tracing", "windows-sys 0.48.0", ] -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-macros" version = "2.2.0" @@ -4561,11 +4426,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.22.4", + "rustls 0.23.12", "rustls-pki-types", "tokio", ] @@ -4579,6 +4444,7 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", + "tokio-util", ] [[package]] @@ -4656,28 +4522,30 @@ dependencies = [ [[package]] name = "tonic" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.21.7", + "base64 0.22.1", "bytes", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.28", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", "hyper-timeout", + "hyper-util", "percent-encoding", "pin-project", - "prost 0.12.6", + "prost", "rustls-native-certs", "rustls-pemfile 2.1.2", - "rustls-pki-types", + "socket2", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.0", "tokio-stream", "tower", "tower-layer", @@ -4760,6 +4628,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "trait-variant" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.64", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -5043,15 +4922,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-roots" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.3", -] - [[package]] name = "webpki-roots" version = "0.25.4" @@ -5067,18 +4937,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "winapi" version = "0.3.9" @@ -5338,39 +5196,31 @@ dependencies = [ [[package]] name = "xmtp_api_grpc" -version = "0.1.0" +version = "0.0.1" dependencies = [ "async-stream", - "base64 0.21.7", + "base64 0.22.1", "futures", "hex", - "http-body 0.4.6", - "hyper 0.14.28", - "log", - "pbjson 0.5.1", - "pbjson-types 0.5.1", - "prost 0.12.6", + "prost", "serde", "tokio", "tonic", - "tower", "tracing", - "webpki-roots 0.23.1", "xmtp_proto", "xmtp_v2", ] [[package]] name = "xmtp_cryptography" -version = "0.1.0" +version = "0.0.1" dependencies = [ "curve25519-dalek", - "ecdsa 0.15.1", + "ecdsa 0.16.9", "ethers", - "ethers-core", + "getrandom", "hex", - "k256 0.12.0", - "log", + "k256 0.13.3", "rand", "rand_chacha", "rustc-hex", @@ -5378,25 +5228,21 @@ dependencies = [ "sha2", "sha3", "thiserror", + "tracing", ] [[package]] name = "xmtp_id" -version = "0.1.0" +version = "0.0.1" dependencies = [ "async-trait", "chrono", - "ed25519", "ed25519-dalek", "ethers", "futures", "hex", - "log", - "openmls", - "openmls_basic_credential", - "openmls_rust_crypto", "openmls_traits", - "prost 0.12.6", + "prost", "rand", "regex", "rustc-hex", @@ -5412,64 +5258,59 @@ dependencies = [ [[package]] name = "xmtp_mls" -version = "0.1.0" +version = "0.0.1" dependencies = [ - "aes", "aes-gcm", - "async-trait", - "chrono", + "async-stream", + "bincode", "diesel", "diesel_migrations", "ed25519-dalek", - "ethers", - "ethers-core", "futures", "hex", "libsqlite3-sys", - "log", "openmls", "openmls_basic_credential", "openmls_rust_crypto", "openmls_traits", - "prost 0.12.6", + "parking_lot", + "prost", "rand", "reqwest 0.12.4", - "ring 0.17.8", "serde", "serde_json", "sha2", - "smart-default", "thiserror", - "tls_codec 0.4.1", + "tls_codec", "tokio", + "tokio-stream", "toml", "tracing", + "trait-variant", + "xmtp_api_grpc", "xmtp_cryptography", "xmtp_id", "xmtp_proto", - "xmtp_v2", ] [[package]] name = "xmtp_proto" -version = "0.1.0" +version = "0.0.1" dependencies = [ - "async-trait", "futures", - "futures-core", "openmls", - "openmls_basic_credential", - "pbjson 0.6.0", - "pbjson-types 0.6.0", - "prost 0.12.6", - "prost-types 0.12.6", + "pbjson", + "pbjson-types", + "prost", + "prost-types", "serde", "tonic", + "trait-variant", ] [[package]] name = "xmtp_v2" -version = "0.1.0" +version = "0.0.1" dependencies = [ "aes-gcm", "ecdsa 0.15.1", diff --git a/bindings_node/Cargo.toml b/bindings_node/Cargo.toml index 4b4ba232e..b53058bf4 100644 --- a/bindings_node/Cargo.toml +++ b/bindings_node/Cargo.toml @@ -10,21 +10,22 @@ crate-type = ["cdylib"] # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix futures = "0.3.30" hex = "0.4.3" +tracing = { version = "0.1", features = ["release_max_level_debug"] } napi = { version = "2.12.2", default-features = false, features = [ "napi4", "napi6", "async", ] } napi-derive = "2.12.2" -prost = "^0.12" +prost = "^0.13" +rand = "0.8.5" tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread", "time"] } -tonic = { version = "^0.11", features = ["tls"] } +tonic = { version = "^0.12", features = ["tls"] } xmtp_api_grpc = { path = "../xmtp_api_grpc" } xmtp_cryptography = { path = "../xmtp_cryptography" } -xmtp_mls = { path = "../xmtp_mls", features = ["grpc", "native"] } -xmtp_proto = { path = "../xmtp_proto", features = ["proto_full"] } xmtp_id = { path = "../xmtp_id" } -rand = "0.8.5" +xmtp_mls = { path = "../xmtp_mls", features = ["native", "message-history"] } +xmtp_proto = { path = "../xmtp_proto", features = ["proto_full"] } [build-dependencies] napi-build = "2.0.1" diff --git a/bindings_node/package.json b/bindings_node/package.json index 9354bb1fc..10cdb0d12 100644 --- a/bindings_node/package.json +++ b/bindings_node/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/mls-client-bindings-node", - "version": "0.0.6", + "version": "0.0.11", "repository": { "type": "git", "url": "git+https://git@github.com/xmtp/libxmtp.git", @@ -35,9 +35,11 @@ "@ianvs/prettier-plugin-sort-imports": "^4.2.1", "@napi-rs/cli": "^3.0.0-alpha.55", "@types/node": "^20.14.2", + "@types/uuid": "^10", "prettier": "^3.3.2", "prettier-plugin-packagejson": "^2.5.0", "typescript": "^5.4.5", + "uuid": "^10.0.0", "viem": "^2.11.1", "vite": "^5.2.13", "vite-tsconfig-paths": "^4.3.2", diff --git a/bindings_node/src/conversations.rs b/bindings_node/src/conversations.rs index 188f22216..3928c36f0 100644 --- a/bindings_node/src/conversations.rs +++ b/bindings_node/src/conversations.rs @@ -6,14 +6,11 @@ use napi::bindgen_prelude::{Error, Result, Uint8Array}; use napi::threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode}; use napi::JsFunction; use napi_derive::napi; -use xmtp_mls::groups::GroupMetadataOptions; +use xmtp_mls::groups::{GroupMetadataOptions, PreconfiguredPolicies}; use crate::messages::NapiMessage; -use crate::{ - groups::{GroupPermissions, NapiGroup}, - mls_client::RustXmtpClient, - streams::NapiStreamCloser, -}; +use crate::permissions::NapiGroupPermissionsOptions; +use crate::{groups::NapiGroup, mls_client::RustXmtpClient, streams::NapiStreamCloser}; #[napi(object)] pub struct NapiListConversationsOptions { @@ -23,10 +20,13 @@ pub struct NapiListConversationsOptions { } #[napi(object)] +#[derive(Clone)] pub struct NapiCreateGroupOptions { - pub permissions: Option, + pub permissions: Option, pub group_name: Option, pub group_image_url_square: Option, + pub group_description: Option, + pub group_pinned_frame_url: Option, } impl NapiCreateGroupOptions { @@ -34,6 +34,8 @@ impl NapiCreateGroupOptions { GroupMetadataOptions { name: self.group_name, image_url_square: self.group_image_url_square, + description: self.group_description, + pinned_frame_url: self.group_pinned_frame_url, } } } @@ -61,23 +63,36 @@ impl NapiConversations { permissions: None, group_name: None, group_image_url_square: None, + group_description: None, + group_pinned_frame_url: None, }, }; - let group_permissions = options - .permissions - .map(|group_permissions| group_permissions.into()); + let group_permissions = match options.permissions { + Some(NapiGroupPermissionsOptions::AllMembers) => { + Some(PreconfiguredPolicies::AllMembers.to_policy_set()) + } + Some(NapiGroupPermissionsOptions::AdminOnly) => { + Some(PreconfiguredPolicies::AdminsOnly.to_policy_set()) + } + _ => None, + }; - let convo = self - .inner_client - .create_group(group_permissions, options.into_group_metadata_options()) - .map_err(|e| Error::from_reason(format!("ClientError: {}", e)))?; - if !account_addresses.is_empty() { - convo - .add_members(&self.inner_client, account_addresses) + let metadata_options = options.clone().into_group_metadata_options(); + + let convo = if account_addresses.is_empty() { + self + .inner_client + .create_group(group_permissions, metadata_options) + .map_err(|e| Error::from_reason(format!("ClientError: {}", e)))? + } else { + self + .inner_client + .create_group_with_members(account_addresses, group_permissions, metadata_options) .await - .map_err(|e| Error::from_reason(format!("GroupError: {}", e)))?; - } + .map_err(|e| Error::from_reason(format!("ClientError: {}", e)))? + }; + let out = NapiGroup::new( self.inner_client.clone(), convo.group_id, @@ -87,6 +102,34 @@ impl NapiConversations { Ok(out) } + #[napi] + pub fn find_group_by_id(&self, group_id: String) -> Result { + let group_id = hex::decode(group_id).map_err(|e| Error::from_reason(format!("{}", e)))?; + + let group = self + .inner_client + .group(group_id) + .map_err(|e| Error::from_reason(format!("{}", e)))?; + + Ok(NapiGroup::new( + self.inner_client.clone(), + group.group_id, + group.created_at_ns, + )) + } + + #[napi] + pub fn find_message_by_id(&self, message_id: String) -> Result { + let message_id = hex::decode(message_id).map_err(|e| Error::from_reason(format!("{}", e)))?; + + let message = self + .inner_client + .message(message_id) + .map_err(|e| Error::from_reason(format!("{}", e)))?; + + Ok(NapiMessage::from(message)) + } + #[napi] pub async fn process_streamed_welcome_message( &self, @@ -153,9 +196,8 @@ impl NapiConversations { let tsfn: ThreadsafeFunction = callback.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value]))?; let client = self.inner_client.clone(); - let stream_closer = RustXmtpClient::stream_conversations_with_callback( - client.clone(), - move |convo| { + let stream_closer = + RustXmtpClient::stream_conversations_with_callback(client.clone(), move |convo| { tsfn.call( Ok(NapiGroup::new( client.clone(), @@ -164,32 +206,22 @@ impl NapiConversations { )), ThreadsafeFunctionCallMode::Blocking, ); - }, - || {}, // on_close_callback - ) - .map_err(|e| Error::from_reason(format!("{}", e)))?; + }); - Ok(NapiStreamCloser::new( - stream_closer.close_fn, - stream_closer.is_closed_atomic, - )) + Ok(NapiStreamCloser::new(stream_closer)) } #[napi(ts_args_type = "callback: (err: null | Error, result: NapiMessage) => void")] pub fn stream_all_messages(&self, callback: JsFunction) -> Result { let tsfn: ThreadsafeFunction = callback.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value]))?; - let stream_closer = RustXmtpClient::stream_all_messages_with_callback_sync( + let stream_closer = RustXmtpClient::stream_all_messages_with_callback( self.inner_client.clone(), move |message| { tsfn.call(Ok(message.into()), ThreadsafeFunctionCallMode::Blocking); }, - ) - .map_err(|e| Error::from_reason(format!("{}", e)))?; + ); - Ok(NapiStreamCloser::new( - stream_closer.close_fn, - stream_closer.is_closed_atomic, - )) + Ok(NapiStreamCloser::new(stream_closer)) } } diff --git a/bindings_node/src/groups.rs b/bindings_node/src/groups.rs index 7b4646f4a..0cf62e04a 100644 --- a/bindings_node/src/groups.rs +++ b/bindings_node/src/groups.rs @@ -8,9 +8,8 @@ use napi::{ use xmtp_cryptography::signature::ed25519_public_key_to_address; use xmtp_mls::groups::{ group_metadata::{ConversationType, GroupMetadata}, - group_permissions::GroupMutablePermissions, members::PermissionLevel, - MlsGroup, PreconfiguredPolicies, UpdateAdminListType, + MlsGroup, UpdateAdminListType, }; use xmtp_proto::xmtp::mls::message_contents::EncodedContent; @@ -18,37 +17,15 @@ use crate::{ encoded_content::NapiEncodedContent, messages::{NapiListMessagesOptions, NapiMessage}, mls_client::RustXmtpClient, + permissions::NapiGroupPermissions, streams::NapiStreamCloser, + ErrorWrapper, }; use prost::Message; use napi_derive::napi; -#[napi] -pub enum GroupPermissions { - EveryoneIsAdmin, - GroupCreatorIsAdmin, -} - -impl From for GroupPermissions { - fn from(policy: PreconfiguredPolicies) -> Self { - match policy { - PreconfiguredPolicies::AllMembers => GroupPermissions::EveryoneIsAdmin, - PreconfiguredPolicies::AdminsOnly => GroupPermissions::GroupCreatorIsAdmin, - } - } -} - -impl From for PreconfiguredPolicies { - fn from(permissions: GroupPermissions) -> Self { - match permissions { - GroupPermissions::EveryoneIsAdmin => PreconfiguredPolicies::AllMembers, - GroupPermissions::GroupCreatorIsAdmin => PreconfiguredPolicies::AdminsOnly, - } - } -} - #[napi] pub struct NapiGroupMetadata { inner: GroupMetadata, @@ -86,25 +63,6 @@ pub struct NapiGroupMember { pub permission_level: NapiPermissionLevel, } -#[napi] -pub struct NapiGroupPermissions { - inner: GroupMutablePermissions, -} - -#[napi] -impl NapiGroupPermissions { - #[napi] - pub fn policy_type(&self) -> Result { - Ok( - self - .inner - .preconfigured_policy() - .map_err(|e| Error::from_reason(format!("{}", e)))? - .into(), - ) - } -} - #[derive(Debug)] #[napi] pub struct NapiGroup { @@ -143,10 +101,42 @@ impl NapiGroup { &self.inner_client, ) .await - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .map_err(ErrorWrapper::from)?; Ok(hex::encode(message_id.clone())) } + /// send a message without immediately publishing to the delivery service. + #[napi] + pub fn send_optimistic(&self, encoded_content: NapiEncodedContent) -> Result> { + let encoded_content: EncodedContent = encoded_content.into(); + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + + let id = group + .send_message_optimistic(encoded_content.encode_to_vec().as_slice()) + .map_err(ErrorWrapper::from)?; + + Ok(id) + } + + /// Publish all unpublished messages + #[napi] + pub async fn publish_messages(&self) -> Result<()> { + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + group + .publish_messages(&self.inner_client) + .await + .map_err(ErrorWrapper::from)?; + Ok(()) + } + #[napi] pub async fn sync(&self) -> Result<()> { let group = MlsGroup::new( @@ -158,7 +148,7 @@ impl NapiGroup { group .sync(&self.inner_client) .await - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .map_err(ErrorWrapper::from)?; Ok(()) } @@ -191,7 +181,7 @@ impl NapiGroup { delivery_status, opts.limit, ) - .map_err(|e| Error::from_reason(format!("{}", e)))? + .map_err(ErrorWrapper::from)? .into_iter() .map(|msg| msg.into()) .collect(); @@ -211,15 +201,15 @@ impl NapiGroup { ); let envelope_bytes: Vec = envelope_bytes.deref().to_vec(); let message = group - .process_streamed_group_message(envelope_bytes, self.inner_client.clone()) + .process_streamed_group_message(envelope_bytes, &self.inner_client) .await - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .map_err(ErrorWrapper::from)?; Ok(message.into()) } #[napi] - pub fn list_members(&self) -> Result> { + pub async fn list_members(&self) -> Result> { let group = MlsGroup::new( self.inner_client.context().clone(), self.group_id.clone(), @@ -227,8 +217,9 @@ impl NapiGroup { ); let members: Vec = group - .members() - .map_err(|e| Error::from_reason(format!("{}", e)))? + .members(&self.inner_client) + .await + .map_err(ErrorWrapper::from)? .into_iter() .map(|member| NapiGroupMember { inbox_id: member.inbox_id, @@ -258,8 +249,8 @@ impl NapiGroup { ); let admin_list = group - .admin_list() - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .admin_list(group.mls_provider().map_err(ErrorWrapper::from)?) + .map_err(ErrorWrapper::from)?; Ok(admin_list) } @@ -273,25 +264,21 @@ impl NapiGroup { ); let super_admin_list = group - .super_admin_list() - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .super_admin_list(group.mls_provider().map_err(ErrorWrapper::from)?) + .map_err(ErrorWrapper::from)?; Ok(super_admin_list) } #[napi] pub fn is_admin(&self, inbox_id: String) -> Result { - let admin_list = self - .admin_list() - .map_err(|e| Error::from_reason(format!("{}", e)))?; + let admin_list = self.admin_list().map_err(ErrorWrapper::from)?; Ok(admin_list.contains(&inbox_id)) } #[napi] pub fn is_super_admin(&self, inbox_id: String) -> Result { - let super_admin_list = self - .super_admin_list() - .map_err(|e| Error::from_reason(format!("{}", e)))?; + let super_admin_list = self.super_admin_list().map_err(ErrorWrapper::from)?; Ok(super_admin_list.contains(&inbox_id)) } @@ -306,7 +293,7 @@ impl NapiGroup { group .add_members(&self.inner_client, account_addresses) .await - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .map_err(ErrorWrapper::from)?; Ok(()) } @@ -321,7 +308,7 @@ impl NapiGroup { group .update_admin_list(&self.inner_client, UpdateAdminListType::Add, inbox_id) .await - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .map_err(ErrorWrapper::from)?; Ok(()) } @@ -336,7 +323,7 @@ impl NapiGroup { group .update_admin_list(&self.inner_client, UpdateAdminListType::Remove, inbox_id) .await - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .map_err(ErrorWrapper::from)?; Ok(()) } @@ -351,7 +338,7 @@ impl NapiGroup { group .update_admin_list(&self.inner_client, UpdateAdminListType::AddSuper, inbox_id) .await - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .map_err(ErrorWrapper::from)?; Ok(()) } @@ -370,7 +357,7 @@ impl NapiGroup { inbox_id, ) .await - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .map_err(ErrorWrapper::from)?; Ok(()) } @@ -383,11 +370,9 @@ impl NapiGroup { self.created_at_ns, ); - let permissions = group - .permissions() - .map_err(|e| Error::from_reason(format!("{}", e)))?; + let permissions = group.permissions().map_err(ErrorWrapper::from)?; - Ok(NapiGroupPermissions { inner: permissions }) + Ok(NapiGroupPermissions::new(permissions)) } #[napi] @@ -401,7 +386,7 @@ impl NapiGroup { group .add_members_by_inbox_id(&self.inner_client, inbox_ids) .await - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .map_err(ErrorWrapper::from)?; Ok(()) } @@ -417,7 +402,7 @@ impl NapiGroup { group .remove_members(&self.inner_client, account_addresses) .await - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .map_err(ErrorWrapper::from)?; Ok(()) } @@ -433,7 +418,7 @@ impl NapiGroup { group .remove_members_by_inbox_id(&self.inner_client, inbox_ids) .await - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .map_err(ErrorWrapper::from)?; Ok(()) } @@ -449,7 +434,7 @@ impl NapiGroup { group .update_group_name(&self.inner_client, group_name) .await - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .map_err(ErrorWrapper::from)?; Ok(()) } @@ -463,8 +448,8 @@ impl NapiGroup { ); let group_name = group - .group_name() - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .group_name(group.mls_provider().map_err(ErrorWrapper::from)?) + .map_err(ErrorWrapper::from)?; Ok(group_name) } @@ -480,7 +465,7 @@ impl NapiGroup { group .update_group_image_url_square(&self.inner_client, group_image_url_square) .await - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .map_err(ErrorWrapper::from)?; Ok(()) } @@ -494,12 +479,74 @@ impl NapiGroup { ); let group_image_url_square = group - .group_image_url_square() - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .group_image_url_square(group.mls_provider().map_err(ErrorWrapper::from)?) + .map_err(ErrorWrapper::from)?; Ok(group_image_url_square) } + #[napi] + pub async fn update_group_description(&self, group_description: String) -> Result<()> { + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + + group + .update_group_description(&self.inner_client, group_description) + .await + .map_err(ErrorWrapper::from)?; + + Ok(()) + } + + #[napi] + pub fn group_description(&self) -> Result { + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + + let group_description = group + .group_description(group.mls_provider().map_err(ErrorWrapper::from)?) + .map_err(ErrorWrapper::from)?; + + Ok(group_description) + } + + #[napi] + pub async fn update_group_pinned_frame_url(&self, pinned_frame_url: String) -> Result<()> { + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + + group + .update_group_pinned_frame_url(&self.inner_client, pinned_frame_url) + .await + .map_err(ErrorWrapper::from)?; + + Ok(()) + } + + #[napi] + pub fn group_pinned_frame_url(&self) -> Result { + let group = MlsGroup::new( + self.inner_client.context().clone(), + self.group_id.clone(), + self.created_at_ns, + ); + + let group_pinned_frame_url = group + .group_pinned_frame_url(group.mls_provider().map_err(ErrorWrapper::from)?) + .map_err(|e| Error::from_reason(format!("{}", e)))?; + + Ok(group_pinned_frame_url) + } + #[napi(ts_args_type = "callback: (err: null | Error, result: NapiMessage) => void")] pub fn stream(&self, callback: JsFunction) -> Result { let tsfn: ThreadsafeFunction = @@ -511,13 +558,9 @@ impl NapiGroup { move |message| { tsfn.call(Ok(message.into()), ThreadsafeFunctionCallMode::Blocking); }, - ) - .map_err(|e| Error::from_reason(format!("{}", e)))?; + ); - Ok(NapiStreamCloser::new( - stream_closer.close_fn, - stream_closer.is_closed_atomic, - )) + Ok(stream_closer.into()) } #[napi] @@ -533,9 +576,11 @@ impl NapiGroup { self.created_at_ns, ); - group - .is_active() - .map_err(|e| Error::from_reason(format!("{}", e))) + Ok( + group + .is_active(group.mls_provider().map_err(ErrorWrapper::from)?) + .map_err(ErrorWrapper::from)?, + ) } #[napi] @@ -546,9 +591,7 @@ impl NapiGroup { self.created_at_ns, ); - group - .added_by_inbox_id() - .map_err(|e| Error::from_reason(format!("{}", e))) + Ok(group.added_by_inbox_id().map_err(ErrorWrapper::from)?) } #[napi] @@ -560,8 +603,9 @@ impl NapiGroup { ); let metadata = group - .metadata() - .map_err(|e| Error::from_reason(format!("{}", e)))?; + .metadata(group.mls_provider().map_err(ErrorWrapper::from)?) + .map_err(ErrorWrapper::from)?; + Ok(NapiGroupMetadata { inner: metadata }) } } diff --git a/bindings_node/src/lib.rs b/bindings_node/src/lib.rs index 3084a43f8..0dea07153 100755 --- a/bindings_node/src/lib.rs +++ b/bindings_node/src/lib.rs @@ -1,7 +1,40 @@ #![recursion_limit = "256"] +#![warn(clippy::unwrap_used)] + mod conversations; pub mod encoded_content; mod groups; mod messages; pub mod mls_client; +mod permissions; mod streams; + +use napi::bindgen_prelude::Error; + +/// Wrapper over any error +/// to make most error handling in napi cleaner +#[derive(Debug)] +pub struct ErrorWrapper(E) +where + E: std::error::Error; + +impl std::fmt::Display for ErrorWrapper { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "{}", self.0) + } +} + +impl From for ErrorWrapper +where + T: std::error::Error, +{ + fn from(err: T) -> ErrorWrapper { + ErrorWrapper(err) + } +} + +impl From> for napi::bindgen_prelude::Error { + fn from(e: ErrorWrapper) -> napi::bindgen_prelude::Error { + Error::from_reason(e.to_string()) + } +} diff --git a/bindings_node/src/mls_client.rs b/bindings_node/src/mls_client.rs index 392297ddf..e5000cc61 100644 --- a/bindings_node/src/mls_client.rs +++ b/bindings_node/src/mls_client.rs @@ -1,15 +1,15 @@ use crate::conversations::NapiConversations; -use napi::bindgen_prelude::{BigInt, Error, Result, Uint8Array}; +use napi::bindgen_prelude::{Error, Result, Uint8Array}; use napi_derive::napi; use std::collections::HashMap; use std::ops::Deref; use std::sync::Arc; -use xmtp_api_grpc::grpc_api_helper::Client as TonicApiClient; +use tokio::sync::Mutex; +pub use xmtp_api_grpc::grpc_api_helper::Client as TonicApiClient; use xmtp_cryptography::signature::ed25519_public_key_to_address; -use xmtp_id::associations::generate_inbox_id as xmtp_id_generate_inbox_id; -use xmtp_id::associations::{ - AccountId, MemberIdentifier, RecoverableEcdsaSignature, Signature, SmartContractWalletSignature, -}; +use xmtp_id::associations::builder::SignatureRequest; +use xmtp_id::associations::unverified::UnverifiedSignature; +use xmtp_id::associations::{generate_inbox_id as xmtp_id_generate_inbox_id, AssociationState}; use xmtp_mls::api::ApiClientWrapper; use xmtp_mls::builder::ClientBuilder; use xmtp_mls::identity::IdentityStrategy; @@ -19,10 +19,42 @@ use xmtp_mls::Client as MlsClient; pub type RustXmtpClient = MlsClient; +#[napi] +#[derive(Eq, Hash, PartialEq)] +pub enum NapiSignatureRequestType { + AddWallet, + CreateInbox, + RevokeWallet, + RevokeInstallations, +} + +#[napi(object)] +pub struct NapiInboxState { + pub inbox_id: String, + pub recovery_address: String, + pub installation_ids: Vec, + pub account_addresses: Vec, +} + +impl From for NapiInboxState { + fn from(state: AssociationState) -> Self { + Self { + inbox_id: state.inbox_id().to_string(), + recovery_address: state.recovery_address().to_string(), + installation_ids: state + .installation_ids() + .into_iter() + .map(|id| ed25519_public_key_to_address(id.as_slice())) + .collect(), + account_addresses: state.account_addresses(), + } + } +} + #[napi] pub struct NapiClient { inner_client: Arc, - signatures: HashMap>, + signature_requests: Arc>>, pub account_address: String, } @@ -34,6 +66,7 @@ pub async fn create_client( inbox_id: String, account_address: String, encryption_key: Option, + history_sync_url: Option, ) -> Result { let api_client = TonicApiClient::create(host.clone(), is_secure) .await @@ -62,17 +95,26 @@ pub async fn create_client( None, ); - let xmtp_client = ClientBuilder::new(identity_strategy) - .api_client(api_client) - .store(store) - .build() - .await - .map_err(|e| Error::from_reason(format!("{}", e)))?; + let xmtp_client = match history_sync_url { + Some(url) => ClientBuilder::new(identity_strategy) + .api_client(api_client) + .store(store) + .history_sync_url(&url) + .build() + .await + .map_err(|e| Error::from_reason(format!("{}", e)))?, + None => ClientBuilder::new(identity_strategy) + .api_client(api_client) + .store(store) + .build() + .await + .map_err(|e| Error::from_reason(format!("{}", e)))?, + }; Ok(NapiClient { inner_client: Arc::new(xmtp_client), account_address, - signatures: HashMap::new(), + signature_requests: Arc::new(Mutex::new(HashMap::new())), }) } @@ -135,117 +177,204 @@ impl NapiClient { } #[napi] - pub fn add_ecdsa_signature(&mut self, signature_bytes: Uint8Array) -> Result<()> { + pub async fn register_identity(&self) -> Result<()> { if self.is_registered() { return Err(Error::from_reason( "An identity is already registered with this client", )); } - let signature_text = match self.signature_text() { - Some(text) => text, - None => return Err(Error::from_reason("No signature text found")), - }; + let mut signature_requests = self.signature_requests.lock().await; - let signature = Box::new(RecoverableEcdsaSignature::new( - signature_text, - signature_bytes.deref().to_vec(), - )); + let signature_request = signature_requests + .get(&NapiSignatureRequestType::CreateInbox) + .ok_or(Error::from_reason("No signature request found"))?; - self.signatures.insert( - MemberIdentifier::Address(self.account_address.clone().to_lowercase()), - signature, - ); + self + .inner_client + .register_identity(signature_request.clone()) + .await + .map_err(|e| Error::from_reason(format!("{}", e)))?; + + signature_requests.remove(&NapiSignatureRequestType::CreateInbox); Ok(()) } #[napi] - pub fn add_scw_signature( - &mut self, - signature_bytes: Uint8Array, - chain_id: String, - account_address: String, - chain_rpc_url: String, - block_number: BigInt, - ) -> Result<()> { - if self.is_registered() { - return Err(Error::from_reason( - "An identity is already registered with this client", - )); - } - - let signature_text = match self.signature_text() { - Some(text) => text, - None => return Err(Error::from_reason("No signature text found")), + pub async fn create_inbox_signature_text(&self) -> Result> { + let signature_request = match self.inner_client.identity().signature_request() { + Some(signature_req) => signature_req, + // this should never happen since we're checking for it above in is_registered + None => return Err(Error::from_reason("No signature request found")), }; + let signature_text = signature_request.signature_text(); + let mut signature_requests = self.signature_requests.lock().await; - let account_id = AccountId::new(chain_id, account_address.clone()); + signature_requests.insert(NapiSignatureRequestType::CreateInbox, signature_request); - let signature = Box::new(SmartContractWalletSignature::new( - signature_text, - signature_bytes.deref().to_vec(), - account_id, - chain_rpc_url, - block_number.get_u64().1, - )); + Ok(Some(signature_text)) + } - self.signatures.insert( - MemberIdentifier::Address(account_address.clone().to_lowercase()), - signature, - ); + #[napi] + pub fn conversations(&self) -> NapiConversations { + NapiConversations::new(self.inner_client.clone()) + } + + #[napi] + pub async fn request_history_sync(&self) -> Result<()> { + let _ = self + .inner_client + .send_history_request() + .await + .map_err(|e| Error::from_reason(format!("{}", e))); Ok(()) } #[napi] - pub async fn register_identity(&self) -> Result<()> { - if self.is_registered() { - return Err(Error::from_reason( - "An identity is already registered with this client", - )); - } + pub async fn find_inbox_id_by_address(&self, address: String) -> Result> { + let inbox_id = self + .inner_client + .find_inbox_id_from_address(address) + .await + .map_err(|e| Error::from_reason(format!("{}", e)))?; - if self.signatures.is_empty() { - return Err(Error::from_reason( - "No client signatures found, add at least 1 before registering", - )); - } + Ok(inbox_id) + } - let mut signature_request = match self.inner_client.identity().signature_request() { - Some(signature_req) => signature_req, - // this should never happen since we're checking for it above in is_registered - None => return Err(Error::from_reason("No signature request found")), - }; + /** + * Get the client's inbox state. + * + * If `refresh_from_network` is true, the client will go to the network first to refresh the state. + * Otherwise, the state will be read from the local database. + */ + #[napi] + pub async fn inbox_state(&self, refresh_from_network: bool) -> Result { + let state = self + .inner_client + .inbox_state(refresh_from_network) + .await + .map_err(|e| Error::from_reason(format!("{}", e)))?; + Ok(state.into()) + } - // apply added signatures to the signature request - for signature in self.signatures.values() { - signature_request - .add_signature(signature.clone()) - .await - .map_err(|e| Error::from_reason(format!("{}", e)))?; - } + #[napi] + pub async fn add_wallet_signature_text( + &self, + existing_wallet_address: String, + new_wallet_address: String, + ) -> Result { + let signature_request = self + .inner_client + .associate_wallet( + existing_wallet_address.to_lowercase(), + new_wallet_address.to_lowercase(), + ) + .map_err(|e| Error::from_reason(format!("{}", e)))?; + let signature_text = signature_request.signature_text(); + let mut signature_requests = self.signature_requests.lock().await; - self + signature_requests.insert(NapiSignatureRequestType::AddWallet, signature_request); + + Ok(signature_text) + } + + #[napi] + pub async fn revoke_wallet_signature_text(&self, wallet_address: String) -> Result { + let signature_request = self .inner_client - .register_identity(signature_request) + .revoke_wallets(vec![wallet_address.to_lowercase()]) .await .map_err(|e| Error::from_reason(format!("{}", e)))?; + let signature_text = signature_request.signature_text(); + let mut signature_requests = self.signature_requests.lock().await; - Ok(()) + signature_requests.insert(NapiSignatureRequestType::RevokeWallet, signature_request); + + Ok(signature_text) } #[napi] - pub fn signature_text(&self) -> Option { - self + pub async fn revoke_installations_signature_text(&self) -> Result { + let installation_id = self.inner_client.installation_public_key(); + let inbox_state = self + .inner_client + .inbox_state(true) + .await + .map_err(|e| Error::from_reason(format!("{}", e)))?; + let other_installation_ids = inbox_state + .installation_ids() + .into_iter() + .filter(|id| id != &installation_id) + .collect(); + let signature_request = self .inner_client - .identity() - .signature_request() - .map(|signature_req| signature_req.signature_text()) + .revoke_installations(other_installation_ids) + .await + .map_err(|e| Error::from_reason(format!("{}", e)))?; + let signature_text = signature_request.signature_text(); + let mut signature_requests = self.signature_requests.lock().await; + + signature_requests.insert( + NapiSignatureRequestType::RevokeInstallations, + signature_request, + ); + + Ok(signature_text) } #[napi] - pub fn conversations(&self) -> NapiConversations { - NapiConversations::new(self.inner_client.clone()) + pub async fn add_signature( + &self, + signature_type: NapiSignatureRequestType, + signature_bytes: Uint8Array, + ) -> Result<()> { + let mut signature_requests = self.signature_requests.lock().await; + + if let Some(signature_request) = signature_requests.get_mut(&signature_type) { + let signature = UnverifiedSignature::new_recoverable_ecdsa(signature_bytes.deref().to_vec()); + + signature_request + .add_signature( + signature, + self + .inner_client + .smart_contract_signature_verifier() + .as_ref(), + ) + .await + .map_err(|e| Error::from_reason(format!("{}", e)))?; + } else { + return Err(Error::from_reason("Signature request not found")); + } + + Ok(()) + } + + #[napi] + pub async fn apply_signature_requests(&self) -> Result<()> { + let mut signature_requests = self.signature_requests.lock().await; + + let request_types: Vec = signature_requests.keys().cloned().collect(); + for signature_request_type in request_types { + // ignore the create inbox request since it's applied with register_identity + if signature_request_type == NapiSignatureRequestType::CreateInbox { + continue; + } + + if let Some(signature_request) = signature_requests.get(&signature_request_type) { + self + .inner_client + .apply_signature_request(signature_request.clone()) + .await + .map_err(|e| Error::from_reason(format!("{}", e)))?; + + // remove the signature request after applying it + signature_requests.remove(&signature_request_type); + } + } + + Ok(()) } } diff --git a/bindings_node/src/permissions.rs b/bindings_node/src/permissions.rs new file mode 100644 index 000000000..45c2f12d0 --- /dev/null +++ b/bindings_node/src/permissions.rs @@ -0,0 +1,172 @@ +use napi::bindgen_prelude::{Error, Result}; +use napi_derive::napi; +use xmtp_mls::groups::{ + group_mutable_metadata::MetadataField, + group_permissions::{ + BasePolicies, GroupMutablePermissions, MembershipPolicies, MetadataBasePolicies, + MetadataPolicies, PermissionsBasePolicies, PermissionsPolicies, + }, + intents::{PermissionPolicyOption, PermissionUpdateType}, + PreconfiguredPolicies, +}; + +#[napi] +pub enum NapiGroupPermissionsOptions { + AllMembers, + AdminOnly, + CustomPolicy, +} + +#[napi] +pub enum NapiPermissionUpdateType { + AddMember, + RemoveMember, + AddAdmin, + RemoveAdmin, + UpdateMetadata, +} + +impl From<&NapiPermissionUpdateType> for PermissionUpdateType { + fn from(update_type: &NapiPermissionUpdateType) -> Self { + match update_type { + NapiPermissionUpdateType::AddMember => PermissionUpdateType::AddMember, + NapiPermissionUpdateType::RemoveMember => PermissionUpdateType::RemoveMember, + NapiPermissionUpdateType::AddAdmin => PermissionUpdateType::AddAdmin, + NapiPermissionUpdateType::RemoveAdmin => PermissionUpdateType::RemoveAdmin, + NapiPermissionUpdateType::UpdateMetadata => PermissionUpdateType::UpdateMetadata, + } + } +} + +#[napi] +pub enum NapiPermissionPolicy { + Allow, + Deny, + Admin, + SuperAdmin, + DoesNotExist, + Other, +} + +impl TryInto for NapiPermissionPolicy { + type Error = Error; + + fn try_into(self) -> Result { + match self { + NapiPermissionPolicy::Allow => Ok(PermissionPolicyOption::Allow), + NapiPermissionPolicy::Deny => Ok(PermissionPolicyOption::Deny), + NapiPermissionPolicy::Admin => Ok(PermissionPolicyOption::AdminOnly), + NapiPermissionPolicy::SuperAdmin => Ok(PermissionPolicyOption::SuperAdminOnly), + _ => Err(Error::from_reason("InvalidPermissionPolicyOption")), + } + } +} + +impl From<&MembershipPolicies> for NapiPermissionPolicy { + fn from(policies: &MembershipPolicies) -> Self { + if let MembershipPolicies::Standard(base_policy) = policies { + match base_policy { + BasePolicies::Allow => NapiPermissionPolicy::Allow, + BasePolicies::Deny => NapiPermissionPolicy::Deny, + BasePolicies::AllowSameMember => NapiPermissionPolicy::Other, + BasePolicies::AllowIfAdminOrSuperAdmin => NapiPermissionPolicy::Admin, + BasePolicies::AllowIfSuperAdmin => NapiPermissionPolicy::SuperAdmin, + } + } else { + NapiPermissionPolicy::Other + } + } +} + +impl From<&MetadataPolicies> for NapiPermissionPolicy { + fn from(policies: &MetadataPolicies) -> Self { + if let MetadataPolicies::Standard(base_policy) = policies { + match base_policy { + MetadataBasePolicies::Allow => NapiPermissionPolicy::Allow, + MetadataBasePolicies::Deny => NapiPermissionPolicy::Deny, + MetadataBasePolicies::AllowIfActorAdminOrSuperAdmin => NapiPermissionPolicy::Admin, + MetadataBasePolicies::AllowIfActorSuperAdmin => NapiPermissionPolicy::SuperAdmin, + } + } else { + NapiPermissionPolicy::Other + } + } +} + +impl From<&PermissionsPolicies> for NapiPermissionPolicy { + fn from(policies: &PermissionsPolicies) -> Self { + if let PermissionsPolicies::Standard(base_policy) = policies { + match base_policy { + PermissionsBasePolicies::Deny => NapiPermissionPolicy::Deny, + PermissionsBasePolicies::AllowIfActorAdminOrSuperAdmin => NapiPermissionPolicy::Admin, + PermissionsBasePolicies::AllowIfActorSuperAdmin => NapiPermissionPolicy::SuperAdmin, + } + } else { + NapiPermissionPolicy::Other + } + } +} + +#[napi(object)] +pub struct NapiPermissionPolicySet { + pub add_member_policy: NapiPermissionPolicy, + pub remove_member_policy: NapiPermissionPolicy, + pub add_admin_policy: NapiPermissionPolicy, + pub remove_admin_policy: NapiPermissionPolicy, + pub update_group_name_policy: NapiPermissionPolicy, + pub update_group_description_policy: NapiPermissionPolicy, + pub update_group_image_url_square_policy: NapiPermissionPolicy, + pub update_group_pinned_frame_url_policy: NapiPermissionPolicy, +} + +impl From for NapiGroupPermissionsOptions { + fn from(policy: PreconfiguredPolicies) -> Self { + match policy { + PreconfiguredPolicies::AllMembers => NapiGroupPermissionsOptions::AllMembers, + PreconfiguredPolicies::AdminsOnly => NapiGroupPermissionsOptions::AdminOnly, + } + } +} + +#[napi] +pub struct NapiGroupPermissions { + inner: GroupMutablePermissions, +} + +#[napi] +impl NapiGroupPermissions { + pub fn new(permissions: GroupMutablePermissions) -> Self { + Self { inner: permissions } + } + + #[napi] + pub fn policy_type(&self) -> Result { + if let Ok(preconfigured_policy) = self.inner.preconfigured_policy() { + Ok(preconfigured_policy.into()) + } else { + Ok(NapiGroupPermissionsOptions::CustomPolicy) + } + } + + #[napi] + pub fn policy_set(&self) -> Result { + let policy_set = &self.inner.policies; + let metadata_policy_map = &policy_set.update_metadata_policy; + let get_policy = |field: &str| { + metadata_policy_map + .get(field) + .map(NapiPermissionPolicy::from) + .unwrap_or(NapiPermissionPolicy::DoesNotExist) + }; + Ok(NapiPermissionPolicySet { + add_member_policy: NapiPermissionPolicy::from(&policy_set.add_member_policy), + remove_member_policy: NapiPermissionPolicy::from(&policy_set.remove_member_policy), + add_admin_policy: NapiPermissionPolicy::from(&policy_set.add_admin_policy), + remove_admin_policy: NapiPermissionPolicy::from(&policy_set.remove_admin_policy), + update_group_name_policy: get_policy(MetadataField::GroupName.as_str()), + update_group_description_policy: get_policy(MetadataField::Description.as_str()), + update_group_image_url_square_policy: get_policy(MetadataField::GroupImageUrlSquare.as_str()), + update_group_pinned_frame_url_policy: get_policy(MetadataField::GroupPinnedFrameUrl.as_str()), + }) + } +} diff --git a/bindings_node/src/streams.rs b/bindings_node/src/streams.rs index 0d82382db..cc00d3b6d 100644 --- a/bindings_node/src/streams.rs +++ b/bindings_node/src/streams.rs @@ -1,35 +1,73 @@ -use std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, Mutex, -}; -use tokio::sync::oneshot::Sender; +use napi::bindgen_prelude::Error; +use std::sync::Arc; +use tokio::{sync::Mutex, task::AbortHandle}; +use xmtp_mls::{client::ClientError, subscriptions::StreamHandle}; use napi_derive::napi; #[napi] pub struct NapiStreamCloser { - close_fn: Arc>>>, - is_closed_atomic: Arc, + #[allow(clippy::type_complexity)] + handle: Arc>>>>, + // for convenience, does not require locking mutex. + abort_handle: Arc, } -#[napi] impl NapiStreamCloser { - pub fn new(close_fn: Arc>>>, is_closed_atomic: Arc) -> Self { + pub fn new(handle: StreamHandle>) -> Self { Self { - close_fn, - is_closed_atomic, + abort_handle: Arc::new(handle.handle.abort_handle()), + handle: Arc::new(Mutex::new(Some(handle))), } } +} + +impl From>> for NapiStreamCloser { + fn from(handle: StreamHandle>) -> Self { + NapiStreamCloser::new(handle) + } +} +#[napi] +impl NapiStreamCloser { + /// Signal the stream to end + /// Does not wait for the stream to end. #[napi] pub fn end(&self) { - if let Ok(mut close_fn_option) = self.close_fn.lock() { - let _ = close_fn_option.take().map(|close_fn| close_fn.send(())); + self.abort_handle.abort(); + } + + /// End the stream and `await` for it to shutdown + /// Returns the `Result` of the task. + #[napi] + /// End the stream and asyncronously wait for it to shutdown + pub async fn end_and_wait(&self) -> Result<(), Error> { + if self.abort_handle.is_finished() { + return Ok(()); + } + + let mut stream_handle = self.handle.lock().await; + let stream_handle = stream_handle.take(); + if let Some(h) = stream_handle { + h.handle.abort(); + match h.handle.await { + Err(e) if !e.is_cancelled() => Err(Error::from_reason(format!( + "subscription event loop join error {}", + e + ))), + Err(e) if e.is_cancelled() => Ok(()), + Ok(t) => t.map_err(|e| Error::from_reason(e.to_string())), + Err(e) => Err(Error::from_reason(format!("error joining task {}", e))), + } + } else { + tracing::warn!("subscription already closed"); + Ok(()) } } + /// Checks if this stream is closed #[napi] pub fn is_closed(&self) -> bool { - self.is_closed_atomic.load(Ordering::Relaxed) + self.abort_handle.is_finished() } } diff --git a/bindings_node/test/Client.test.ts b/bindings_node/test/Client.test.ts index 010d89dd2..e0be25722 100644 --- a/bindings_node/test/Client.test.ts +++ b/bindings_node/test/Client.test.ts @@ -1,5 +1,8 @@ +import { v4 } from 'uuid' +import { toBytes } from 'viem' import { describe, expect, it } from 'vitest' import { createClient, createRegisteredClient, createUser } from '@test/helpers' +import { NapiSignatureRequestType } from '../dist' describe('Client', () => { it('should not be registered at first', async () => { @@ -8,7 +11,7 @@ describe('Client', () => { expect(client.isRegistered()).toBe(false) }) - it('should be registered aafter registration', async () => { + it('should be registered after registration', async () => { const user = createUser() // must create 2 clients to get the expected value // this is currently a limitation in the rust implementation as the @@ -24,4 +27,141 @@ describe('Client', () => { const canMessage = await client.canMessage([user.account.address]) expect(canMessage).toEqual({ [user.account.address.toLowerCase()]: true }) }) + + it('should find an inbox ID from an address', async () => { + const user = createUser() + const client = await createRegisteredClient(user) + const inboxId = await client.findInboxIdByAddress(user.account.address) + expect(inboxId).toBe(client.inboxId()) + }) + + it('should return the correct inbox state', async () => { + const user = createUser() + const client = await createRegisteredClient(user) + const inboxState = await client.inboxState(false) + expect(inboxState.inboxId).toBe(client.inboxId()) + expect(inboxState.installationIds).toEqual([client.installationId()]) + expect(inboxState.accountAddresses).toEqual([ + user.account.address.toLowerCase(), + ]) + expect(inboxState.recoveryAddress).toBe(user.account.address.toLowerCase()) + }) + + it('should add a wallet association to the client', async () => { + const user = createUser() + const user2 = createUser() + const client = await createRegisteredClient(user) + const signatureText = await client.addWalletSignatureText( + user.account.address, + user2.account.address + ) + expect(signatureText).toBeDefined() + + // sign message + const signature = await user.wallet.signMessage({ + message: signatureText, + }) + const signature2 = await user2.wallet.signMessage({ + message: signatureText, + }) + + await client.addSignature( + NapiSignatureRequestType.AddWallet, + toBytes(signature) + ) + await client.addSignature( + NapiSignatureRequestType.AddWallet, + toBytes(signature2) + ) + await client.applySignatureRequests() + const inboxState = await client.inboxState(false) + expect(inboxState.accountAddresses.length).toEqual(2) + expect(inboxState.accountAddresses).toContain( + user.account.address.toLowerCase() + ) + expect(inboxState.accountAddresses).toContain( + user2.account.address.toLowerCase() + ) + }) + + it('should revoke a wallet association from the client', async () => { + const user = createUser() + const user2 = createUser() + const client = await createRegisteredClient(user) + const signatureText = await client.addWalletSignatureText( + user.account.address, + user2.account.address + ) + expect(signatureText).toBeDefined() + + // sign message + const signature = await user.wallet.signMessage({ + message: signatureText, + }) + const signature2 = await user2.wallet.signMessage({ + message: signatureText, + }) + + await client.addSignature( + NapiSignatureRequestType.AddWallet, + toBytes(signature) + ) + await client.addSignature( + NapiSignatureRequestType.AddWallet, + toBytes(signature2) + ) + await client.applySignatureRequests() + + const signatureText2 = await client.revokeWalletSignatureText( + user2.account.address + ) + expect(signatureText2).toBeDefined() + + // sign message + const signature3 = await user.wallet.signMessage({ + message: signatureText2, + }) + + await client.addSignature( + NapiSignatureRequestType.RevokeWallet, + toBytes(signature3) + ) + await client.applySignatureRequests() + const inboxState = await client.inboxState(false) + expect(inboxState.accountAddresses).toEqual([ + user.account.address.toLowerCase(), + ]) + }) + + it('should revoke all installations', async () => { + const user = createUser() + + const client = await createRegisteredClient(user) + user.uuid = v4() + const client2 = await createRegisteredClient(user) + user.uuid = v4() + const client3 = await createRegisteredClient(user) + + const inboxState = await client3.inboxState(true) + expect(inboxState.installationIds.length).toEqual(3) + expect(inboxState.installationIds).toContain(client.installationId()) + expect(inboxState.installationIds).toContain(client2.installationId()) + expect(inboxState.installationIds).toContain(client3.installationId()) + + const signatureText = await client3.revokeInstallationsSignatureText() + expect(signatureText).toBeDefined() + + // sign message + const signature = await user.wallet.signMessage({ + message: signatureText, + }) + + await client3.addSignature( + NapiSignatureRequestType.RevokeInstallations, + toBytes(signature) + ) + await client3.applySignatureRequests() + const inboxState2 = await client3.inboxState(true) + expect(inboxState2.installationIds).toEqual([client3.installationId()]) + }) }) diff --git a/bindings_node/test/Conversations.test.ts b/bindings_node/test/Conversations.test.ts index fe2c94290..a631ad19c 100644 --- a/bindings_node/test/Conversations.test.ts +++ b/bindings_node/test/Conversations.test.ts @@ -1,4 +1,3 @@ -import { encode } from 'punycode' import { describe, expect, it } from 'vitest' import { AsyncStream } from '@test/AsyncStream' import { @@ -6,14 +5,14 @@ import { createUser, encodeTextMessage, } from '@test/helpers' -import { GroupPermissions, NapiGroup, NapiMessage } from '../dist' +import { NapiGroup, NapiGroupPermissionsOptions, NapiMessage } from '../dist' describe('Conversations', () => { it('should not have initial conversations', async () => { const user = createUser() const client = await createRegisteredClient(user) - const conversations = client.conversations().list() - expect((await conversations).length).toBe(0) + const conversations = await client.conversations().list() + expect(conversations.length).toBe(0) }) it('should create a new group', async () => { @@ -30,11 +29,21 @@ describe('Conversations', () => { expect(group.isActive()).toBe(true) expect(group.groupName()).toBe('') expect(group.groupPermissions().policyType()).toBe( - GroupPermissions.EveryoneIsAdmin + NapiGroupPermissionsOptions.AllMembers ) + expect(group.groupPermissions().policySet()).toEqual({ + addMemberPolicy: 0, + removeMemberPolicy: 2, + addAdminPolicy: 3, + removeAdminPolicy: 3, + updateGroupNamePolicy: 0, + updateGroupDescriptionPolicy: 0, + updateGroupImageUrlSquarePolicy: 0, + updateGroupPinnedFrameUrlPolicy: 0, + }) expect(group.addedByInboxId()).toBe(client1.inboxId()) expect(group.findMessages().length).toBe(1) - const members = group.listMembers() + const members = await group.listMembers() expect(members.length).toBe(2) const memberInboxIds = members.map((member) => member.inboxId) expect(memberInboxIds).toContain(client1.inboxId()) @@ -55,6 +64,37 @@ describe('Conversations', () => { expect(group2[0].id).toBe(group.id) }) + it('should find a group by ID', async () => { + const user1 = createUser() + const user2 = createUser() + const client1 = await createRegisteredClient(user1) + const client2 = await createRegisteredClient(user2) + const group = await client1 + .conversations() + .createGroup([user2.account.address]) + expect(group).toBeDefined() + expect(group.id()).toBeDefined() + const foundGroup = client1.conversations().findGroupById(group.id()) + expect(foundGroup).toBeDefined() + expect(foundGroup!.id()).toBe(group.id()) + }) + + it('should find a message by ID', async () => { + const user1 = createUser() + const user2 = createUser() + const client1 = await createRegisteredClient(user1) + await createRegisteredClient(user2) + const group = await client1 + .conversations() + .createGroup([user2.account.address]) + const messageId = await group.send(encodeTextMessage('gm!')) + expect(messageId).toBeDefined() + + const message = client1.conversations().findMessageById(messageId) + expect(message).toBeDefined() + expect(message!.id).toBe(messageId) + }) + it('should create a new group with options', async () => { const user1 = createUser() const user2 = createUser() @@ -99,16 +139,72 @@ describe('Conversations', () => { const groupWithPermissions = await client1 .conversations() .createGroup([user4.account.address], { - permissions: GroupPermissions.GroupCreatorIsAdmin, + permissions: NapiGroupPermissionsOptions.AdminOnly, }) expect(groupWithPermissions).toBeDefined() expect(groupWithPermissions.groupName()).toBe('') expect(groupWithPermissions.groupImageUrlSquare()).toBe('') expect(groupWithPermissions.groupPermissions().policyType()).toBe( - GroupPermissions.GroupCreatorIsAdmin + NapiGroupPermissionsOptions.AdminOnly + ) + + expect(groupWithPermissions.groupPermissions().policySet()).toEqual({ + addMemberPolicy: 2, + removeMemberPolicy: 2, + addAdminPolicy: 3, + removeAdminPolicy: 3, + updateGroupNamePolicy: 2, + updateGroupDescriptionPolicy: 2, + updateGroupImageUrlSquarePolicy: 2, + updateGroupPinnedFrameUrlPolicy: 2, + }) + + const groupWithDescription = await client1 + .conversations() + .createGroup([user2.account.address], { + groupDescription: 'foo', + }) + expect(groupWithDescription).toBeDefined() + expect(groupWithDescription.groupName()).toBe('') + expect(groupWithDescription.groupImageUrlSquare()).toBe('') + expect(groupWithDescription.groupDescription()).toBe('foo') + + const groupWithPinnedFrameUrl = await client1 + .conversations() + .createGroup([user2.account.address], { + groupPinnedFrameUrl: 'https://frameurl.xyz', + }) + expect(groupWithPinnedFrameUrl).toBeDefined() + expect(groupWithPinnedFrameUrl.groupName()).toBe('') + expect(groupWithPinnedFrameUrl.groupImageUrlSquare()).toBe('') + expect(groupWithPinnedFrameUrl.groupDescription()).toBe('') + expect(groupWithPinnedFrameUrl.groupPinnedFrameUrl()).toBe( + 'https://frameurl.xyz' ) }) + it('should update group metadata', async () => { + const user1 = createUser() + const user2 = createUser() + const client1 = await createRegisteredClient(user1) + await createRegisteredClient(user2) + const group = await client1 + .conversations() + .createGroup([user2.account.address]) + + await group.updateGroupName('foo') + expect(group.groupName()).toBe('foo') + + await group.updateGroupImageUrlSquare('https://foo/bar.png') + expect(group.groupImageUrlSquare()).toBe('https://foo/bar.png') + + await group.updateGroupDescription('bar') + expect(group.groupDescription()).toBe('bar') + + await group.updateGroupPinnedFrameUrl('https://frameurl.xyz') + expect(group.groupPinnedFrameUrl()).toBe('https://frameurl.xyz') + }) + it('should stream new groups', async () => { const user1 = createUser() const user2 = createUser() diff --git a/bindings_node/test/helpers.ts b/bindings_node/test/helpers.ts index e9336a844..ca96b8721 100644 --- a/bindings_node/test/helpers.ts +++ b/bindings_node/test/helpers.ts @@ -1,5 +1,6 @@ import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' +import { v4 } from 'uuid' import { createWalletClient, http, toBytes } from 'viem' import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts' import { sepolia } from 'viem/chains' @@ -7,6 +8,7 @@ import { createClient as create, generateInboxId, getInboxIdForAddress, + NapiSignatureRequestType, } from '../dist/index' const __dirname = dirname(fileURLToPath(import.meta.url)) @@ -23,13 +25,14 @@ export const createUser = () => { chain: sepolia, transport: http(), }), + uuid: v4(), } } export type User = ReturnType export const createClient = async (user: User) => { - const dbPath = join(__dirname, `${user.account.address}.db3`) + const dbPath = join(__dirname, `${user.uuid}.db3`) const inboxId = (await getInboxIdForAddress(TEST_API_URL, false, user.account.address)) || generateInboxId(user.account.address) @@ -39,12 +42,15 @@ export const createClient = async (user: User) => { export const createRegisteredClient = async (user: User) => { const client = await createClient(user) if (!client.isRegistered()) { - const signatureText = client.signatureText() + const signatureText = await client.createInboxSignatureText() if (signatureText) { const signature = await user.wallet.signMessage({ message: signatureText, }) - client.addEcdsaSignature(toBytes(signature)) + await client.addSignature( + NapiSignatureRequestType.CreateInbox, + toBytes(signature) + ) } await client.registerIdentity() } diff --git a/bindings_node/yarn.lock b/bindings_node/yarn.lock index a8913e6c3..be8e242b9 100644 --- a/bindings_node/yarn.lock +++ b/bindings_node/yarn.lock @@ -1442,6 +1442,13 @@ __metadata: languageName: node linkType: hard +"@types/uuid@npm:^10": + version: 10.0.0 + resolution: "@types/uuid@npm:10.0.0" + checksum: 10/e3958f8b0fe551c86c14431f5940c3470127293280830684154b91dc7eb3514aeb79fe3216968833cf79d4d1c67f580f054b5be2cd562bebf4f728913e73e944 + languageName: node + linkType: hard + "@vitest/expect@npm:1.6.0": version: 1.6.0 resolution: "@vitest/expect@npm:1.6.0" @@ -1503,9 +1510,11 @@ __metadata: "@ianvs/prettier-plugin-sort-imports": "npm:^4.2.1" "@napi-rs/cli": "npm:^3.0.0-alpha.55" "@types/node": "npm:^20.14.2" + "@types/uuid": "npm:^10" prettier: "npm:^3.3.2" prettier-plugin-packagejson: "npm:^2.5.0" typescript: "npm:^5.4.5" + uuid: "npm:^10.0.0" viem: "npm:^2.11.1" vite: "npm:^5.2.13" vite-tsconfig-paths: "npm:^4.3.2" @@ -3882,6 +3891,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^10.0.0": + version: 10.0.0 + resolution: "uuid@npm:10.0.0" + bin: + uuid: dist/bin/uuid + checksum: 10/35aa60614811a201ff90f8ca5e9ecb7076a75c3821e17f0f5ff72d44e36c2d35fcbc2ceee9c4ac7317f4cc41895da30e74f3885e30313bee48fda6338f250538 + languageName: node + linkType: hard + "viem@npm:^2.11.1": version: 2.11.1 resolution: "viem@npm:2.11.1" diff --git a/bindings_wasm/Cargo.toml b/bindings_wasm/Cargo.toml index 01be1d6b2..d8efd3557 100644 --- a/bindings_wasm/Cargo.toml +++ b/bindings_wasm/Cargo.toml @@ -2,28 +2,23 @@ edition = "2021" name = "bindings_wasm" version = "0.1.0" -description = "WASM bindings for the libXMTP rust library" -repository = "https://github.com/xmtp/libxmtp" -license = "MIT" [lib] crate-type = ["cdylib", "rlib"] [dependencies] -async-trait = "0.1.68" -getrandom = { version = "0.2", features = ["js"] } -hex = "0.4" js-sys = "0.3" -prost = { version = "^0.12", features = ["prost-derive"] } -prost-types = "^0.12" +serde = { version = "1.0", features = ["derive"] } +serde-wasm-bindgen = "0.6.5" wasm-bindgen = "0.2.91" wasm-bindgen-futures = "0.4.41" -xmtp_api_grpc_gateway = { path = "../xmtp_api_grpc_gateway" } -xmtp_cryptography = { path = "../xmtp_cryptography", features = ["ws"] } +xmtp_api_http = { path = "../xmtp_api_http" } +xmtp_cryptography = { path = "../xmtp_cryptography" } +xmtp_id = { path = "../xmtp_id" } +xmtp_mls = { path = "../xmtp_mls", features = ["message-history"] } xmtp_proto = { path = "../xmtp_proto", features = ["proto_full"] } [dev-dependencies] -uuid = { version = "1.3.1", features = ["v4"] } wasm-bindgen-test = "0.3.41" [profile.release] diff --git a/bindings_wasm/package-lock.json b/bindings_wasm/package-lock.json index 46f479037..1dcd55649 100644 --- a/bindings_wasm/package-lock.json +++ b/bindings_wasm/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "license": "MIT", "devDependencies": { - "wasm-pack": "^0.12.1" + "wasm-pack": "^0.13.0" }, "engines": { "node": ">=18" @@ -264,9 +264,9 @@ } }, "node_modules/wasm-pack": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.12.1.tgz", - "integrity": "sha512-dIyKWUumPFsGohdndZjDXRFaokUT/kQS+SavbbiXVAvA/eN4riX5QNdB6AhXQx37zNxluxQkuixZUgJ8adKjOg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.13.0.tgz", + "integrity": "sha512-AmboGZEnZoIcVCzSlkLEmNFEqJN+IwgshJ5S7pi30uNUTce4LvWkifQzsQRxnWj47G8gkqZxlyGlyQplsnIS7w==", "dev": true, "hasInstallScript": true, "dependencies": { diff --git a/bindings_wasm/package.json b/bindings_wasm/package.json index 5fe210718..90ef91975 100644 --- a/bindings_wasm/package.json +++ b/bindings_wasm/package.json @@ -24,10 +24,11 @@ }, "scripts": { "build": "npm run clean && npm run build:node && npm run clean:gitignore", - "build:node": "wasm-pack build --target nodejs --no-pack --release --out-dir ./dist/node", + "build:node": "wasm-pack build --target web --no-pack --release --out-dir ./dist", "clean": "rm -rf ./dist", "clean:gitignore": "rm -f ./dist/**/.gitignore", - "prepublishOnly": "npm run build" + "prepublishOnly": "npm run build", + "test": "wasm-pack test --chrome --headless" }, "files": [ "./dist/**/*" @@ -49,6 +50,6 @@ "registry": "https://registry.npmjs.org/" }, "devDependencies": { - "wasm-pack": "^0.12.1" + "wasm-pack": "^0.13.0" } } diff --git a/bindings_wasm/rustfmt.toml b/bindings_wasm/rustfmt.toml new file mode 100644 index 000000000..2bc17ab9a --- /dev/null +++ b/bindings_wasm/rustfmt.toml @@ -0,0 +1,2 @@ +edition = "2021" +tab_spaces = 2 diff --git a/bindings_wasm/src/lib.rs b/bindings_wasm/src/lib.rs index 374800493..9e31f7a45 100644 --- a/bindings_wasm/src/lib.rs +++ b/bindings_wasm/src/lib.rs @@ -1,24 +1 @@ -use wasm_bindgen::prelude::{wasm_bindgen, JsError}; -use xmtp_api_grpc_gateway::XmtpGrpcGatewayClient; - -#[wasm_bindgen] -pub struct WasmXmtpClient { - api: XmtpGrpcGatewayClient, - // inbox_owner: WasmInboxOwner, -} - -impl WasmXmtpClient { - pub fn api(&self) -> &XmtpGrpcGatewayClient { - &self.api - } -} - -#[wasm_bindgen] -impl WasmXmtpClient { - #[wasm_bindgen(constructor)] - pub fn create_client(host_url: String) -> Result { - Ok(WasmXmtpClient { - api: XmtpGrpcGatewayClient::new(host_url), - }) - } -} +pub mod mls_client; diff --git a/bindings_wasm/src/mls_client.rs b/bindings_wasm/src/mls_client.rs new file mode 100644 index 000000000..c84ac166b --- /dev/null +++ b/bindings_wasm/src/mls_client.rs @@ -0,0 +1,266 @@ +use js_sys::Uint8Array; +use std::collections::HashMap; +use std::sync::Arc; +use wasm_bindgen::prelude::{wasm_bindgen, JsError}; +use wasm_bindgen::JsValue; +use xmtp_api_http::XmtpHttpApiClient; +use xmtp_cryptography::signature::ed25519_public_key_to_address; +use xmtp_id::associations::{ + generate_inbox_id as xmtp_id_generate_inbox_id, unverified::UnverifiedSignature, AccountId, + MemberIdentifier, +}; +use xmtp_mls::api::ApiClientWrapper; +use xmtp_mls::builder::ClientBuilder; +use xmtp_mls::identity::IdentityStrategy; +use xmtp_mls::retry::Retry; +use xmtp_mls::storage::{EncryptedMessageStore, EncryptionKey, StorageOption}; +use xmtp_mls::Client as MlsClient; + +pub type RustXmtpClient = MlsClient; + +#[wasm_bindgen] +pub struct WasmClient { + account_address: String, + inner_client: Arc, + signatures: HashMap, +} + +#[wasm_bindgen] +pub async fn create_client( + host: String, + inbox_id: String, + account_address: String, + encryption_key: Option, + history_sync_url: Option, +) -> Result { + let api_client = XmtpHttpApiClient::new(host.clone()).unwrap(); + + let storage_option = StorageOption::Ephemeral; + let store = match encryption_key { + Some(key) => { + let key: Vec = key.to_vec(); + let key: EncryptionKey = key + .try_into() + .map_err(|_| JsError::new("Malformed 32 byte encryption key"))?; + EncryptedMessageStore::new(storage_option, key) + .map_err(|_| JsError::new("Error creating encrypted message store"))? + } + None => EncryptedMessageStore::new_unencrypted(storage_option) + .map_err(|_| JsError::new("Error creating unencrypted message store"))?, + }; + + let identity_strategy = IdentityStrategy::CreateIfNotFound( + inbox_id.clone(), + account_address.clone().to_lowercase(), + // this is a temporary solution + 1, + None, + ); + + let xmtp_client = match history_sync_url { + Some(url) => ClientBuilder::new(identity_strategy) + .api_client(api_client) + .store(store) + .history_sync_url(&url) + .build() + .await + .map_err(|e| JsError::new(format!("{}", e).as_str()))?, + None => ClientBuilder::new(identity_strategy) + .api_client(api_client) + .store(store) + .build() + .await + .map_err(|e| JsError::new(format!("{}", e).as_str()))?, + }; + + Ok(WasmClient { + account_address, + inner_client: Arc::new(xmtp_client), + signatures: HashMap::new(), + }) +} + +#[wasm_bindgen] +pub async fn get_inbox_id_for_address( + host: String, + account_address: String, +) -> Result, JsError> { + let account_address = account_address.to_lowercase(); + let api_client = ApiClientWrapper::new( + XmtpHttpApiClient::new(host.clone()).unwrap(), + Retry::default(), + ); + + let results = api_client + .get_inbox_ids(vec![account_address.clone()]) + .await + .map_err(|e| JsError::new(format!("{}", e).as_str()))?; + + Ok(results.get(&account_address).cloned()) +} + +#[wasm_bindgen] +pub fn generate_inbox_id(account_address: String) -> String { + let account_address = account_address.to_lowercase(); + // ensure that the nonce is always 1 for now since this will only be used for the + // create_client function above, which also has a hard-coded nonce of 1 + xmtp_id_generate_inbox_id(&account_address, &1) +} + +#[wasm_bindgen] +impl WasmClient { + #[wasm_bindgen(getter)] + pub fn account_address(&self) -> String { + self.account_address.clone() + } + + #[wasm_bindgen(getter)] + pub fn inbox_id(&self) -> String { + self.inner_client.inbox_id() + } + + #[wasm_bindgen(getter)] + pub fn is_registered(&self) -> bool { + self.inner_client.identity().signature_request().is_none() + } + + #[wasm_bindgen(getter)] + pub fn installation_id(&self) -> String { + ed25519_public_key_to_address(self.inner_client.installation_public_key().as_slice()) + } + + #[wasm_bindgen] + pub async fn can_message(&self, account_addresses: Vec) -> Result { + let results: HashMap = self + .inner_client + .can_message(account_addresses) + .await + .map_err(|e| JsError::new(format!("{}", e).as_str()))?; + + Ok(serde_wasm_bindgen::to_value(&results)?) + } + + #[wasm_bindgen] + pub fn add_ecdsa_signature(&mut self, signature_bytes: Uint8Array) -> Result<(), JsError> { + if self.is_registered() { + return Err(JsError::new( + "An identity is already registered with this client", + )); + } + + let signature = UnverifiedSignature::new_recoverable_ecdsa(signature_bytes.to_vec()); + + self.signatures.insert( + MemberIdentifier::Address(self.account_address.clone().to_lowercase()), + signature, + ); + + Ok(()) + } + + #[wasm_bindgen] + pub fn add_scw_signature( + &mut self, + signature_bytes: Uint8Array, + chain_id: u64, + account_address: String, + // TODO:nm Remove this + _chain_rpc_url: String, + block_number: u64, + ) -> Result<(), JsError> { + if self.is_registered() { + return Err(JsError::new( + "An identity is already registered with this client", + )); + } + + let account_id = AccountId::new_evm(chain_id, account_address.clone()); + + let signature = UnverifiedSignature::new_smart_contract_wallet( + signature_bytes.to_vec(), + account_id, + block_number, + ); + + self.signatures.insert( + MemberIdentifier::Address(account_address.clone().to_lowercase()), + signature, + ); + + Ok(()) + } + + #[wasm_bindgen] + pub async fn register_identity(&self) -> Result<(), JsError> { + if self.is_registered() { + return Err(JsError::new( + "An identity is already registered with this client", + )); + } + + if self.signatures.is_empty() { + return Err(JsError::new( + "No client signatures found, add at least 1 before registering", + )); + } + + let mut signature_request = match self.inner_client.identity().signature_request() { + Some(signature_req) => signature_req, + // this should never happen since we're checking for it above in is_registered + None => return Err(JsError::new("No signature request found")), + }; + + // apply added signatures to the signature request + for signature in self.signatures.values() { + signature_request + .add_signature( + signature.clone(), + self + .inner_client + .smart_contract_signature_verifier() + .as_ref(), + ) + .await + .map_err(|e| JsError::new(format!("{}", e).as_str()))?; + } + + self + .inner_client + .register_identity(signature_request) + .await + .map_err(|e| JsError::new(format!("{}", e).as_str()))?; + + Ok(()) + } + + #[wasm_bindgen] + pub fn signature_text(&self) -> Option { + self + .inner_client + .identity() + .signature_request() + .map(|signature_req| signature_req.signature_text()) + } + + #[wasm_bindgen] + pub async fn request_history_sync(&self) -> Result<(), JsError> { + let _ = self + .inner_client + .send_history_request() + .await + .map_err(|e| JsError::new(format!("{}", e).as_str()))?; + + Ok(()) + } + + #[wasm_bindgen] + pub async fn find_inbox_id_by_address(&self, address: String) -> Result, JsError> { + let inbox_id = self + .inner_client + .find_inbox_id_from_address(address) + .await + .map_err(|e| JsError::new(format!("{}", e).as_str()))?; + + Ok(inbox_id) + } +} diff --git a/bindings_wasm/tests/node.rs b/bindings_wasm/tests/node.rs deleted file mode 100644 index 86d48a16b..000000000 --- a/bindings_wasm/tests/node.rs +++ /dev/null @@ -1,21 +0,0 @@ -extern crate bindings_wasm; -extern crate wasm_bindgen_test; - -use wasm_bindgen_futures::JsFuture; -use wasm_bindgen::prelude::*; -use wasm_bindgen_test::*; - -#[wasm_bindgen] -extern "C" { - // Use `js_namespace` here to bind `console.log(..)` instead of just `log(..)` - #[wasm_bindgen(js_namespace = console)] - fn log(s: &str); -} - - -#[wasm_bindgen_test] -async fn test_async_works() { - let promise = js_sys::Promise::resolve(&JsValue::from(42)); - let result = JsFuture::from(promise).await.unwrap(); - assert_eq!(result, 42); -} diff --git a/bindings_wasm/tests/web.rs b/bindings_wasm/tests/web.rs index a13cc71a3..2b678accb 100644 --- a/bindings_wasm/tests/web.rs +++ b/bindings_wasm/tests/web.rs @@ -1,86 +1,35 @@ -extern crate bindings_wasm; -extern crate wasm_bindgen_test; -use bindings_wasm::*; -use prost::Message; +use bindings_wasm::mls_client::{create_client, get_inbox_id_for_address}; use wasm_bindgen::prelude::*; use wasm_bindgen_test::*; -use xmtp_cryptography::signature::RecoverableSignature; -use xmtp_proto::api_client::XmtpApiClient; -use xmtp_proto::xmtp::message_api::v1::{ - BatchQueryRequest, Envelope, PublishRequest, QueryRequest, -}; +use xmtp_api_http::constants::ApiUrls; +use xmtp_cryptography::utils::{rng, LocalWallet}; +use xmtp_id::InboxOwner; // Only run these tests in a browser. wasm_bindgen_test_configure!(run_in_browser); #[wasm_bindgen] extern "C" { - // Use `js_namespace` here to bind `console.log(..)` instead of just `log(..)` - #[wasm_bindgen(js_namespace = console)] - fn log(s: &str); + #[wasm_bindgen(js_namespace = console)] + fn log(s: &str); } #[wasm_bindgen_test] -pub async fn test_client_raw_requests() { - let xmtp_url: String = "http://localhost:5555".to_string(); - let client = WasmXmtpClient::create_client(xmtp_url).unwrap_or_else(|error| { - let error_str = format!("{:?}", JsValue::from(error)); - log(&error_str); - panic!("client should be constructed"); - }); - let api = client.api(); - - let topic = uuid::Uuid::new_v4(); - let q = QueryRequest { - content_topics: vec![topic.to_string()], - ..QueryRequest::default() - }; - - let res = api.query(q.clone()).await.expect("successfully queried"); - assert_eq!(0, res.envelopes.len()); - - // Note: this is mostly just to make sure it's all wired up correctly. - // More functionality tests live in the xmtp_api_grpc_gateway crate. -} - -#[wasm_bindgen_test] -pub fn test_xmtp_proto_in_wasm() { - // Use some basic stuff from `xmtp_proto` to test that it works in Wasm. - // TODO: cut once we have a full wasm client to test - - let env1 = Envelope { - timestamp_ns: 12345, - content_topic: "abc123".to_string(), - message: vec![65], - }; - let mut buf = Vec::with_capacity(env1.encoded_len()); - env1.encode(&mut buf).unwrap(); - let env2 = Envelope::decode(buf.as_slice()).unwrap(); - assert_eq!(12345, env2.timestamp_ns); - assert_eq!("abc123".to_string(), env2.content_topic); -} - -#[wasm_bindgen_test] -pub fn test_xmtp_cryptography_in_wasm() { - // (cribbed from `xmtp_cryptography`) tests that we can use it in Wasm. - // TODO: cut once we have a full wasm client to test - - // This test was generated using Etherscans Signature tool: https://etherscan.io/verifySig/18959 - let addr = "0x1B2a516d691aBb8f08a75B2C73c95c62A1632431"; - let msg = "TestVector1"; - let sig_hash = "19d6bec562518e365d07ba3cce26d08a5fffa2cbb1e7fe03c1f2d6a722fd3a5e544097b91f8f8cd11d43b032659f30529139ab1a9ecb6c81ed4a762179e87db81c"; - - let addr_alt = addr.strip_prefix("0x").unwrap(); - let addr_bad = &addr.replacen('b', "c", 1); - let sig_bytes = hex::decode(sig_hash).unwrap(); - let sig = RecoverableSignature::Eip191Signature(sig_bytes); - let msg_bad = "Testvector1"; - - let recovered_addr = sig.recover_address(msg).unwrap(); - assert_eq!(recovered_addr, addr.to_lowercase()); - - assert!(sig.verify_signature(addr, msg).is_ok()); - assert!(sig.verify_signature(addr_alt, msg).is_ok()); - assert!(sig.verify_signature(addr_bad, msg).is_err()); - assert!(sig.verify_signature(addr, msg_bad).is_err()); +pub async fn test_create_client() { + let wallet = LocalWallet::new(&mut rng()); + let account_address = wallet.get_address(); + let host = ApiUrls::LOCAL_ADDRESS.to_string(); + let inbox_id = get_inbox_id_for_address(host.clone(), account_address.clone()) + .await + .unwrap_or_else(|e| panic!("Error getting inbox ID")); + let client = create_client( + host.clone(), + inbox_id.unwrap(), + account_address.clone(), + None, + None, + ) + .await; + + assert!(client.is_ok()); } diff --git a/dev/docker/compose b/dev/docker/compose index 85d74aa91..67c3791ba 100755 --- a/dev/docker/compose +++ b/dev/docker/compose @@ -1,4 +1,4 @@ #!/bin/bash set -eou pipefail -docker-compose -f dev/docker/docker-compose.yml -p "libxmtp" "$@" +docker compose -f dev/docker/docker-compose.yml -p "libxmtp" "$@" diff --git a/dev/validation_service/Dockerfile b/dev/validation_service/Dockerfile index fbb8cf630..6549ab50c 100644 --- a/dev/validation_service/Dockerfile +++ b/dev/validation_service/Dockerfile @@ -4,7 +4,7 @@ COPY . . RUN cargo build --release --package mls_validation_service FROM debian:bullseye-slim -RUN apt-get update && apt-get install -y sqlite3 +RUN apt-get update && apt-get install -y sqlite3 curl COPY --from=builder /code/target/release/mls-validation-service /usr/local/bin/mls-validation-service ENV RUST_LOG=info CMD ["mls-validation-service"] \ No newline at end of file diff --git a/examples/cli/Cargo.toml b/examples/cli/Cargo.toml index 6a17b9680..f53c4b81e 100644 --- a/examples/cli/Cargo.toml +++ b/examples/cli/Cargo.toml @@ -15,12 +15,11 @@ path = "cli-client.rs" [dependencies] clap = { version = "4.4.6", features = ["derive"] } ethers = "2.0.4" -ethers-core = "2.0.4" femme = "2.2.1" -futures = "0.3.28" +futures.workspace = true hex = "0.4.3" kv-log-macro = "1.0.7" -log = { workspace = true, features = [ +log = { version = "0.4", features = [ "kv_unstable", "std", "kv_unstable_serde", @@ -31,9 +30,8 @@ serde_json.workspace = true thiserror.workspace = true timeago = "0.4.1" tokio = "1.28.1" -url = "2.3.1" xmtp_api_grpc = { path = "../../xmtp_api_grpc" } xmtp_cryptography = { path = "../../xmtp_cryptography" } xmtp_id = { path = "../../xmtp_id" } -xmtp_mls = { path = "../../xmtp_mls", features = ["grpc", "native"] } -xmtp_proto = { path = "../../xmtp_proto", features = ["proto_full", "grpc"] } +xmtp_mls = { path = "../../xmtp_mls", features = ["native", "message-history"] } +xmtp_proto = { path = "../../xmtp_proto", features = ["proto_full"] } diff --git a/examples/cli/README.md b/examples/cli/README.md index a9c53e002..c1f701b1d 100644 --- a/examples/cli/README.md +++ b/examples/cli/README.md @@ -55,6 +55,30 @@ Use the CLI to send a [double ratchet message](https://github.com/xmtp/libxmtp/b ./xli.sh --db user2.db3 list-group-messages $GROUP_ID ``` +9. Request a message history sync + + ```bash + ./xli.sh --db user1.db3 request-history-sync + ``` + +10. Reply to the history sync request + + ```bash + ./xli.sh --db user1.db3 reply-to-history-sync-request + ``` + +11. Process the history sync reply + + ```bash + ./xli.sh --db user1.db3 process-history-sync-reply + ``` + +12. List the history sync messages + + ```bash + ./xli.sh --db user1.db3 list-history-sync-messages + ``` + If you want to run the CLI against localhost, go to the root directory and run `dev/up` to start a local server. Then run the CLI commands using the `--local` flag. ## Structured logging diff --git a/examples/cli/cli-client.rs b/examples/cli/cli-client.rs index 14713aafb..6ede48fa3 100755 --- a/examples/cli/cli-client.rs +++ b/examples/cli/cli-client.rs @@ -10,13 +10,17 @@ extern crate ethers; extern crate log; extern crate xmtp_mls; +use std::iter::Iterator; use std::{fs, path::PathBuf, time::Duration}; use clap::{Parser, Subcommand, ValueEnum}; use ethers::signers::{coins_bip39::English, LocalWallet, MnemonicBuilder}; +use futures::future::join_all; use kv_log_macro::{error, info}; use prost::Message; -use xmtp_id::associations::RecoverableEcdsaSignature; +use xmtp_id::associations::unverified::{UnverifiedRecoverableEcdsaSignature, UnverifiedSignature}; +use xmtp_mls::groups::message_history::MessageHistoryContent; +use xmtp_mls::storage::group_message::GroupMessageKind; use crate::{ json_logger::make_value, @@ -34,7 +38,7 @@ use xmtp_mls::{ builder::ClientBuilderError, client::ClientError, codecs::{text::TextCodec, ContentCodec}, - groups::{GroupMetadataOptions, MlsGroup}, + groups::{message_history::MessageHistoryUrls, GroupMetadataOptions, MlsGroup}, identity::IdentityStrategy, storage::{ group_message::StoredGroupMessage, EncryptedMessageStore, EncryptionKey, StorageError, @@ -108,6 +112,10 @@ enum Commands { #[clap(short, long, value_parser, num_args = 1.., value_delimiter = ' ')] account_addresses: Vec, }, + RequestHistorySync {}, + ReplyToHistorySyncRequest {}, + ProcessHistorySyncReply {}, + ListHistorySyncMessages {}, /// Information about the account that owns the DB Info {}, Clear {}, @@ -206,10 +214,12 @@ async fn main() { for group in group_list.iter() { group.sync(&client).await.expect("error syncing group"); } + let serializable_group_list = group_list .iter() - .map(Into::into) - .collect::>(); + .map(|g| SerializableGroup::from(g, &client)) + .collect::>(); + let serializable_group_list = join_all(serializable_group_list).await; info!( "group members", @@ -314,7 +324,10 @@ async fn main() { .unwrap(); let group = client - .create_group(Some(group_permissions), GroupMetadataOptions::default()) + .create_group( + Some(group_permissions.to_policy_set()), + GroupMetadataOptions::default(), + ) .expect("failed to create group"); let group_id = hex::encode(group.group_id); info!("Created group {}", group_id, { command_output: true, group_id: group_id}) @@ -327,9 +340,71 @@ async fn main() { .group(hex::decode(group_id).expect("bad group id")) .expect("group not found"); group.sync(&client).await.unwrap(); - let serializable: SerializableGroup = group.into(); + let serializable = SerializableGroup::from(group, &client).await; info!("Group {}", group_id, { command_output: true, group_id: group_id, group_info: make_value(&serializable) }) } + Commands::RequestHistorySync {} => { + let client = create_client(&cli, IdentityStrategy::CachedOnly) + .await + .unwrap(); + client.sync_welcomes().await.unwrap(); + client.enable_history_sync().await.unwrap(); + let (group_id, _) = client.send_history_request().await.unwrap(); + let group_id_str = hex::encode(group_id); + info!("Sent history sync request in sync group {group_id_str}", { group_id: group_id_str}) + } + Commands::ReplyToHistorySyncRequest {} => { + let client = create_client(&cli, IdentityStrategy::CachedOnly) + .await + .unwrap(); + let group = client.get_sync_group().unwrap(); + let group_id_str = hex::encode(group.group_id); + let reply = client.reply_to_history_request().await.unwrap(); + + info!("Sent history sync reply in sync group {group_id_str}", { group_id: group_id_str}); + info!("Reply: {:?}", reply); + } + Commands::ProcessHistorySyncReply {} => { + let client = create_client(&cli, IdentityStrategy::CachedOnly) + .await + .unwrap(); + client.sync_welcomes().await.unwrap(); + client.enable_history_sync().await.unwrap(); + client.process_history_reply().await.unwrap(); + + info!("History bundle downloaded and inserted into user DB", {}) + } + Commands::ListHistorySyncMessages {} => { + let client = create_client(&cli, IdentityStrategy::CachedOnly) + .await + .unwrap(); + client.sync_welcomes().await.unwrap(); + client.enable_history_sync().await.unwrap(); + let group = client.get_sync_group().unwrap(); + let group_id_str = hex::encode(group.group_id.clone()); + group.sync(&client).await.unwrap(); + let messages = group + .find_messages(Some(GroupMessageKind::Application), None, None, None, None) + .unwrap(); + info!("Listing history sync messages", { group_id: group_id_str, messages: messages.len()}); + for message in messages { + let message_history_content = serde_json::from_slice::( + &message.decrypted_message_bytes, + ); + + match message_history_content { + Ok(MessageHistoryContent::Request(ref request)) => { + info!("Request: {:?}", request); + } + Ok(MessageHistoryContent::Reply(ref reply)) => { + info!("Reply: {:?}", reply); + } + _ => { + info!("Unknown message type: {:?}", message); + } + } + } + } Commands::Clear {} => { fs::remove_file(cli.db.unwrap()).unwrap(); } @@ -342,21 +417,27 @@ async fn create_client(cli: &Cli, account: IdentityStrategy) -> Result) -> Result<(), CliError> { @@ -379,14 +460,17 @@ async fn register(cli: &Cli, maybe_seed_phrase: Option) -> Result<(), Cl ) .await?; let mut signature_request = client.identity().signature_request().unwrap(); - let signature = RecoverableEcdsaSignature::new( - signature_request.signature_text(), - w.sign(signature_request.signature_text().as_str()) - .unwrap() - .into(), - ); + let sig_bytes: Vec = w + .sign(signature_request.signature_text().as_str()) + .unwrap() + .into(); + let signature = + UnverifiedSignature::RecoverableEcdsa(UnverifiedRecoverableEcdsaSignature::new(sig_bytes)); signature_request - .add_signature(Box::new(signature)) + .add_signature( + signature, + client.smart_contract_signature_verifier().as_ref(), + ) .await .unwrap(); @@ -431,7 +515,7 @@ fn format_messages( if text.is_none() { continue; } - // TODO:nm use inbox ID + let sender = if msg.sender_inbox_id == my_account_address { "Me".to_string() } else { diff --git a/examples/cli/serializable.rs b/examples/cli/serializable.rs index f90e840a7..9153e0345 100644 --- a/examples/cli/serializable.rs +++ b/examples/cli/serializable.rs @@ -4,6 +4,7 @@ use xmtp_mls::{ codecs::{text::TextCodec, ContentCodec}, groups::MlsGroup, storage::group_message::StoredGroupMessage, + XmtpApi, }; use xmtp_proto::xmtp::mls::message_contents::EncodedContent; @@ -20,17 +21,27 @@ pub struct SerializableGroup { pub metadata: SerializableGroupMetadata, } -impl<'a> From<&'a MlsGroup> for SerializableGroup { - fn from(group: &'a MlsGroup) -> Self { +impl SerializableGroup { + pub async fn from( + group: &MlsGroup, + client: &xmtp_mls::Client, + ) -> Self { let group_id = hex::encode(group.group_id.clone()); let members = group - .members() + .members(client) + .await .expect("could not load members") .into_iter() .map(|m| m.inbox_id) .collect::>(); - let metadata = group.metadata().expect("could not load metadata"); + let metadata = group + .metadata( + group + .mls_provider() + .expect("MLS Provider could not be created"), + ) + .expect("could not load metadata"); let permissions = group.permissions().expect("could not load permissions"); Self { diff --git a/mls_validation_service/Cargo.toml b/mls_validation_service/Cargo.toml index c562eb88b..2c46aed6a 100644 --- a/mls_validation_service/Cargo.toml +++ b/mls_validation_service/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2021" name = "mls_validation_service" -version = "0.1.0" +version.workspace = true [[bin]] # Bin to run the Validation Service name = "mls-validation-service" @@ -9,35 +9,31 @@ path = "src/main.rs" [dependencies] clap = { version = "4.4.6", features = ["derive"] } -env_logger = "0.10.0" +ed25519-dalek = { workspace = true, features = ["digest"] } +futures = { workspace = true } hex = { workspace = true } -log = { workspace = true } openmls = { workspace = true } openmls_rust_crypto = { workspace = true } openmls_traits = { workspace = true } prost = { workspace = true, features = ["prost-derive"] } serde = { workspace = true } -futures = { workspace = true } +thiserror.workspace = true tokio = { workspace = true, features = ["full"] } tonic = { workspace = true } warp = "0.3.6" +xmtp_id.workspace = true +xmtp_mls.workspace = true xmtp_proto = { path = "../xmtp_proto", features = [ "proto_full", - "grpc", - "tonic", "convert", ] } -xmtp_id.workspace = true -xmtp_mls.workspace = true -thiserror.workspace = true -ed25519-dalek = { workspace = true, features = ["digest"] } - +tracing.workspace = true +tracing-subscriber = { workspace = true, features = ["env-filter"] } [dev-dependencies] -ethers.workspace = true -rand = { workspace = true } -xmtp_id = { workspace = true, features = ["test-utils"]} anyhow.workspace = true -sha2.workspace = true +ethers.workspace = true openmls_basic_credential = { workspace = true, features = ["test-utils"] } - +rand = { workspace = true } +sha2.workspace = true +xmtp_id = { workspace = true, features = ["test-utils"] } diff --git a/mls_validation_service/src/handlers.rs b/mls_validation_service/src/handlers.rs index f1e2128df..07c3145c6 100644 --- a/mls_validation_service/src/handlers.rs +++ b/mls_validation_service/src/handlers.rs @@ -1,17 +1,17 @@ -use futures::future::join_all; -use openmls::{ - credentials::BasicCredential, - prelude::{tls_codec::Deserialize, MlsMessageIn, ProtocolMessage}, -}; +use futures::future::{join_all, try_join_all}; +use openmls::prelude::{tls_codec::Deserialize, MlsMessageIn, ProtocolMessage}; use openmls_rust_crypto::RustCrypto; use tonic::{Request, Response, Status}; -use xmtp_id::associations::{ - self, try_map_vec, AssociationError, DeserializationError, MemberIdentifier, +use xmtp_id::{ + associations::{ + self, try_map_vec, unverified::UnverifiedIdentityUpdate, AssociationError, + DeserializationError, SignatureError, + }, + scw_verifier::SmartContractSignatureVerifier, }; use xmtp_mls::{ utils::id::serialize_group_id, - verified_key_package::VerifiedKeyPackage, verified_key_package_v2::{KeyPackageVerificationError, VerifiedKeyPackageV2}, }; use xmtp_proto::xmtp::{ @@ -19,13 +19,9 @@ use xmtp_proto::xmtp::{ mls_validation::v1::{ validate_group_messages_response::ValidationResponse as ValidateGroupMessageValidationResponse, validate_inbox_id_key_packages_response::Response as ValidateInboxIdKeyPackageResponse, - validate_inbox_ids_request::ValidationRequest as InboxIdValidationRequest, - validate_inbox_ids_response::ValidationResponse as InboxIdValidationResponse, - validate_key_packages_response::ValidationResponse as ValidateKeyPackageValidationResponse, validation_api_server::ValidationApi, GetAssociationStateRequest, GetAssociationStateResponse, ValidateGroupMessagesRequest, ValidateGroupMessagesResponse, - ValidateInboxIdKeyPackagesResponse, ValidateInboxIdsRequest, ValidateInboxIdsResponse, - ValidateKeyPackagesRequest, ValidateKeyPackagesResponse, + ValidateInboxIdKeyPackagesRequest, ValidateInboxIdKeyPackagesResponse, }, }; @@ -35,6 +31,8 @@ pub enum GrpcServerError { Deserialization(#[from] DeserializationError), #[error(transparent)] Association(#[from] AssociationError), + #[error(transparent)] + Signature(#[from] SignatureError), } impl From for Status { @@ -43,46 +41,21 @@ impl From for Status { } } -#[derive(Debug, Default)] -pub struct ValidationService {} - -#[tonic::async_trait] -impl ValidationApi for ValidationService { - async fn validate_key_packages( - &self, - request: Request, - ) -> Result, Status> { - let out: Vec = request - .into_inner() - .key_packages - .into_iter() - .map( - |kp| match validate_key_package(kp.key_package_bytes_tls_serialized) { - Ok(res) => ValidateKeyPackageValidationResponse { - is_ok: true, - error_message: "".to_string(), - installation_id: res.installation_id, - account_address: res.account_address, - credential_identity_bytes: res.credential_identity_bytes, - expiration: res.expiration, - }, - Err(e) => ValidateKeyPackageValidationResponse { - is_ok: false, - error_message: e, - installation_id: vec![], - account_address: "".to_string(), - credential_identity_bytes: vec![], - expiration: 0, - }, - }, - ) - .collect(); +#[derive(Debug)] +pub struct ValidationService { + pub(crate) scw_verifier: Box, +} - Ok(Response::new(ValidateKeyPackagesResponse { - responses: out, - })) +impl ValidationService { + pub fn new(scw_verifier: impl SmartContractSignatureVerifier) -> Self { + Self { + scw_verifier: Box::new(scw_verifier), + } } +} +#[tonic::async_trait] +impl ValidationApi for ValidationService { async fn validate_group_messages( &self, request: Request, @@ -121,7 +94,7 @@ impl ValidationApi for ValidationService { new_updates, } = request.into_inner(); - get_association_state(old_updates, new_updates) + get_association_state(old_updates, new_updates, self.scw_verifier.as_ref()) .await .map(Response::new) .map_err(Into::into) @@ -129,9 +102,9 @@ impl ValidationApi for ValidationService { async fn validate_inbox_id_key_packages( &self, - request: Request, + request: Request, ) -> Result, Status> { - let ValidateKeyPackagesRequest { key_packages } = request.into_inner(); + let ValidateInboxIdKeyPackagesRequest { key_packages } = request.into_inner(); let responses: Vec<_> = key_packages .into_iter() @@ -150,23 +123,6 @@ impl ValidationApi for ValidationService { responses, })) } - - async fn validate_inbox_ids( - &self, - request: Request, - ) -> Result, Status> { - let ValidateInboxIdsRequest { requests } = request.into_inner(); - let responses: Vec<_> = requests.into_iter().map(validate_inbox_id).collect(); - - let responses: Vec = join_all(responses) - .await - .into_iter() - .map(|res| res.map_err(InboxIdValidationResponse::from)) - .map(|r| r.unwrap_or_else(|e| e)) - .collect(); - - Ok(Response::new(ValidateInboxIdsResponse { responses })) - } } #[derive(thiserror::Error, Debug)] @@ -198,136 +154,43 @@ async fn validate_inbox_id_key_package( error_message: "".into(), credential: Some(kp.credential), installation_public_key: kp.installation_public_key, - expiration: kp.inner.life_time().not_after(), - }) -} - -/// Error type for inbox ID validation -/// Each variant requires carrying the ID that failed to validate -/// The error variant itself becomes the failed version of `InboxIdValidationResponse` but allows -/// us to write normal rust in `validate_inbox_id` -#[derive(thiserror::Error, Debug)] -enum InboxIdValidationError { - #[error("Inbox ID {id} failed to validate")] - Deserialization { - id: String, - source: DeserializationError, - }, - #[error("Valid association state could not be found for inbox {id}, {source}")] - Association { - id: String, - source: AssociationError, - }, - #[error("Missing Credential")] - MissingCredential, - #[error("Inbox {id} is not associated with member {member}")] - MemberNotAssociated { - id: String, - member: MemberIdentifier, - }, - #[error( - "Given Inbox Id, {credential_inbox_id} does not match resulting inbox id, {state_inbox_id}" - )] - InboxIdDoesNotMatch { - credential_inbox_id: String, - state_inbox_id: String, - }, -} - -impl InboxIdValidationError { - pub fn inbox_id(&self) -> String { - match self { - InboxIdValidationError::Deserialization { id, .. } => id.clone(), - InboxIdValidationError::MissingCredential => "null".to_string(), - InboxIdValidationError::Association { id, .. } => id.clone(), - InboxIdValidationError::MemberNotAssociated { id, .. } => id.clone(), - InboxIdValidationError::InboxIdDoesNotMatch { - credential_inbox_id, - .. - } => credential_inbox_id.clone(), - } - } -} - -impl From for InboxIdValidationResponse { - fn from(err: InboxIdValidationError) -> Self { - InboxIdValidationResponse { - is_ok: false, - error_message: err.to_string(), - inbox_id: err.inbox_id(), - } - } -} - -async fn validate_inbox_id( - request: InboxIdValidationRequest, -) -> Result { - let InboxIdValidationRequest { - credential, - installation_public_key, - identity_updates, - } = request; - - if credential.is_none() { - return Err(InboxIdValidationError::MissingCredential); - } - - let inbox_id = credential.expect("checked for empty credential").inbox_id; - - let state = associations::get_state(try_map_vec(identity_updates).map_err(|e| { - InboxIdValidationError::Deserialization { - source: e, - id: inbox_id.clone(), - } - })?) - .await - .map_err(|e| InboxIdValidationError::Association { - source: e, - id: inbox_id.clone(), - })?; - - // this is defensive and should not happen. - // The only way an inbox id is different is if xmtp-node-go hands over identity updates with a different inbox id. - // which is a bug. - if state.inbox_id().as_ref() != *inbox_id { - return Err(InboxIdValidationError::InboxIdDoesNotMatch { - credential_inbox_id: inbox_id.clone(), - state_inbox_id: state.inbox_id().clone(), - }); - } - - let member = MemberIdentifier::Installation(installation_public_key); - if state.get(&member).is_none() { - return Err(InboxIdValidationError::MemberNotAssociated { - id: inbox_id, - member, - }); - } - Ok(InboxIdValidationResponse { - is_ok: true, - error_message: "".to_string(), - inbox_id, + // We are deprecating the expiration field and key package lifetimes, so stop checking for its existence + expiration: 0, }) } async fn get_association_state( old_updates: Vec, new_updates: Vec, + scw_verifier: &dyn SmartContractSignatureVerifier, ) -> Result { - let (old_updates, new_updates) = (try_map_vec(old_updates)?, try_map_vec(new_updates)?); - + let old_unverified_updates: Vec = try_map_vec(old_updates)?; + let new_unverified_updates: Vec = try_map_vec(new_updates)?; + + let old_updates = try_join_all( + old_unverified_updates + .iter() + .map(|u| async { u.to_verified(scw_verifier).await }), + ) + .await?; + let new_updates = try_join_all( + new_unverified_updates + .iter() + .map(|u| async { u.to_verified(scw_verifier).await }), + ) + .await?; if old_updates.is_empty() { - let new_state = associations::get_state(&new_updates).await?; + let new_state = associations::get_state(&new_updates)?; return Ok(GetAssociationStateResponse { association_state: Some(new_state.clone().into()), state_diff: Some(new_state.as_diff().into()), }); } - let old_state = associations::get_state(&old_updates).await?; + let old_state = associations::get_state(&old_updates)?; let mut new_state = old_state.clone(); for update in new_updates { - new_state = associations::apply_update(new_state, update).await?; + new_state = associations::apply_update(new_state, update)?; } let state_diff = old_state.diff(&new_state); @@ -355,92 +218,37 @@ fn validate_group_message(message: Vec) -> Result, - account_address: String, - credential_identity_bytes: Vec, - expiration: u64, -} - -fn validate_key_package(key_package_bytes: Vec) -> Result { - let rust_crypto = RustCrypto::default(); - let verified_key_package = - VerifiedKeyPackage::from_bytes(&rust_crypto, key_package_bytes.as_slice()) - .map_err(|e| e.to_string())?; - - let credential = verified_key_package.inner.leaf_node().credential(); - - let basic_credential = - BasicCredential::try_from(credential.clone()).map_err(|e| e.to_string())?; - - Ok(ValidateKeyPackageResult { - installation_id: verified_key_package.installation_id(), - account_address: verified_key_package.account_address, - credential_identity_bytes: basic_credential.identity().to_vec(), - expiration: verified_key_package.inner.life_time().not_after(), - }) -} - #[cfg(test)] mod tests { use ed25519_dalek::SigningKey; use ethers::signers::{LocalWallet, Signer as _}; use openmls::{ extensions::{ApplicationIdExtension, Extension, Extensions}, - prelude::{ - tls_codec::Serialize, Ciphersuite, Credential as OpenMlsCredential, CredentialWithKey, - }, + prelude::{tls_codec::Serialize, Credential as OpenMlsCredential, CredentialWithKey}, prelude_test::KeyPackage, }; use openmls_basic_credential::SignatureKeyPair; use openmls_rust_crypto::OpenMlsRustCrypto; - use prost::Message; - use sha2::{Digest, Sha512}; - use xmtp_id::{ - associations::{ - generate_inbox_id, - unsigned_actions::{ - SignatureTextCreator as _, UnsignedAction, UnsignedAddAssociation, - UnsignedCreateInbox, UnsignedIdentityUpdate, - }, - Action, AddAssociation, CreateInbox, IdentityUpdate, InstallationKeySignature, - RecoverableEcdsaSignature, - }, - constants::INSTALLATION_KEY_SIGNATURE_CONTEXT, + use xmtp_id::associations::{ + generate_inbox_id, + test_utils::{rand_string, rand_u64, MockSmartContractSignatureVerifier}, + unverified::{UnverifiedAction, UnverifiedIdentityUpdate}, }; - use xmtp_mls::{credential::Credential, InboxOwner}; - use xmtp_proto::xmtp::{ - identity::associations::IdentityUpdate as IdentityUpdateProto, - identity::MlsCredential as InboxIdMlsCredential, - mls::message_contents::MlsCredential as CredentialProto, - mls_validation::v1::validate_key_packages_request::KeyPackage as KeyPackageProtoWrapper, + use xmtp_mls::configuration::CIPHERSUITE; + use xmtp_proto::xmtp::identity::{ + associations::IdentityUpdate as IdentityUpdateProto, MlsCredential as InboxIdMlsCredential, }; + use xmtp_proto::xmtp::mls_validation::v1::validate_inbox_id_key_packages_request::KeyPackage as KeyPackageProtoWrapper; use super::*; - const CIPHERSUITE: Ciphersuite = - Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519; - - fn generate_identity() -> (Vec, SignatureKeyPair, String) { - let rng = &mut rand::thread_rng(); - let wallet = LocalWallet::new(rng); - let signature_key_pair = SignatureKeyPair::new(CIPHERSUITE.signature_algorithm()).unwrap(); - - let _pub_key = signature_key_pair.public(); - let account_address = wallet.get_address(); - - let credential = - Credential::create(&signature_key_pair, &wallet).expect("failed to create credential"); - let credential_proto: CredentialProto = credential.into(); - - ( - credential_proto.encode_to_vec(), - signature_key_pair, - account_address, - ) + impl Default for ValidationService { + fn default() -> Self { + Self::new(MockSmartContractSignatureVerifier::new(true)) + } } - async fn generate_inbox_id_credential() -> (String, LocalWallet, SigningKey, CreateInbox) { + async fn generate_inbox_id_credential() -> (String, SigningKey) { let signing_key = SigningKey::generate(&mut rand::thread_rng()); let wallet = LocalWallet::new(&mut rand::thread_rng()); @@ -448,80 +256,7 @@ mod tests { let inbox_id = generate_inbox_id(&address, &0); - let unsigned_action = UnsignedAction::CreateInbox(UnsignedCreateInbox { - nonce: 0, - account_address: address.clone(), - }); - - let update = UnsignedIdentityUpdate { - client_timestamp_ns: 1_000_000u64, - inbox_id: inbox_id.clone(), - actions: vec![unsigned_action], - }; - - let signature = wallet - .sign_message(update.signature_text()) - .await - .unwrap() - .to_vec(); - - let ecdsa_signature = - RecoverableEcdsaSignature::new(update.signature_text(), signature.clone()); - let create = CreateInbox { - nonce: 0, - account_address: address, - initial_address_signature: Box::new(ecdsa_signature), - }; - - (inbox_id, wallet, signing_key, create) - } - - async fn generate_installation_association( - signing_key: &SigningKey, - wallet: LocalWallet, - inbox_id: &str, - ) -> AddAssociation { - let keypair = SignatureKeyPair::from_raw( - CIPHERSUITE.signature_algorithm(), - signing_key.to_bytes().into(), - signing_key.verifying_key().to_bytes().into(), - ); - - let action = UnsignedAction::AddAssociation(UnsignedAddAssociation { - new_member_identifier: MemberIdentifier::Installation(keypair.public().to_vec()), - }); - - let update = UnsignedIdentityUpdate { - client_timestamp_ns: 2_000_000u64, - inbox_id: inbox_id.to_owned(), - actions: vec![action], - }; - - let mut prehashed = Sha512::new(); - prehashed.update(update.signature_text()); - let signature = signing_key - .sign_prehashed(prehashed.clone(), Some(INSTALLATION_KEY_SIGNATURE_CONTEXT)) - .unwrap(); - let existing_member = wallet - .sign_message(update.signature_text()) - .await - .unwrap() - .to_vec(); - - let existing_member = - RecoverableEcdsaSignature::new(update.signature_text(), existing_member); - - let signature = InstallationKeySignature::new( - update.signature_text(), - signature.to_vec(), - signing_key.verifying_key().as_bytes().to_vec(), - ); - - AddAssociation { - new_member_signature: Box::new(signature), - new_member_identifier: MemberIdentifier::Installation(keypair.public().to_vec()), - existing_member_signature: Box::new(existing_member), - } + (inbox_id, signing_key) } fn build_key_package_bytes( @@ -563,84 +298,23 @@ mod tests { ) } - #[tokio::test] - async fn test_validate_key_packages_happy_path() { - let (identity, keypair, account_address) = generate_identity(); - - let credential: OpenMlsCredential = BasicCredential::new(identity).into(); - let credential_with_key = CredentialWithKey { - credential, - signature_key: keypair.to_public_vec().into(), - }; - - let key_package_bytes = build_key_package_bytes( - &keypair, - &credential_with_key, - Some(account_address.clone()), - ); - let request = ValidateKeyPackagesRequest { - key_packages: vec![KeyPackageProtoWrapper { - key_package_bytes_tls_serialized: key_package_bytes, - is_inbox_id_credential: false, - }], - }; - - let res = ValidationService::default() - .validate_key_packages(Request::new(request)) - .await - .unwrap(); - - let first_response = &res.into_inner().responses[0]; - assert_eq!(first_response.installation_id, keypair.public()); - assert_eq!(first_response.account_address, account_address); - assert!(!first_response.credential_identity_bytes.is_empty()); - } - - #[tokio::test] - async fn test_validate_key_packages_fail() { - let (identity, keypair, account_address) = generate_identity(); - let (_, other_keypair, _) = generate_identity(); - - let credential: OpenMlsCredential = BasicCredential::new(identity).into(); - let credential_with_key = CredentialWithKey { - credential, - // Use the wrong signature key to make the validation fail - signature_key: other_keypair.to_public_vec().into(), - }; - - let key_package_bytes = - build_key_package_bytes(&keypair, &credential_with_key, Some(account_address)); - - let request = ValidateKeyPackagesRequest { - key_packages: vec![KeyPackageProtoWrapper { - key_package_bytes_tls_serialized: key_package_bytes, - is_inbox_id_credential: false, - }], - }; - - let res = ValidationService::default() - .validate_key_packages(Request::new(request)) - .await - .unwrap(); - - let first_response = &res.into_inner().responses[0]; - - assert!(!first_response.is_ok); - assert_eq!(first_response.account_address, "".to_string()); - } - // this test will panic until signature recovery is added // and `MockSignature` is updated with signatures that can be recovered #[tokio::test] #[should_panic] async fn test_get_association_state() { - let create_request = CreateInbox::default(); - let inbox_id = generate_inbox_id(&create_request.account_address, &create_request.nonce); - - let updates = vec![IdentityUpdate::new_test( - vec![Action::CreateInbox(create_request)], + let account_address = rand_string(); + let nonce = rand_u64(); + let inbox_id = generate_inbox_id(&account_address, &nonce); + let update = UnverifiedIdentityUpdate::new_test( + vec![UnverifiedAction::new_test_create_inbox( + &account_address, + &nonce, + )], inbox_id.clone(), - )]; + ); + + let updates = vec![update]; ValidationService::default() .get_association_state(Request::new(GetAssociationStateRequest { @@ -656,7 +330,7 @@ mod tests { #[tokio::test] async fn test_validate_inbox_id_key_package_happy_path() { - let (inbox_id, _, keypair, _) = generate_inbox_id_credential().await; + let (inbox_id, keypair) = generate_inbox_id_credential().await; let keypair = to_signature_keypair(keypair); let credential: OpenMlsCredential = InboxIdMlsCredential { @@ -671,7 +345,7 @@ mod tests { }; let key_package_bytes = build_key_package_bytes(&keypair, &credential_with_key, None); - let request = ValidateKeyPackagesRequest { + let request = ValidateInboxIdKeyPackagesRequest { key_packages: vec![KeyPackageProtoWrapper { key_package_bytes_tls_serialized: key_package_bytes, is_inbox_id_credential: false, @@ -694,8 +368,8 @@ mod tests { #[tokio::test] async fn test_validate_inbox_id_key_package_failure() { - let (inbox_id, _, keypair, _) = generate_inbox_id_credential().await; - let (_, _, other_keypair, _) = generate_inbox_id_credential().await; + let (inbox_id, keypair) = generate_inbox_id_credential().await; + let (_, other_keypair) = generate_inbox_id_credential().await; let keypair = to_signature_keypair(keypair); let other_keypair = to_signature_keypair(other_keypair); @@ -712,7 +386,7 @@ mod tests { }; let key_package_bytes = build_key_package_bytes(&keypair, &credential_with_key, None); - let request = ValidateKeyPackagesRequest { + let request = ValidateInboxIdKeyPackagesRequest { key_packages: vec![KeyPackageProtoWrapper { key_package_bytes_tls_serialized: key_package_bytes, is_inbox_id_credential: false, @@ -733,48 +407,4 @@ mod tests { assert_eq!(first_response.credential, None); assert_eq!(first_response.installation_public_key, Vec::::new()); } - - #[tokio::test] - async fn test_validate_inbox_ids_happy_path() { - let (inbox_id, wallet, keypair, create) = generate_inbox_id_credential().await; - let add_association = generate_installation_association(&keypair, wallet, &inbox_id).await; - let keypair = to_signature_keypair(keypair); - - let updates = vec![ - IdentityUpdate::new( - vec![Action::CreateInbox(create)], - inbox_id.clone(), - 1_000_000u64, - ) - .to_proto(), - IdentityUpdate::new( - vec![Action::AddAssociation(add_association)], - inbox_id.clone(), - 2_000_000u64, - ) - .to_proto(), - ]; - - let credential = Some(InboxIdMlsCredential { - inbox_id: inbox_id.clone(), - }); - let request = ValidateInboxIdsRequest { - requests: vec![InboxIdValidationRequest { - credential: credential.clone(), - installation_public_key: keypair.public().to_vec(), - identity_updates: updates.clone(), - }], - }; - - let res = ValidationService::default() - .validate_inbox_ids(Request::new(request)) - .await - .unwrap(); - - let res = &res.into_inner().responses[0]; - - assert!(res.is_ok); - assert_eq!(res.error_message, "".to_string()); - assert_eq!(res.inbox_id, inbox_id); - } } diff --git a/mls_validation_service/src/main.rs b/mls_validation_service/src/main.rs index 0209667e3..db10c2f3a 100644 --- a/mls_validation_service/src/main.rs +++ b/mls_validation_service/src/main.rs @@ -4,21 +4,24 @@ mod health_check; use clap::Parser; use config::Args; -use env_logger::Env; use handlers::ValidationService; use health_check::health_check_server; use tokio::signal::unix::{signal, SignalKind}; use tonic::transport::Server; +use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt as _, EnvFilter}; +use xmtp_id::scw_verifier::RpcSmartContractWalletVerifier; use xmtp_proto::xmtp::mls_validation::v1::validation_api_server::ValidationApiServer; #[macro_use] -extern crate log; +extern crate tracing; #[tokio::main] async fn main() -> Result<(), Box> { - let env = Env::default(); - env_logger::init_from_env(env); + tracing_subscriber::registry() + .with(fmt::layer()) + .with(EnvFilter::from_default_env()) + .init(); let args = Args::parse(); let addr = format!("0.0.0.0:{}", args.port).parse()?; @@ -27,8 +30,13 @@ async fn main() -> Result<(), Box> { let health_server = health_check_server(args.health_check_port as u16); + // TODO:nm replace with real verifier + let scw_verifier = RpcSmartContractWalletVerifier::new("http://fixme.com".to_string()); + let grpc_server = Server::builder() - .add_service(ValidationApiServer::new(ValidationService::default())) + .add_service(ValidationApiServer::new(ValidationService::new( + scw_verifier, + ))) .serve_with_shutdown(addr, async { wait_for_quit().await; info!("Shutdown signal received"); diff --git a/xmtp_api_grpc/Cargo.toml b/xmtp_api_grpc/Cargo.toml index c3b5de304..f0d6b8bac 100644 --- a/xmtp_api_grpc/Cargo.toml +++ b/xmtp_api_grpc/Cargo.toml @@ -1,31 +1,24 @@ [package] edition = "2021" name = "xmtp_api_grpc" -version = "0.1.0" +version.workspace = true [dependencies] -async-stream = "0.3.5" -base64 = "0.21.0" -futures = "0.3.29" +async-stream.workspace = true +base64 = "0.22" +futures.workspace = true hex.workspace = true -http-body = "0.4.5" -hyper = "0.14.26" -log = { workspace = true, features = ["std"] } -pbjson = "0.5.1" -pbjson-types = "0.5.1" prost = { workspace = true, features = ["prost-derive"] } serde = { workspace = true, features = ["derive"] } tokio = { workspace = true, features = ["macros", "rt-multi-thread", "time"] } tonic = { workspace = true, features = [ "tls", - "tls-roots", + "tls-native-roots", "tls-webpki-roots", ] } -tower = "0.4.13" -webpki-roots = "0.23.0" -xmtp_proto = { path = "../xmtp_proto", features = ["proto_full", "grpc"] } -xmtp_v2 = { path = "../xmtp_v2" } tracing.workspace = true +xmtp_proto = { path = "../xmtp_proto", features = ["proto_full"] } +xmtp_v2 = { path = "../xmtp_v2" } [dev-dependencies] uuid = { version = "1.3.1", features = ["v4"] } diff --git a/xmtp_api_grpc/src/grpc_api_helper.rs b/xmtp_api_grpc/src/grpc_api_helper.rs index 6ec010a44..6e61a81ac 100644 --- a/xmtp_api_grpc/src/grpc_api_helper.rs +++ b/xmtp_api_grpc/src/grpc_api_helper.rs @@ -7,12 +7,13 @@ use futures::stream::{AbortHandle, Abortable}; use futures::{SinkExt, Stream, StreamExt, TryStreamExt}; use tokio::sync::oneshot; use tonic::transport::ClientTlsConfig; -use tonic::{async_trait, metadata::MetadataValue, transport::Channel, Request, Streaming}; +use tonic::{metadata::MetadataValue, transport::Channel, Request, Streaming}; +use xmtp_proto::api_client::{ClientWithMetadata, XmtpMlsStreams}; +use xmtp_proto::xmtp::mls::api::v1::{GroupMessage, WelcomeMessage}; use xmtp_proto::{ api_client::{ - Error, ErrorKind, GroupMessageStream, MutableApiSubscription, WelcomeMessageStream, - XmtpApiClient, XmtpApiSubscription, XmtpMlsClient, + Error, ErrorKind, MutableApiSubscription, XmtpApiClient, XmtpApiSubscription, XmtpMlsClient, }, xmtp::identity::api::v1::identity_api_client::IdentityApiClient as ProtoIdentityApiClient, xmtp::message_api::v1::{ @@ -21,11 +22,10 @@ use xmtp_proto::{ }, xmtp::mls::api::v1::{ mls_api_client::MlsApiClient as ProtoMlsApiClient, FetchKeyPackagesRequest, - FetchKeyPackagesResponse, GetIdentityUpdatesRequest, GetIdentityUpdatesResponse, - QueryGroupMessagesRequest, QueryGroupMessagesResponse, QueryWelcomeMessagesRequest, - QueryWelcomeMessagesResponse, RegisterInstallationRequest, RegisterInstallationResponse, - SendGroupMessagesRequest, SendWelcomeMessagesRequest, SubscribeGroupMessagesRequest, - SubscribeWelcomeMessagesRequest, UploadKeyPackageRequest, + FetchKeyPackagesResponse, QueryGroupMessagesRequest, QueryGroupMessagesResponse, + QueryWelcomeMessagesRequest, QueryWelcomeMessagesResponse, SendGroupMessagesRequest, + SendWelcomeMessagesRequest, SubscribeGroupMessagesRequest, SubscribeWelcomeMessagesRequest, + UploadKeyPackageRequest, }, }; @@ -56,7 +56,7 @@ async fn create_tls_channel(address: String) -> Result { // Functionality: If a ping response is not received within this duration, the connection is presumed to be lost and is closed. // Impact: This setting is crucial for quickly detecting unresponsive connections and freeing up resources associated with them. It ensures that the client has up-to-date information on the status of connections and can react accordingly. .keep_alive_timeout(Duration::from_secs(25)) - .tls_config(ClientTlsConfig::new()) + .tls_config(ClientTlsConfig::new().with_enabled_roots()) .map_err(|e| Error::new(ErrorKind::SetupTLSConfigError).with(e))? .connect() .await @@ -71,55 +71,70 @@ pub struct Client { pub(crate) mls_client: ProtoMlsApiClient, pub(crate) identity_client: ProtoIdentityApiClient, pub(crate) app_version: MetadataValue, + pub(crate) libxmtp_version: MetadataValue, } impl Client { pub async fn create(host: String, is_secure: bool) -> Result { let host = host.to_string(); - let app_version = MetadataValue::try_from(&String::from("0.0.0")).unwrap(); - if is_secure { - let channel = create_tls_channel(host).await?; - - let client = MessageApiClient::new(channel.clone()); - let mls_client = ProtoMlsApiClient::new(channel.clone()); - let identity_client = ProtoIdentityApiClient::new(channel); - - Ok(Self { - client, - mls_client, - app_version, - identity_client, - }) - } else { - let channel = Channel::from_shared(host) + let app_version = MetadataValue::try_from(&String::from("0.0.0")) + .map_err(|e| Error::new(ErrorKind::MetadataError).with(e))?; + let libxmtp_version = MetadataValue::try_from(&String::from("0.0.0")) + .map_err(|e| Error::new(ErrorKind::MetadataError).with(e))?; + + let channel = match is_secure { + true => create_tls_channel(host).await?, + false => Channel::from_shared(host) .map_err(|e| Error::new(ErrorKind::SetupCreateChannelError).with(e))? .connect() .await - .map_err(|e| Error::new(ErrorKind::SetupConnectionError).with(e))?; - - let client = MessageApiClient::new(channel.clone()); - let mls_client = ProtoMlsApiClient::new(channel.clone()); - let identity_client = ProtoIdentityApiClient::new(channel); - - Ok(Self { - client, - mls_client, - identity_client, - app_version, - }) - } + .map_err(|e| Error::new(ErrorKind::SetupConnectionError).with(e))?, + }; + + let client = MessageApiClient::new(channel.clone()); + let mls_client = ProtoMlsApiClient::new(channel.clone()); + let identity_client = ProtoIdentityApiClient::new(channel); + + Ok(Self { + client, + mls_client, + app_version, + libxmtp_version, + identity_client, + }) + } + + pub fn build_request(&self, request: RequestType) -> Request { + let mut req = Request::new(request); + req.metadata_mut() + .insert("x-app-version", self.app_version.clone()); + req.metadata_mut() + .insert("x-libxmtp-version", self.libxmtp_version.clone()); + + req + } +} + +impl ClientWithMetadata for Client { + fn set_libxmtp_version(&mut self, version: String) -> Result<(), Error> { + self.libxmtp_version = MetadataValue::try_from(&version) + .map_err(|e| Error::new(ErrorKind::MetadataError).with(e))?; + + Ok(()) + } + + fn set_app_version(&mut self, version: String) -> Result<(), Error> { + self.app_version = MetadataValue::try_from(&version) + .map_err(|e| Error::new(ErrorKind::MetadataError).with(e))?; + + Ok(()) } } -#[async_trait] impl XmtpApiClient for Client { type Subscription = Subscription; type MutableSubscription = GrpcMutableSubscription; - fn set_app_version(&mut self, version: String) { - self.app_version = MetadataValue::try_from(&version).unwrap(); - } - async fn publish( &self, token: String, @@ -130,11 +145,8 @@ impl XmtpApiClient for Client { .parse() .map_err(|e| Error::new(ErrorKind::PublishError).with(e))?; - let mut tonic_request = Request::new(request); + let mut tonic_request = self.build_request(request); tonic_request.metadata_mut().insert("authorization", token); - tonic_request - .metadata_mut() - .insert("x-app-version", self.app_version.clone()); let client = &mut self.client.clone(); client @@ -145,14 +157,9 @@ impl XmtpApiClient for Client { } async fn subscribe(&self, request: SubscribeRequest) -> Result { - let mut tonic_request = Request::new(request); - tonic_request - .metadata_mut() - .insert("x-app-version", self.app_version.clone()); - let client = &mut self.client.clone(); let stream = client - .subscribe(tonic_request) + .subscribe(self.build_request(request)) .await .map_err(|e| Error::new(ErrorKind::SubscribeError).with(e))? .into_inner(); @@ -175,16 +182,10 @@ impl XmtpApiClient for Client { } }; - let mut tonic_request = Request::new(input_stream); - - tonic_request - .metadata_mut() - .insert("x-app-version", self.app_version.clone()); - let client = &mut self.client.clone(); let stream = client - .subscribe2(tonic_request) + .subscribe2(self.build_request(input_stream)) .await .map_err(|e| Error::new(ErrorKind::SubscribeError).with(e))? .into_inner(); @@ -196,13 +197,9 @@ impl XmtpApiClient for Client { } async fn query(&self, request: QueryRequest) -> Result { - let mut tonic_request = Request::new(request); - tonic_request - .metadata_mut() - .insert("x-app-version", self.app_version.clone()); let client = &mut self.client.clone(); - let res = client.query(tonic_request).await; + let res = client.query(self.build_request(request)).await; match res { Ok(response) => Ok(response.into_inner()), @@ -211,13 +208,8 @@ impl XmtpApiClient for Client { } async fn batch_query(&self, request: BatchQueryRequest) -> Result { - let mut tonic_request = Request::new(request); - tonic_request - .metadata_mut() - .insert("x-app-version", self.app_version.clone()); - let client = &mut self.client.clone(); - let res = client.batch_query(tonic_request).await; + let res = client.batch_query(self.build_request(request)).await; match res { Ok(response) => Ok(response.into_inner()), @@ -325,7 +317,6 @@ impl Stream for GrpcMutableSubscription { } } -#[async_trait] impl MutableApiSubscription for GrpcMutableSubscription { async fn update(&mut self, req: SubscribeRequest) -> Result<(), Error> { self.update_channel @@ -341,26 +332,12 @@ impl MutableApiSubscription for GrpcMutableSubscription { self.update_channel.close_channel(); } } - -#[async_trait] impl XmtpMlsClient for Client { - #[tracing::instrument(level = "trace", skip_all)] - async fn register_installation( - &self, - req: RegisterInstallationRequest, - ) -> Result { - let client = &mut self.mls_client.clone(); - let res = client.register_installation(req).await; - match res { - Ok(response) => Ok(response.into_inner()), - Err(e) => Err(Error::new(ErrorKind::MlsError).with(e)), - } - } - #[tracing::instrument(level = "trace", skip_all)] async fn upload_key_package(&self, req: UploadKeyPackageRequest) -> Result<(), Error> { let client = &mut self.mls_client.clone(); - let res = client.upload_key_package(req).await; + + let res = client.upload_key_package(self.build_request(req)).await; match res { Ok(_) => Ok(()), Err(e) => Err(Error::new(ErrorKind::MlsError).with(e)), @@ -373,7 +350,7 @@ impl XmtpMlsClient for Client { req: FetchKeyPackagesRequest, ) -> Result { let client = &mut self.mls_client.clone(); - let res = client.fetch_key_packages(req).await; + let res = client.fetch_key_packages(self.build_request(req)).await; res.map(|r| r.into_inner()) .map_err(|e| Error::new(ErrorKind::MlsError).with(e)) @@ -382,7 +359,7 @@ impl XmtpMlsClient for Client { #[tracing::instrument(level = "trace", skip_all)] async fn send_group_messages(&self, req: SendGroupMessagesRequest) -> Result<(), Error> { let client = &mut self.mls_client.clone(); - let res = client.send_group_messages(req).await; + let res = client.send_group_messages(self.build_request(req)).await; match res { Ok(_) => Ok(()), @@ -393,7 +370,7 @@ impl XmtpMlsClient for Client { #[tracing::instrument(level = "trace", skip_all)] async fn send_welcome_messages(&self, req: SendWelcomeMessagesRequest) -> Result<(), Error> { let client = &mut self.mls_client.clone(); - let res = client.send_welcome_messages(req).await; + let res = client.send_welcome_messages(self.build_request(req)).await; match res { Ok(_) => Ok(()), @@ -407,7 +384,7 @@ impl XmtpMlsClient for Client { req: QueryGroupMessagesRequest, ) -> Result { let client = &mut self.mls_client.clone(); - let res = client.query_group_messages(req).await; + let res = client.query_group_messages(self.build_request(req)).await; res.map(|r| r.into_inner()) .map_err(|e| Error::new(ErrorKind::MlsError).with(e)) @@ -419,55 +396,89 @@ impl XmtpMlsClient for Client { req: QueryWelcomeMessagesRequest, ) -> Result { let client = &mut self.mls_client.clone(); - let res = client.query_welcome_messages(req).await; + let res = client.query_welcome_messages(self.build_request(req)).await; res.map(|r| r.into_inner()) .map_err(|e| Error::new(ErrorKind::MlsError).with(e)) } +} - #[tracing::instrument(level = "trace", skip_all)] - async fn get_identity_updates( - &self, - req: GetIdentityUpdatesRequest, - ) -> Result { - let client = &mut self.mls_client.clone(); - let res = client.get_identity_updates(req).await; +pub struct GroupMessageStream { + inner: tonic::codec::Streaming, +} - res.map(|r| r.into_inner()) - .map_err(|e| Error::new(ErrorKind::MlsError).with(e)) +impl From> for GroupMessageStream { + fn from(inner: tonic::codec::Streaming) -> Self { + GroupMessageStream { inner } } +} + +impl Stream for GroupMessageStream { + type Item = Result; + + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.inner + .poll_next_unpin(cx) + .map(|data| data.map(|v| v.map_err(|e| Error::new(ErrorKind::SubscribeError).with(e)))) + } +} + +pub struct WelcomeMessageStream { + inner: tonic::codec::Streaming, +} + +impl From> for WelcomeMessageStream { + fn from(inner: tonic::codec::Streaming) -> Self { + WelcomeMessageStream { inner } + } +} + +impl Stream for WelcomeMessageStream { + type Item = Result; + + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.inner + .poll_next_unpin(cx) + .map(|data| data.map(|v| v.map_err(|e| Error::new(ErrorKind::SubscribeError).with(e)))) + } +} + +impl XmtpMlsStreams for Client { + type GroupMessageStream<'a> = GroupMessageStream; + type WelcomeMessageStream<'a> = WelcomeMessageStream; async fn subscribe_group_messages( &self, req: SubscribeGroupMessagesRequest, - ) -> Result { + ) -> Result, Error> { let client = &mut self.mls_client.clone(); let res = client - .subscribe_group_messages(req) + .subscribe_group_messages(self.build_request(req)) .await .map_err(|e| Error::new(ErrorKind::MlsError).with(e))?; let stream = res.into_inner(); - - let new_stream = stream.map_err(|e| Error::new(ErrorKind::SubscribeError).with(e)); - - Ok(Box::pin(new_stream)) + Ok(stream.into()) } async fn subscribe_welcome_messages( &self, req: SubscribeWelcomeMessagesRequest, - ) -> Result { + ) -> Result, Error> { let client = &mut self.mls_client.clone(); let res = client - .subscribe_welcome_messages(req) + .subscribe_welcome_messages(self.build_request(req)) .await .map_err(|e| Error::new(ErrorKind::MlsError).with(e))?; let stream = res.into_inner(); - let new_stream = stream.map_err(|e| Error::new(ErrorKind::SubscribeError).with(e)); - - Ok(Box::pin(new_stream)) + Ok(stream.into()) } } diff --git a/xmtp_api_grpc/src/identity.rs b/xmtp_api_grpc/src/identity.rs index 5b635ef77..92531e0a6 100644 --- a/xmtp_api_grpc/src/identity.rs +++ b/xmtp_api_grpc/src/identity.rs @@ -1,4 +1,3 @@ -use tonic::{async_trait, Request}; use xmtp_proto::{ api_client::{Error, ErrorKind, XmtpIdentityClient}, xmtp::identity::api::v1::{ @@ -10,20 +9,17 @@ use xmtp_proto::{ use crate::Client; -#[async_trait] impl XmtpIdentityClient for Client { #[tracing::instrument(level = "trace", skip_all)] async fn publish_identity_update( &self, request: PublishIdentityUpdateRequest, ) -> Result { - let mut tonic_request = Request::new(request); - tonic_request - .metadata_mut() - .insert("x-app-version", self.app_version.clone()); let client = &mut self.identity_client.clone(); - let res = client.publish_identity_update(tonic_request).await; + let res = client + .publish_identity_update(self.build_request(request)) + .await; res.map(|response| response.into_inner()) .map_err(|err| Error::new(ErrorKind::IdentityError).with(err)) @@ -34,13 +30,9 @@ impl XmtpIdentityClient for Client { &self, request: GetInboxIdsRequest, ) -> Result { - let mut tonic_request = Request::new(request); - tonic_request - .metadata_mut() - .insert("x-app-version", self.app_version.clone()); let client = &mut self.identity_client.clone(); - let res = client.get_inbox_ids(tonic_request).await; + let res = client.get_inbox_ids(self.build_request(request)).await; res.map(|response| response.into_inner()) .map_err(|err| Error::new(ErrorKind::IdentityError).with(err)) @@ -51,13 +43,11 @@ impl XmtpIdentityClient for Client { &self, request: GetIdentityUpdatesV2Request, ) -> Result { - let mut tonic_request = Request::new(request); - tonic_request - .metadata_mut() - .insert("x-app-version", self.app_version.clone()); let client = &mut self.identity_client.clone(); - let res = client.get_identity_updates(tonic_request).await; + let res = client + .get_identity_updates(self.build_request(request)) + .await; res.map(|response| response.into_inner()) .map_err(|err| Error::new(ErrorKind::IdentityError).with(err)) diff --git a/xmtp_api_grpc/src/lib.rs b/xmtp_api_grpc/src/lib.rs index 9cc8bf750..a774b86bc 100644 --- a/xmtp_api_grpc/src/lib.rs +++ b/xmtp_api_grpc/src/lib.rs @@ -5,7 +5,7 @@ mod identity; pub const LOCALHOST_ADDRESS: &str = "http://localhost:5556"; pub const DEV_ADDRESS: &str = "https://grpc.dev.xmtp.network:443"; -pub use grpc_api_helper::Client; +pub use grpc_api_helper::{Client, GroupMessageStream, WelcomeMessageStream}; #[cfg(test)] mod tests { @@ -15,6 +15,7 @@ mod tests { use super::*; use futures::StreamExt; + use xmtp_proto::api_client::ClientWithMetadata; use xmtp_proto::{ api_client::{MutableApiSubscription, XmtpApiClient, XmtpApiSubscription}, xmtp::message_api::v1::{ @@ -52,7 +53,9 @@ mod tests { .await .unwrap(); - client.set_app_version("test/0.1.0".to_string()); + client + .set_app_version("test/0.1.0".to_string()) + .expect("failed to set app version"); let result = client .query(QueryRequest { @@ -301,4 +304,37 @@ mod tests { .await .expect("Timed out"); } + + #[tokio::test] + async fn metadata_test() { + let mut client = Client::create(DEV_ADDRESS.to_string(), true).await.unwrap(); + let app_version = "test/1.0.0".to_string(); + let libxmtp_version = "0.0.1".to_string(); + + client.set_app_version(app_version.clone()).unwrap(); + client.set_libxmtp_version(libxmtp_version.clone()).unwrap(); + + let request = client.build_request(PublishRequest { envelopes: vec![] }); + + assert_eq!( + request + .metadata() + .get("x-app-version") + .unwrap() + .to_str() + .unwrap() + .to_string(), + app_version + ); + assert_eq!( + request + .metadata() + .get("x-libxmtp-version") + .unwrap() + .to_str() + .unwrap() + .to_string(), + libxmtp_version + ); + } } diff --git a/xmtp_api_grpc_gateway/Cargo.lock b/xmtp_api_grpc_gateway/Cargo.lock deleted file mode 100644 index 88a4457e7..000000000 --- a/xmtp_api_grpc_gateway/Cargo.lock +++ /dev/null @@ -1,4171 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex", -] - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aes" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aho-corasick" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - -[[package]] -name = "array-init" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" -dependencies = [ - "nodrop", -] - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - -[[package]] -name = "ascii-canvas" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" -dependencies = [ - "term", -] - -[[package]] -name = "async-trait" -version = "0.1.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "async_io_stream" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" -dependencies = [ - "futures", - "pharos", - "rustc_version", -] - -[[package]] -name = "auto_impl" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bech32" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bs58" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" -dependencies = [ - "sha2", - "tinyvec", -] - -[[package]] -name = "bumpalo" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" - -[[package]] -name = "byte-slice-cast" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" -dependencies = [ - "serde", -] - -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "camino" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "jobserver", - "libc", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" -dependencies = [ - "num-traits", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "coins-bip32" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3" -dependencies = [ - "bs58", - "coins-core", - "digest", - "hmac", - "k256 0.13.1", - "serde", - "sha2", - "thiserror", -] - -[[package]] -name = "coins-bip39" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" -dependencies = [ - "bitvec", - "coins-bip32", - "hmac", - "once_cell", - "pbkdf2 0.12.2", - "rand", - "sha2", - "thiserror", -] - -[[package]] -name = "coins-core" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" -dependencies = [ - "base64 0.21.4", - "bech32", - "bs58", - "digest", - "generic-array", - "hex", - "ripemd", - "serde", - "serde_derive", - "sha2", - "sha3", - "thiserror", -] - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "const-hex" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08849ed393c907c90016652a01465a12d86361cd38ad2a7de026c56a520cc259" -dependencies = [ - "cfg-if", - "cpufeatures", - "hex", - "serde", -] - -[[package]] -name = "const-oid" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - -[[package]] -name = "cpufeatures" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-bigint" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "fiat-crypto", - "platforms", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "data-encoding" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "der" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dunce" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" - -[[package]] -name = "ecdsa" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12844141594ad74185a926d030f3b605f6a903b4e3fec351f3ea338ac5b7637e" -dependencies = [ - "der 0.6.1", - "elliptic-curve 0.12.3", - "rfc6979 0.3.1", - "signature", -] - -[[package]] -name = "ecdsa" -version = "0.16.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" -dependencies = [ - "der 0.7.8", - "digest", - "elliptic-curve 0.13.5", - "rfc6979 0.4.0", - "signature", - "spki 0.7.2", -] - -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct 0.1.1", - "crypto-bigint 0.4.9", - "der 0.6.1", - "digest", - "ff 0.12.1", - "generic-array", - "group 0.12.1", - "hkdf", - "pkcs8 0.9.0", - "rand_core", - "sec1 0.3.0", - "subtle", - "zeroize", -] - -[[package]] -name = "elliptic-curve" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" -dependencies = [ - "base16ct 0.2.0", - "crypto-bigint 0.5.3", - "digest", - "ff 0.13.0", - "generic-array", - "group 0.13.0", - "pkcs8 0.10.2", - "rand_core", - "sec1 0.7.3", - "subtle", - "zeroize", -] - -[[package]] -name = "ena" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" -dependencies = [ - "log", -] - -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enr" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be7b2ac146c1f99fe245c02d16af0696450d8e06c135db75e10eeb9e642c20d" -dependencies = [ - "base64 0.21.4", - "bytes", - "hex", - "k256 0.13.1", - "log", - "rand", - "rlp", - "serde", - "serde-hex", - "sha3", - "zeroize", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "eth-keystore" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" -dependencies = [ - "aes", - "ctr", - "digest", - "hex", - "hmac", - "pbkdf2 0.11.0", - "rand", - "scrypt", - "serde", - "serde_json", - "sha2", - "sha3", - "thiserror", - "uuid 0.8.2", -] - -[[package]] -name = "ethabi" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" -dependencies = [ - "ethereum-types", - "hex", - "once_cell", - "regex", - "serde", - "serde_json", - "sha3", - "thiserror", - "uint", -] - -[[package]] -name = "ethbloom" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" -dependencies = [ - "crunchy", - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", - "tiny-keccak", -] - -[[package]] -name = "ethereum-types" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" -dependencies = [ - "ethbloom", - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "primitive-types", - "scale-info", - "uint", -] - -[[package]] -name = "ethers" -version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5344eea9b20effb5efeaad29418215c4d27017639fd1f908260f59cbbd226e" -dependencies = [ - "ethers-addressbook", - "ethers-contract", - "ethers-core", - "ethers-etherscan", - "ethers-middleware", - "ethers-providers", - "ethers-signers", - "ethers-solc", -] - -[[package]] -name = "ethers-addressbook" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bf35eb7d2e2092ad41f584951e08ec7c077b142dba29c4f1b8f52d2efddc49c" -dependencies = [ - "ethers-core", - "once_cell", - "serde", - "serde_json", -] - -[[package]] -name = "ethers-contract" -version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0111ead599d17a7bff6985fd5756f39ca7033edc79a31b23026a8d5d64fa95cd" -dependencies = [ - "const-hex", - "ethers-contract-abigen", - "ethers-contract-derive", - "ethers-core", - "ethers-providers", - "futures-util", - "once_cell", - "pin-project", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "ethers-contract-abigen" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbdfb952aafd385b31d316ed80d7b76215ce09743c172966d840e96924427e0c" -dependencies = [ - "Inflector", - "const-hex", - "dunce", - "ethers-core", - "ethers-etherscan", - "eyre", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "reqwest", - "serde", - "serde_json", - "syn 2.0.48", - "toml", - "walkdir", -] - -[[package]] -name = "ethers-contract-derive" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7465c814a2ecd0de0442160da13584205d1cdc08f4717a6511cad455bd5d7dc4" -dependencies = [ - "Inflector", - "const-hex", - "ethers-contract-abigen", - "ethers-core", - "proc-macro2", - "quote", - "serde_json", - "syn 2.0.48", -] - -[[package]] -name = "ethers-core" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "918b1a9ba585ea61022647def2f27c29ba19f6d2a4a4c8f68a9ae97fd5769737" -dependencies = [ - "arrayvec", - "bytes", - "cargo_metadata", - "chrono", - "const-hex", - "elliptic-curve 0.13.5", - "ethabi", - "generic-array", - "k256 0.13.1", - "num_enum", - "once_cell", - "open-fastrlp", - "rand", - "rlp", - "serde", - "serde_json", - "strum", - "syn 2.0.48", - "tempfile", - "thiserror", - "tiny-keccak", - "unicode-xid", -] - -[[package]] -name = "ethers-etherscan" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "facabf8551b4d1a3c08cb935e7fca187804b6c2525cc0dafb8e5a6dd453a24de" -dependencies = [ - "chrono", - "ethers-core", - "reqwest", - "semver", - "serde", - "serde_json", - "thiserror", - "tracing", -] - -[[package]] -name = "ethers-middleware" -version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681ece6eb1d10f7cf4f873059a77c04ff1de4f35c63dd7bccde8f438374fcb93" -dependencies = [ - "async-trait", - "auto_impl", - "ethers-contract", - "ethers-core", - "ethers-etherscan", - "ethers-providers", - "ethers-signers", - "futures-channel", - "futures-locks", - "futures-util", - "instant", - "reqwest", - "serde", - "serde_json", - "thiserror", - "tokio", - "tracing", - "tracing-futures", - "url", -] - -[[package]] -name = "ethers-providers" -version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25d6c0c9455d93d4990c06e049abf9b30daf148cf461ee939c11d88907c60816" -dependencies = [ - "async-trait", - "auto_impl", - "base64 0.21.4", - "bytes", - "const-hex", - "enr", - "ethers-core", - "futures-channel", - "futures-core", - "futures-timer", - "futures-util", - "hashers", - "http", - "instant", - "jsonwebtoken", - "once_cell", - "pin-project", - "reqwest", - "serde", - "serde_json", - "thiserror", - "tokio", - "tokio-tungstenite", - "tracing", - "tracing-futures", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "ws_stream_wasm", -] - -[[package]] -name = "ethers-signers" -version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb1b714e227bbd2d8c53528adb580b203009728b17d0d0e4119353aa9bc5532" -dependencies = [ - "async-trait", - "coins-bip32", - "coins-bip39", - "const-hex", - "elliptic-curve 0.13.5", - "eth-keystore", - "ethers-core", - "rand", - "sha2", - "thiserror", - "tracing", -] - -[[package]] -name = "ethers-solc" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2e46e3ec8ef0c986145901fa9864205dc4dcee701f9846be2d56112d34bdea" -dependencies = [ - "cfg-if", - "const-hex", - "dirs", - "dunce", - "ethers-core", - "glob", - "home", - "md-5", - "num_cpus", - "once_cell", - "path-slash", - "rayon", - "regex", - "semver", - "serde", - "serde_json", - "solang-parser", - "svm-rs", - "thiserror", - "tiny-keccak", - "tokio", - "tracing", - "walkdir", - "yansi", -] - -[[package]] -name = "eyre" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" -dependencies = [ - "indenter", - "once_cell", -] - -[[package]] -name = "fastrand" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" - -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core", - "subtle", -] - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" - -[[package]] -name = "fixed-hash" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" -dependencies = [ - "byteorder", - "rand", - "rustc-hex", - "static_assertions", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flate2" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-executor" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-locks" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ec6fe3675af967e67c5536c0b9d44e34e6c52f86bedc4ea49c5317b8e94d06" -dependencies = [ - "futures-channel", - "futures-task", -] - -[[package]] -name = "futures-macro" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-timer" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" -dependencies = [ - "gloo-timers", - "send_wrapper 0.4.0", -] - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "gimli" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "ff 0.12.1", - "rand_core", - "subtle", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff 0.13.0", - "rand_core", - "subtle", -] - -[[package]] -name = "h2" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap 1.9.3", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" - -[[package]] -name = "hashers" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2bca93b15ea5a746f220e56587f71e73c6165eab783df9e26590069953e3c30" -dependencies = [ - "fxhash", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "0.14.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.4.9", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" -dependencies = [ - "futures-util", - "http", - "hyper", - "rustls", - "tokio", - "tokio-rustls", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "impl-codec" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "impl-rlp" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" -dependencies = [ - "rlp", -] - -[[package]] -name = "impl-serde" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" -dependencies = [ - "serde", -] - -[[package]] -name = "impl-trait-for-tuples" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "indenter" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" -dependencies = [ - "equivalent", - "hashbrown 0.14.0", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "ipnet" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" - -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "jobserver" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "jsonwebtoken" -version = "8.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" -dependencies = [ - "base64 0.21.4", - "pem", - "ring", - "serde", - "serde_json", - "simple_asn1", -] - -[[package]] -name = "k256" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a55e0ff3b72c262bcf041d9e97f1b84492b68f1c1a384de2323d3dc9403397" -dependencies = [ - "cfg-if", - "ecdsa 0.15.1", - "elliptic-curve 0.12.3", - "once_cell", - "sha2", - "signature", -] - -[[package]] -name = "k256" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" -dependencies = [ - "cfg-if", - "ecdsa 0.16.8", - "elliptic-curve 0.13.5", - "once_cell", - "sha2", - "signature", -] - -[[package]] -name = "keccak" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "lalrpop" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8" -dependencies = [ - "ascii-canvas", - "bit-set", - "diff", - "ena", - "is-terminal", - "itertools 0.10.5", - "lalrpop-util", - "petgraph", - "regex", - "regex-syntax", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid", -] - -[[package]] -name = "lalrpop-util" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.148" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" - -[[package]] -name = "linux-raw-sys" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" - -[[package]] -name = "lock_api" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - -[[package]] -name = "md-5" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" -dependencies = [ - "digest", -] - -[[package]] -name = "memchr" -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.48.0", -] - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "object" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "open-fastrlp" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" -dependencies = [ - "arrayvec", - "auto_impl", - "bytes", - "ethereum-types", - "open-fastrlp-derive", -] - -[[package]] -name = "open-fastrlp-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" -dependencies = [ - "bytes", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "openssl" -version = "0.10.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" -dependencies = [ - "bitflags 2.4.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "parity-scale-codec" -version = "3.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" -dependencies = [ - "arrayvec", - "bitvec", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "serde", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "3.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.3.5", - "smallvec 1.11.0", - "windows-targets 0.48.5", -] - -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core", - "subtle", -] - -[[package]] -name = "path-slash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" - -[[package]] -name = "pbjson" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1030c719b0ec2a2d25a5df729d6cff1acf3cc230bf766f4f97833591f7577b90" -dependencies = [ - "base64 0.21.4", - "serde", -] - -[[package]] -name = "pbjson-build" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2580e33f2292d34be285c5bc3dba5259542b083cfad6037b6d70345f24dcb735" -dependencies = [ - "heck", - "itertools 0.11.0", - "prost", - "prost-types", -] - -[[package]] -name = "pbjson-types" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18f596653ba4ac51bdecbb4ef6773bc7f56042dc13927910de1684ad3d32aa12" -dependencies = [ - "bytes", - "chrono", - "pbjson", - "pbjson-build", - "prost", - "prost-build", - "serde", -] - -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest", - "hmac", - "password-hash", - "sha2", -] - -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", -] - -[[package]] -name = "pem" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" -dependencies = [ - "base64 0.13.1", -] - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -[[package]] -name = "petgraph" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" -dependencies = [ - "fixedbitset", - "indexmap 2.0.0", -] - -[[package]] -name = "pharos" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" -dependencies = [ - "futures", - "rustc_version", -] - -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_macros", - "phf_shared 0.11.2", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared 0.11.2", - "rand", -] - -[[package]] -name = "phf_macros" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" -dependencies = [ - "phf_generator", - "phf_shared 0.11.2", - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" -dependencies = [ - "der 0.6.1", - "spki 0.6.0", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der 0.7.8", - "spki 0.7.2", -] - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "platforms" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "prettyplease" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" -dependencies = [ - "proc-macro2", - "syn 2.0.48", -] - -[[package]] -name = "primitive-types" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" -dependencies = [ - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", - "uint", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-build" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2" -dependencies = [ - "bytes", - "heck", - "itertools 0.11.0", - "log", - "multimap", - "once_cell", - "petgraph", - "prettyplease", - "prost", - "prost-types", - "regex", - "syn 2.0.48", - "tempfile", - "which", -] - -[[package]] -name = "prost-derive" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" -dependencies = [ - "anyhow", - "itertools 0.11.0", - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "prost-types" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" -dependencies = [ - "prost", -] - -[[package]] -name = "quote" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom", - "redox_syscall 0.2.16", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - -[[package]] -name = "reqwest" -version = "0.11.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" -dependencies = [ - "base64 0.21.4", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-rustls", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 0.25.2", - "winreg", -] - -[[package]] -name = "rfc6979" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" -dependencies = [ - "crypto-bigint 0.4.9", - "hmac", - "zeroize", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest", -] - -[[package]] -name = "rlp" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" -dependencies = [ - "bytes", - "rlp-derive", - "rustc-hex", -] - -[[package]] -name = "rlp-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" -dependencies = [ - "bitflags 2.4.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustls" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" -dependencies = [ - "log", - "ring", - "rustls-webpki 0.101.5", - "sct", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" -dependencies = [ - "base64 0.21.4", -] - -[[package]] -name = "rustls-webpki" -version = "0.100.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a27e3b59326c16e23d30aeb7a36a24cc0d29e71d68ff611cdfb4a01d013bed" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scale-info" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" -dependencies = [ - "cfg-if", - "derive_more", - "parity-scale-codec", - "scale-info-derive", -] - -[[package]] -name = "scale-info-derive" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "schannel" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "scrypt" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" -dependencies = [ - "hmac", - "pbkdf2 0.11.0", - "salsa20", - "sha2", -] - -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct 0.1.1", - "der 0.6.1", - "generic-array", - "pkcs8 0.9.0", - "subtle", - "zeroize", -] - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct 0.2.0", - "der 0.7.8", - "generic-array", - "pkcs8 0.10.2", - "subtle", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" -dependencies = [ - "serde", -] - -[[package]] -name = "send_wrapper" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" - -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" - -[[package]] -name = "serde" -version = "1.0.195" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-hex" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca37e3e4d1b39afd7ff11ee4e947efae85adfddf4841787bfa47c470e96dc26d" -dependencies = [ - "array-init", - "serde", - "smallvec 0.6.14", -] - -[[package]] -name = "serde_derive" -version = "1.0.195" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "serde_json" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_spanned" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest", - "keccak", -] - -[[package]] -name = "signature" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" -dependencies = [ - "digest", - "rand_core", -] - -[[package]] -name = "simple_asn1" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" -dependencies = [ - "num-bigint", - "num-traits", - "thiserror", - "time", -] - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] - -[[package]] -name = "smallvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "solang-parser" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c425ce1c59f4b154717592f0bdf4715c3a1d55058883622d3157e1f0908a5b26" -dependencies = [ - "itertools 0.11.0", - "lalrpop", - "lalrpop-util", - "phf", - "thiserror", - "unicode-xid", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "base64ct", - "der 0.6.1", -] - -[[package]] -name = "spki" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" -dependencies = [ - "base64ct", - "der 0.7.8", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot", - "phf_shared 0.10.0", - "precomputed-hash", -] - -[[package]] -name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.48", -] - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[package]] -name = "svm-rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597e3a746727984cb7ea2487b6a40726cad0dbe86628e7d429aa6b8c4c153db4" -dependencies = [ - "dirs", - "fs2", - "hex", - "once_cell", - "reqwest", - "semver", - "serde", - "serde_json", - "sha2", - "thiserror", - "url", - "zip", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - -[[package]] -name = "thiserror" -version = "1.0.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "time" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" -dependencies = [ - "deranged", - "itoa", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" - -[[package]] -name = "time-macros" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572" -dependencies = [ - "time-core", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "pin-project-lite", - "socket2 0.5.4", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2dbec703c26b00d74844519606ef15d09a7d6857860f84ad223dec002ddea2" -dependencies = [ - "futures-util", - "log", - "rustls", - "tokio", - "tokio-rustls", - "tungstenite", - "webpki-roots 0.23.1", -] - -[[package]] -name = "tokio-util" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.21.0", -] - -[[package]] -name = "toml_datetime" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.0.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" -dependencies = [ - "indexmap 2.0.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "tracing-core" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "tungstenite" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e862a1c4128df0112ab625f55cd5c934bcb4312ba80b39ae4b4835a3fd58e649" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand", - "rustls", - "sha1", - "thiserror", - "url", - "utf-8", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "url" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -dependencies = [ - "getrandom", - "serde", -] - -[[package]] -name = "uuid" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" -dependencies = [ - "getrandom", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "walkdir" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.48", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" - -[[package]] -name = "wasm-bindgen-test" -version = "0.3.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671" -dependencies = [ - "console_error_panic_hook", - "js-sys", - "scoped-tls", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test-macro", -] - -[[package]] -name = "wasm-bindgen-test-macro" -version = "0.3.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "web-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-roots" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.3", -] - -[[package]] -name = "webpki-roots" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" - -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" - -[[package]] -name = "winnow" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "ws_stream_wasm" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" -dependencies = [ - "async_io_stream", - "futures", - "js-sys", - "log", - "pharos", - "rustc_version", - "send_wrapper 0.6.0", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "xmtp_api_grpc_gateway" -version = "0.1.0" -dependencies = [ - "async-trait", - "futures", - "getrandom", - "hex", - "js-sys", - "prost", - "prost-types", - "reqwest", - "uuid 1.4.1", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test", - "xmtp_cryptography", - "xmtp_proto", -] - -[[package]] -name = "xmtp_cryptography" -version = "0.1.0" -dependencies = [ - "curve25519-dalek", - "ecdsa 0.15.1", - "ethers", - "ethers-core", - "hex", - "k256 0.12.0", - "log", - "rand", - "rand_chacha", - "rustc-hex", - "serde", - "sha2", - "sha3", - "thiserror", -] - -[[package]] -name = "xmtp_proto" -version = "0.1.0" -dependencies = [ - "async-trait", - "futures", - "futures-core", - "pbjson", - "pbjson-types", - "prost", - "prost-types", - "serde", -] - -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - -[[package]] -name = "zeroize" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" - -[[package]] -name = "zip" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" -dependencies = [ - "aes", - "byteorder", - "bzip2", - "constant_time_eq", - "crc32fast", - "crossbeam-utils", - "flate2", - "hmac", - "pbkdf2 0.11.0", - "sha1", - "time", - "zstd", -] - -[[package]] -name = "zstd" -version = "0.11.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" -dependencies = [ - "cc", - "libc", - "pkg-config", -] diff --git a/xmtp_api_grpc_gateway/Cargo.toml b/xmtp_api_grpc_gateway/Cargo.toml deleted file mode 100644 index 3fb0db72f..000000000 --- a/xmtp_api_grpc_gateway/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "xmtp_api_grpc_gateway" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["cdylib", "rlib"] - -[dependencies] -async-trait = "0.1.68" -futures = "0.3.29" -getrandom = { version = "0.2", features = ["js"] } -hex = "0.4" -js-sys = "0.3" -prost = { version = "^0.12", features = ["prost-derive"] } -prost-types = "^0.12" -reqwest = { version = "0.11.20", features = ["json"] } -wasm-bindgen = "0.2.87" -wasm-bindgen-futures = "0.4.37" -xmtp_cryptography = { path = "../xmtp_cryptography", features = ["ws"] } -xmtp_proto = { path = "../xmtp_proto", features = ["proto_full"] } - -[dev-dependencies] -uuid = { version = "1.3.1", features = ["v4"] } -wasm-bindgen-test = "0.3.34" - -[profile.release] -opt-level = "s" diff --git a/xmtp_api_grpc_gateway/README.md b/xmtp_api_grpc_gateway/README.md deleted file mode 100644 index 608d830fd..000000000 --- a/xmtp_api_grpc_gateway/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# XMTP API client using a gRPC Gateway - -# WARNING: DO NOT USE FOR PRODUCTION XMTP CLIENTS - -This code is still under development. - -- `$ cd xmtp_api_grpc_gateway` -- `$ wasm-pack test --headless --chrome` diff --git a/xmtp_api_grpc_gateway/src/lib.rs b/xmtp_api_grpc_gateway/src/lib.rs deleted file mode 100644 index 2603c3675..000000000 --- a/xmtp_api_grpc_gateway/src/lib.rs +++ /dev/null @@ -1,142 +0,0 @@ -use async_trait::async_trait; -use futures::Stream; - -use xmtp_proto::{ - api_client::{Error, ErrorKind, MutableApiSubscription, XmtpApiClient, XmtpApiSubscription}, - xmtp::message_api::v1::{ - BatchQueryRequest, BatchQueryResponse, Envelope, PublishRequest, PublishResponse, - QueryRequest, QueryResponse, SubscribeRequest, - }, -}; - -// TODO: consider moving these (and other address const) into `xmtp_proto` -pub const LOCALHOST_ADDRESS: &str = "http://localhost:5555"; -pub const DEV_ADDRESS: &str = "https://dev.xmtp.network:5555"; - -pub struct XmtpGrpcGatewayClient { - url: String, - http: reqwest::Client, -} - -impl XmtpGrpcGatewayClient { - pub fn new(url: String) -> Self { - XmtpGrpcGatewayClient { - url, - http: reqwest::Client::new(), - } - } -} - -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -impl XmtpApiClient for XmtpGrpcGatewayClient { - type Subscription = XmtpGrpcGatewaySubscription; - type MutableSubscription = XmtpGrpcGatewayMutableSubscription; - - fn set_app_version(&mut self, _version: String) { - // TODO - } - - async fn publish( - &self, - token: String, - request: PublishRequest, - ) -> Result { - let res: PublishResponse = self - .http - .post(&format!("{}/message/v1/publish", self.url)) - .bearer_auth(token) - .json(&request) - .send() - .await - .map_err(|e| Error::new(ErrorKind::PublishError).with(e))? - .json() - .await - .map_err(|e| Error::new(ErrorKind::PublishError).with(e))?; - Ok(res) - } - - async fn subscribe( - &self, - _request: SubscribeRequest, - ) -> Result { - // TODO - Err(Error::new(ErrorKind::SubscribeError)) - } - - async fn subscribe2( - &self, - _request: SubscribeRequest, - ) -> Result { - // TODO - Err(Error::new(ErrorKind::SubscribeError)) - } - - async fn query(&self, request: QueryRequest) -> Result { - let res: QueryResponse = self - .http - .post(&format!("{}/message/v1/query", self.url)) - .json(&request) - .send() - .await - .map_err(|e| Error::new(ErrorKind::QueryError).with(e))? - .json() - .await - .map_err(|e| Error::new(ErrorKind::QueryError).with(e))?; - Ok(res) - } - - async fn batch_query(&self, request: BatchQueryRequest) -> Result { - let res: BatchQueryResponse = self - .http - .post(&format!("{}/message/v1/batch-query", self.url)) - .json(&request) - .send() - .await - .map_err(|e| Error::new(ErrorKind::BatchQueryError).with(e))? - .json() - .await - .map_err(|e| Error::new(ErrorKind::BatchQueryError).with(e))?; - Ok(res) - } -} - -// TODO: implement JSON segmented streaming -pub struct XmtpGrpcGatewaySubscription {} -impl XmtpApiSubscription for XmtpGrpcGatewaySubscription { - fn is_closed(&self) -> bool { - true - } - - fn get_messages(&self) -> Vec { - vec![] - } - - fn close_stream(&mut self) {} -} - -pub struct XmtpGrpcGatewayMutableSubscription {} - -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -impl MutableApiSubscription for XmtpGrpcGatewayMutableSubscription { - async fn update(&mut self, _req: SubscribeRequest) -> Result<(), Error> { - // TODO - Err(Error::new(ErrorKind::SubscriptionUpdateError)) - } - - fn close(&self) { - // TODO - } -} - -impl Stream for XmtpGrpcGatewayMutableSubscription { - type Item = Result; - - fn poll_next( - self: std::pin::Pin<&mut Self>, - _cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - std::task::Poll::Ready(None) - } -} diff --git a/xmtp_api_grpc_gateway/tests/wasm_test.rs b/xmtp_api_grpc_gateway/tests/wasm_test.rs deleted file mode 100644 index bd7ee1eae..000000000 --- a/xmtp_api_grpc_gateway/tests/wasm_test.rs +++ /dev/null @@ -1,131 +0,0 @@ -extern crate wasm_bindgen_test; -extern crate xmtp_api_grpc_gateway; -use wasm_bindgen::prelude::*; -use wasm_bindgen_test::*; -use xmtp_api_grpc_gateway::XmtpGrpcGatewayClient; -use xmtp_proto::api_client::XmtpApiClient; -use xmtp_proto::xmtp::message_api::v1::{ - BatchQueryRequest, Envelope, PublishRequest, QueryRequest, -}; - -// Only run these tests in a browser. -wasm_bindgen_test_configure!(run_in_browser); - -#[wasm_bindgen] -extern "C" { - // Use `js_namespace` here to bind `console.log(..)` instead of just - // `log(..)` - #[wasm_bindgen(js_namespace = console)] - fn log(s: &str); -} - -#[wasm_bindgen_test] -pub async fn test_query_publish_query() { - let xmtp_url: String = "http://localhost:5555".to_string(); - let topic = uuid::Uuid::new_v4(); - let auth_token = ""; // TODO - - let api = XmtpGrpcGatewayClient::new(xmtp_url); - let q = QueryRequest { - content_topics: vec![topic.to_string()], - ..QueryRequest::default() - }; - - // At first there's nothing there. - let res = api.query(q.clone()).await.expect("successfully queried"); - assert_eq!(0, res.envelopes.len()); - - // But after we publish something... - api.publish( - auth_token.to_string(), - PublishRequest { - envelopes: vec![Envelope { - content_topic: topic.to_string(), - message: vec![1, 2, 3, 4], - timestamp_ns: 1234, - }], - }, - ) - .await - .expect("published"); - - // ... then we should see it in the query. - let res = api.query(q.clone()).await.expect("successfully queried"); - assert_eq!(1, res.envelopes.len()); - assert_eq!(topic.to_string(), res.envelopes[0].content_topic); - assert_eq!(1234, res.envelopes[0].timestamp_ns); - assert_eq!(vec![1, 2, 3, 4], res.envelopes[0].message); -} - -#[wasm_bindgen_test] -pub async fn test_batch_query_publish_batch_query() { - let xmtp_url: String = "http://localhost:5555".to_string(); - let api = XmtpGrpcGatewayClient::new(xmtp_url); - let topic1 = uuid::Uuid::new_v4(); - let topic2 = uuid::Uuid::new_v4(); - let auth_token = ""; // TODO - - // First we issue this batch query and get no results. - let batch_q = BatchQueryRequest { - requests: vec![ - QueryRequest { - content_topics: vec![topic1.to_string()], - ..QueryRequest::default() - }, - QueryRequest { - content_topics: vec![topic2.to_string()], - ..QueryRequest::default() - }, - ], - }; - let res = api - .batch_query(batch_q.clone()) - .await - .expect("successfully batch queried"); - assert_eq!(2, res.responses.len()); - assert_eq!(0, res.responses[0].envelopes.len()); - assert_eq!(0, res.responses[1].envelopes.len()); - - // Now we publish to both of the topics... - api.publish( - auth_token.to_string(), - PublishRequest { - envelopes: vec![ - Envelope { - content_topic: topic1.to_string(), - message: vec![1, 1, 1, 1], - timestamp_ns: 1111, - }, - Envelope { - content_topic: topic2.to_string(), - message: vec![2, 2, 2, 2], - timestamp_ns: 2222, - }, - ], - }, - ) - .await - .expect("published to both of them"); - - // ... so when we batch query again we should see the results. - let res = api - .batch_query(batch_q.clone()) - .await - .expect("successfully batch queried again"); - assert_eq!(2, res.responses.len()); - assert_eq!(1, res.responses[0].envelopes.len()); - assert_eq!(1, res.responses[1].envelopes.len()); - let e1: Envelope; - let e2: Envelope; - if res.responses[0].envelopes[0].content_topic == topic1.to_string() { - e1 = res.responses[0].envelopes[0].clone(); - e2 = res.responses[1].envelopes[0].clone(); - } else { - e1 = res.responses[1].envelopes[0].clone(); - e2 = res.responses[0].envelopes[0].clone(); - } - assert_eq!(1111, e1.timestamp_ns); - assert_eq!(2222, e2.timestamp_ns); - assert_eq!(vec![1, 1, 1, 1], e1.message); - assert_eq!(vec![2, 2, 2, 2], e2.message); -} diff --git a/xmtp_api_http/Cargo.toml b/xmtp_api_http/Cargo.toml new file mode 100644 index 000000000..58bd4ab5e --- /dev/null +++ b/xmtp_api_http/Cargo.toml @@ -0,0 +1,24 @@ +[package] +edition = "2021" +name = "xmtp_api_http" +version.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +async-stream.workspace = true +futures = { workspace = true } +tracing.workspace = true +reqwest = { version = "0.12.5", features = ["json", "stream"] } +serde = { workspace = true } +serde_json = { workspace = true } +thiserror = "1.0" +tokio = { workspace = true, features = ["sync", "rt", "macros"] } +xmtp_proto = { path = "../xmtp_proto", features = ["proto_full"] } + +[dev-dependencies] +tokio = { workspace = true, features = ["macros", "rt-multi-thread", "time"] } + +[profile.release] +opt-level = "s" diff --git a/xmtp_api_http/src/constants.rs b/xmtp_api_http/src/constants.rs new file mode 100644 index 000000000..e5df6b82f --- /dev/null +++ b/xmtp_api_http/src/constants.rs @@ -0,0 +1,24 @@ +pub struct ApiUrls; + +impl ApiUrls { + pub const LOCAL_ADDRESS: &'static str = "http://localhost:5555"; + pub const DEV_ADDRESS: &'static str = "https://dev.xmtp.network"; + pub const PRODUCTION_ADDRESS: &'static str = "https://production.xmtp.network"; +} + +pub struct ApiEndpoints; + +impl ApiEndpoints { + pub const FETCH_KEY_PACKAGES: &'static str = "/mls/v1/fetch-key-packages"; + pub const GET_IDENTITY_UPDATES: &'static str = "/identity/v1/get-identity-updates"; + pub const GET_INBOX_IDS: &'static str = "/identity/v1/get-inbox-ids"; + pub const PUBLISH_IDENTITY_UPDATE: &'static str = "/identity/v1/publish-identity-update"; + pub const QUERY_GROUP_MESSAGES: &'static str = "/mls/v1/query-group-messages"; + pub const QUERY_WELCOME_MESSAGES: &'static str = "/mls/v1/query-welcome-messages"; + pub const REGISTER_INSTALLATION: &'static str = "/mls/v1/register-installation"; + pub const SEND_GROUP_MESSAGES: &'static str = "/mls/v1/send-group-messages"; + pub const SEND_WELCOME_MESSAGES: &'static str = "/mls/v1/send-welcome-messages"; + pub const SUBSCRIBE_GROUP_MESSAGES: &'static str = "/mls/v1/subscribe-group-messages"; + pub const SUBSCRIBE_WELCOME_MESSAGES: &'static str = "/mls/v1/subscribe-welcome-messages"; + pub const UPLOAD_KEY_PACKAGE: &'static str = "/mls/v1/upload-key-package"; +} diff --git a/xmtp_api_http/src/lib.rs b/xmtp_api_http/src/lib.rs new file mode 100755 index 000000000..9ba77042f --- /dev/null +++ b/xmtp_api_http/src/lib.rs @@ -0,0 +1,363 @@ +#![warn(clippy::unwrap_used)] + +pub mod constants; +mod util; + +use futures::stream; +use reqwest::header; +use util::{create_grpc_stream, handle_error}; +use xmtp_proto::api_client::{ClientWithMetadata, Error, ErrorKind, XmtpIdentityClient}; +use xmtp_proto::xmtp::identity::api::v1::{ + GetIdentityUpdatesRequest as GetIdentityUpdatesV2Request, + GetIdentityUpdatesResponse as GetIdentityUpdatesV2Response, GetInboxIdsRequest, + GetInboxIdsResponse, PublishIdentityUpdateRequest, PublishIdentityUpdateResponse, +}; +use xmtp_proto::xmtp::mls::api::v1::{GroupMessage, WelcomeMessage}; +use xmtp_proto::{ + api_client::{XmtpMlsClient, XmtpMlsStreams}, + xmtp::mls::api::v1::{ + FetchKeyPackagesRequest, FetchKeyPackagesResponse, QueryGroupMessagesRequest, + QueryGroupMessagesResponse, QueryWelcomeMessagesRequest, QueryWelcomeMessagesResponse, + SendGroupMessagesRequest, SendWelcomeMessagesRequest, SubscribeGroupMessagesRequest, + SubscribeWelcomeMessagesRequest, UploadKeyPackageRequest, + }, +}; + +use crate::constants::ApiEndpoints; + +#[derive(Debug, thiserror::Error)] +pub enum HttpClientError { + #[error(transparent)] + Reqwest(#[from] reqwest::Error), +} + +#[cfg(target_arch = "wasm32")] +fn reqwest_builder() -> reqwest::ClientBuilder { + reqwest::Client::builder() +} + +#[cfg(not(target_arch = "wasm32"))] +fn reqwest_builder() -> reqwest::ClientBuilder { + reqwest::Client::builder().connection_verbose(true) +} + +pub struct XmtpHttpApiClient { + http_client: reqwest::Client, + host_url: String, + app_version: Option, + libxmtp_version: Option, +} + +impl XmtpHttpApiClient { + pub fn new(host_url: String) -> Result { + let client = reqwest_builder().build()?; + + Ok(XmtpHttpApiClient { + http_client: client, + host_url, + app_version: None, + libxmtp_version: None, + }) + } + + fn endpoint(&self, endpoint: &str) -> String { + format!("{}{}", self.host_url, endpoint) + } +} + +fn metadata_err(e: E) -> Error +where + E: Into>, +{ + Error::new(ErrorKind::MetadataError).with(e) +} + +impl ClientWithMetadata for XmtpHttpApiClient { + fn set_app_version(&mut self, version: String) -> Result<(), Error> { + self.app_version = Some(version); + + let mut headers = header::HeaderMap::new(); + if let Some(app_version) = &self.app_version { + headers.insert("x-app-version", app_version.parse().map_err(metadata_err)?); + } + if let Some(libxmtp_version) = &self.libxmtp_version { + headers.insert( + "x-libxmtp-version", + libxmtp_version.parse().map_err(metadata_err)?, + ); + } + self.http_client = reqwest_builder() + .default_headers(headers) + .build() + .map_err(metadata_err)?; + Ok(()) + } + fn set_libxmtp_version(&mut self, version: String) -> Result<(), Error> { + self.libxmtp_version = Some(version); + + let mut headers = header::HeaderMap::new(); + if let Some(app_version) = &self.app_version { + headers.insert( + "x-app-version", + app_version + .parse() + .map_err(|e| Error::new(ErrorKind::MetadataError).with(e))?, + ); + } + if let Some(libxmtp_version) = &self.libxmtp_version { + headers.insert( + "x-libxmtp-version", + libxmtp_version + .parse() + .map_err(|e| Error::new(ErrorKind::MetadataError).with(e))?, + ); + } + self.http_client = reqwest_builder() + .default_headers(headers) + .build() + .map_err(|e| Error::new(ErrorKind::MetadataError).with(e))?; + Ok(()) + } +} + +impl XmtpMlsClient for XmtpHttpApiClient { + async fn upload_key_package(&self, request: UploadKeyPackageRequest) -> Result<(), Error> { + let res = self + .http_client + .post(self.endpoint(ApiEndpoints::UPLOAD_KEY_PACKAGE)) + .json(&request) + .send() + .await + .map_err(|e| Error::new(ErrorKind::MlsError).with(e))? + .bytes() + .await + .map_err(|e| Error::new(ErrorKind::MlsError).with(e))?; + + tracing::debug!("upload_key_package"); + handle_error(&*res) + } + + async fn fetch_key_packages( + &self, + request: FetchKeyPackagesRequest, + ) -> Result { + let res = self + .http_client + .post(self.endpoint(ApiEndpoints::FETCH_KEY_PACKAGES)) + .json(&request) + .send() + .await + .map_err(|e| Error::new(ErrorKind::MlsError).with(e))? + .bytes() + .await + .map_err(|e| Error::new(ErrorKind::MlsError).with(e))?; + + tracing::debug!("fetch_key_packages"); + handle_error(&*res) + } + + async fn send_group_messages(&self, request: SendGroupMessagesRequest) -> Result<(), Error> { + let res = self + .http_client + .post(self.endpoint(ApiEndpoints::SEND_GROUP_MESSAGES)) + .json(&request) + .send() + .await + .map_err(|e| Error::new(ErrorKind::MlsError).with(e))? + .bytes() + .await + .map_err(|e| Error::new(ErrorKind::MlsError).with(e))?; + + tracing::debug!("send_group_messages"); + handle_error(&*res) + } + + async fn send_welcome_messages( + &self, + request: SendWelcomeMessagesRequest, + ) -> Result<(), Error> { + let res = self + .http_client + .post(self.endpoint(ApiEndpoints::SEND_WELCOME_MESSAGES)) + .json(&request) + .send() + .await + .map_err(|e| Error::new(ErrorKind::MlsError).with(e))? + .bytes() + .await + .map_err(|e| Error::new(ErrorKind::MlsError).with(e))?; + + tracing::debug!("send_welcome_messages"); + handle_error(&*res) + } + + async fn query_group_messages( + &self, + request: QueryGroupMessagesRequest, + ) -> Result { + let res = self + .http_client + .post(self.endpoint(ApiEndpoints::QUERY_GROUP_MESSAGES)) + .json(&request) + .send() + .await + .map_err(|e| Error::new(ErrorKind::MlsError).with(e))? + .bytes() + .await + .map_err(|e| Error::new(ErrorKind::MlsError).with(e))?; + + tracing::debug!("query_group_messages"); + handle_error(&*res) + } + + async fn query_welcome_messages( + &self, + request: QueryWelcomeMessagesRequest, + ) -> Result { + let res = self + .http_client + .post(self.endpoint(ApiEndpoints::QUERY_WELCOME_MESSAGES)) + .json(&request) + .send() + .await + .map_err(|e| Error::new(ErrorKind::MlsError).with(e))? + .bytes() + .await + .map_err(|e| Error::new(ErrorKind::MlsError).with(e))?; + + tracing::debug!("query_welcome_messages"); + handle_error(&*res) + } +} + +impl XmtpMlsStreams for XmtpHttpApiClient { + // hard to avoid boxing here: + // 1.) use `hyper` instead of `reqwest` and create our own `Stream` type + // 2.) ise `impl Stream` in return of `XmtpMlsStreams` but that + // breaks the `mockall::` functionality, since `mockall` does not support `impl Trait` in + // `Trait` yet. + + #[cfg(not(target_arch = "wasm32"))] + type GroupMessageStream<'a> = stream::BoxStream<'a, Result>; + #[cfg(not(target_arch = "wasm32"))] + type WelcomeMessageStream<'a> = stream::BoxStream<'a, Result>; + + #[cfg(target_arch = "wasm32")] + type GroupMessageStream<'a> = stream::LocalBoxStream<'a, Result>; + #[cfg(target_arch = "wasm32")] + type WelcomeMessageStream<'a> = stream::LocalBoxStream<'a, Result>; + + async fn subscribe_group_messages( + &self, + request: SubscribeGroupMessagesRequest, + ) -> Result, Error> { + tracing::debug!("subscribe_group_messages"); + Ok(create_grpc_stream::<_, GroupMessage>( + request, + self.endpoint(ApiEndpoints::SUBSCRIBE_GROUP_MESSAGES), + self.http_client.clone(), + )) + } + + async fn subscribe_welcome_messages( + &self, + request: SubscribeWelcomeMessagesRequest, + ) -> Result, Error> { + tracing::debug!("subscribe_welcome_messages"); + Ok(create_grpc_stream::<_, WelcomeMessage>( + request, + self.endpoint(ApiEndpoints::SUBSCRIBE_WELCOME_MESSAGES), + self.http_client.clone(), + )) + } +} + +impl XmtpIdentityClient for XmtpHttpApiClient { + async fn publish_identity_update( + &self, + request: PublishIdentityUpdateRequest, + ) -> Result { + let res = self + .http_client + .post(self.endpoint(ApiEndpoints::PUBLISH_IDENTITY_UPDATE)) + .json(&request) + .send() + .await + .map_err(|e| Error::new(ErrorKind::IdentityError).with(e))? + .bytes() + .await + .map_err(|e| Error::new(ErrorKind::IdentityError).with(e))?; + + tracing::debug!("publish_identity_update"); + handle_error(&*res) + } + + async fn get_identity_updates_v2( + &self, + request: GetIdentityUpdatesV2Request, + ) -> Result { + let res = self + .http_client + .post(self.endpoint(ApiEndpoints::GET_IDENTITY_UPDATES)) + .json(&request) + .send() + .await + .map_err(|e| Error::new(ErrorKind::IdentityError).with(e))? + .bytes() + .await + .map_err(|e| Error::new(ErrorKind::IdentityError).with(e))?; + + tracing::debug!("get_identity_updates_v2"); + handle_error(&*res) + } + + async fn get_inbox_ids( + &self, + request: GetInboxIdsRequest, + ) -> Result { + let res = self + .http_client + .post(self.endpoint(ApiEndpoints::GET_INBOX_IDS)) + .json(&request) + .send() + .await + .map_err(|e| Error::new(ErrorKind::IdentityError).with(e))? + .bytes() + .await + .map_err(|e| Error::new(ErrorKind::IdentityError).with(e))?; + + tracing::debug!("get_inbox_ids"); + handle_error(&*res) + } +} + +// tests +#[cfg(test)] +mod tests { + use xmtp_proto::xmtp::mls::api::v1::KeyPackageUpload; + + use crate::constants::ApiUrls; + + use super::*; + + #[tokio::test] + async fn test_upload_key_package() { + let client = XmtpHttpApiClient::new(ApiUrls::LOCAL_ADDRESS.to_string()).unwrap(); + let result = client + .upload_key_package(UploadKeyPackageRequest { + is_inbox_id_credential: false, + key_package: Some(KeyPackageUpload { + key_package_tls_serialized: vec![1, 2, 3], + }), + }) + .await; + + assert!(result.is_err()); + assert!(result + .as_ref() + .err() + .unwrap() + .to_string() + .contains("invalid identity")); + } +} diff --git a/xmtp_api_http/src/util.rs b/xmtp_api_http/src/util.rs new file mode 100644 index 000000000..80e86f5da --- /dev/null +++ b/xmtp_api_http/src/util.rs @@ -0,0 +1,127 @@ +use futures::{ + stream::{self, StreamExt}, + Stream, +}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde_json::Deserializer; +use std::io::Read; +use xmtp_proto::api_client::{Error, ErrorKind}; + +#[derive(Deserialize, Serialize, Debug)] +#[serde(untagged)] +pub(crate) enum GrpcResponse { + Ok(T), + Err(ErrorResponse), + SubscriptionItem(SubscriptionItem), + Empty {}, +} + +#[derive(Deserialize, Serialize, Debug)] +pub(crate) struct ErrorResponse { + code: usize, + pub message: String, + details: Vec, +} + +#[derive(Deserialize, Serialize, Debug)] +pub(crate) struct SubscriptionItem { + pub result: T, +} + +/// handle JSON response from gRPC, returning either +/// the expected deserialized response object or a gRPC [`Error`] +pub fn handle_error(reader: R) -> Result +where + T: DeserializeOwned + Default, +{ + match serde_json::from_reader(reader) { + Ok(GrpcResponse::Ok(response)) => Ok(response), + Ok(GrpcResponse::Err(e)) => Err(Error::new(ErrorKind::IdentityError).with(e.message)), + Ok(GrpcResponse::Empty {}) => Ok(Default::default()), + Ok(GrpcResponse::SubscriptionItem(item)) => Ok(item.result), + Err(e) => Err(Error::new(ErrorKind::QueryError).with(e.to_string())), + } +} + +#[cfg(target_arch = "wasm32")] +pub fn create_grpc_stream< + T: Serialize + Send + 'static, + R: DeserializeOwned + Send + std::fmt::Debug + 'static, +>( + request: T, + endpoint: String, + http_client: reqwest::Client, +) -> stream::LocalBoxStream<'static, Result> { + create_grpc_stream_inner(request, endpoint, http_client).boxed_local() +} + +#[cfg(not(target_arch = "wasm32"))] +pub fn create_grpc_stream< + T: Serialize + Send + 'static, + R: DeserializeOwned + Send + std::fmt::Debug + 'static, +>( + request: T, + endpoint: String, + http_client: reqwest::Client, +) -> stream::BoxStream<'static, Result> { + create_grpc_stream_inner(request, endpoint, http_client).boxed() +} + +pub fn create_grpc_stream_inner< + T: Serialize + Send + 'static, + R: DeserializeOwned + Send + std::fmt::Debug + 'static, +>( + request: T, + endpoint: String, + http_client: reqwest::Client, +) -> impl Stream> { + async_stream::stream! { + tracing::info!("Spawning grpc http stream"); + let request = http_client + .post(endpoint) + .json(&request) + .send() + .await + .map_err(|e| Error::new(ErrorKind::MlsError).with(e))?; + + let mut remaining = vec![]; + for await bytes in request.bytes_stream() { + let bytes = bytes + .map_err(|e| Error::new(ErrorKind::SubscriptionUpdateError).with(e.to_string()))?; + let bytes = &[remaining.as_ref(), bytes.as_ref()].concat(); + let de = Deserializer::from_slice(bytes); + let mut stream = de.into_iter::>(); + 'messages: loop { + let response = stream.next(); + let res = match response { + Some(Ok(GrpcResponse::Ok(response))) => Ok(response), + Some(Ok(GrpcResponse::SubscriptionItem(item))) => Ok(item.result), + Some(Ok(GrpcResponse::Err(e))) => { + Err(Error::new(ErrorKind::MlsError).with(e.message)) + } + Some(Err(e)) => { + if e.is_eof() { + remaining = (&**bytes)[stream.byte_offset()..].to_vec(); + break 'messages; + } else { + Err(Error::new(ErrorKind::MlsError).with(e.to_string())) + } + } + Some(Ok(GrpcResponse::Empty {})) => continue 'messages, + None => break 'messages, + }; + yield res; + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_error_handler_on_unit_value() { + handle_error::<_, ()>(b"{}".as_slice()).unwrap(); + } +} diff --git a/xmtp_cryptography/Cargo.toml b/xmtp_cryptography/Cargo.toml index e8cff7be9..14c5e3a02 100644 --- a/xmtp_cryptography/Cargo.toml +++ b/xmtp_cryptography/Cargo.toml @@ -1,17 +1,16 @@ [package] -name = "xmtp_cryptography" -version = "0.1.0" edition = "2021" -rust-version = "1.64" +name = "xmtp_cryptography" +rust-version = "1.70" +version.workspace = true [dependencies] +tracing.workspace = true curve25519-dalek = "4" -ecdsa = "0.15.1" +ecdsa = "0.16.9" ethers = { workspace = true } -ethers-core = { workspace = true } hex = { workspace = true } -k256 = { version = "0.12.0", features = ["ecdh"] } -log = { workspace = true } +k256 = { version = "0.13.3", features = ["ecdh"] } rand = { workspace = true } rand_chacha = "0.3.1" rustc-hex.workspace = true @@ -20,9 +19,11 @@ sha2 = "0.10.7" sha3 = "0.10.6" thiserror = { workspace = true } +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { workspace = true, features = ["js"] } + [features] ws = ["ethers/ws"] [dev-dependencies] - tokio = { version = "1.28.1", features = ["rt", "macros"] } diff --git a/xmtp_cryptography/src/signature.rs b/xmtp_cryptography/src/signature.rs index 4f25414fe..b33377831 100644 --- a/xmtp_cryptography/src/signature.rs +++ b/xmtp_cryptography/src/signature.rs @@ -1,6 +1,6 @@ use curve25519_dalek::{edwards::CompressedEdwardsY, traits::IsIdentity}; +use ethers::core::types::{self as ethers_types, H160}; use ethers::types::Address; -use ethers_core::types::{self as ethers_types, H160}; pub use k256::ecdsa::{RecoveryId, SigningKey, VerifyingKey}; use k256::Secp256k1; use serde::{Deserialize, Serialize}; @@ -98,8 +98,8 @@ impl From<(ecdsa::Signature, RecoveryId)> for RecoverableSignature { } } -impl From for RecoverableSignature { - fn from(value: ethers_core::types::Signature) -> Self { +impl From for RecoverableSignature { + fn from(value: ethers::core::types::Signature) -> Self { RecoverableSignature::Eip191Signature(value.to_vec()) } } @@ -168,7 +168,7 @@ pub fn is_valid_ed25519_public_key>(public_key: Bytes) -> boo let compressed = match CompressedEdwardsY::from_slice(public_key) { Ok(v) => v, Err(_) => { - log::debug!("Invalid ed22519 public key. Does not have length of 32"); + tracing::debug!("Invalid ed22519 public key. Does not have length of 32"); return false; } }; @@ -176,14 +176,14 @@ pub fn is_valid_ed25519_public_key>(public_key: Bytes) -> boo match compressed.decompress() { Some(point) => { if point.is_small_order() || point.is_identity() { - log::debug!( + tracing::debug!( "Invalid public key, not a point on the curve or is the identity element." ); return false; } } None => { - log::debug!("Not a valid ed25519 public key: Decompression failure"); + tracing::debug!("Not a valid ed25519 public key: Decompression failure"); return false; } } diff --git a/xmtp_cryptography/src/utils.rs b/xmtp_cryptography/src/utils.rs index efbc1c502..35e13d4ba 100644 --- a/xmtp_cryptography/src/utils.rs +++ b/xmtp_cryptography/src/utils.rs @@ -1,5 +1,5 @@ +use ethers::core::utils::keccak256; pub use ethers::prelude::LocalWallet; -use ethers_core::utils::keccak256; use k256::ecdsa::VerifyingKey; use rand::{CryptoRng, RngCore, SeedableRng}; use rand_chacha::ChaCha20Rng; diff --git a/xmtp_id/Cargo.toml b/xmtp_id/Cargo.toml index eb0b9fc78..0d3441100 100644 --- a/xmtp_id/Cargo.toml +++ b/xmtp_id/Cargo.toml @@ -1,22 +1,16 @@ [package] edition = "2021" name = "xmtp_id" -version = "0.1.0" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +version.workspace = true [dependencies] async-trait.workspace = true chrono.workspace = true ed25519-dalek = { workspace = true, features = ["digest"] } -ed25519.workspace = true ethers.workspace = true futures.workspace = true hex.workspace = true -log.workspace = true -openmls.workspace = true -openmls_basic_credential.workspace = true -openmls_rust_crypto.workspace = true +tracing.workspace = true openmls_traits.workspace = true prost.workspace = true rand.workspace = true @@ -25,8 +19,7 @@ rustc-hex.workspace = true serde.workspace = true sha2.workspace = true thiserror.workspace = true -tokio.workspace = true -tracing.workspace = true +tokio = { workspace = true, features = ["macros"] } url.workspace = true xmtp_cryptography.workspace = true xmtp_proto = { workspace = true, features = ["proto_full"] } @@ -34,10 +27,9 @@ xmtp_proto = { workspace = true, features = ["proto_full"] } [dev-dependencies] ctor = "0.2.5" ed25519-dalek = { workspace = true, features = ["digest"] } -ethers.workspace = true futures = "0.3" regex = "1.10" -tokio = { workspace = true, features = ["time"] } +tokio = { workspace = true, features = ["macros", "time"] } xmtp_v2 = { path = "../xmtp_v2" } [features] diff --git a/xmtp_id/src/associations/association_log.rs b/xmtp_id/src/associations/association_log.rs index 2bccf68d0..cc821d762 100644 --- a/xmtp_id/src/associations/association_log.rs +++ b/xmtp_id/src/associations/association_log.rs @@ -1,12 +1,10 @@ use super::hashes::generate_inbox_id; use super::member::{Member, MemberIdentifier, MemberKind}; -use super::serialization::{from_identity_update_proto, DeserializationError}; -use super::signature::{Signature, SignatureError, SignatureKind}; +use super::serialization::DeserializationError; +use super::signature::{SignatureError, SignatureKind}; use super::state::AssociationState; -use async_trait::async_trait; -use prost::Message; +use super::verified_signature::VerifiedSignature; use thiserror::Error; -use xmtp_proto::xmtp::identity::associations::IdentityUpdate as IdentityUpdateProto; #[derive(Debug, Error)] pub enum AssociationError { @@ -38,11 +36,11 @@ pub enum AssociationError { MissingIdentityUpdate, } -#[async_trait] pub trait IdentityAction: Send + 'static { - async fn update_state( + fn update_state( &self, existing_state: Option, + client_timestamp_ns: u64, ) -> Result; fn signatures(&self) -> Vec>; fn replay_check(&self, state: &AssociationState) -> Result<(), AssociationError> { @@ -62,34 +60,30 @@ pub trait IdentityAction: Send + 'static { pub struct CreateInbox { pub nonce: u64, pub account_address: String, - pub initial_address_signature: Box, + pub initial_address_signature: VerifiedSignature, } -#[async_trait] impl IdentityAction for CreateInbox { - async fn update_state( + fn update_state( &self, existing_state: Option, + _client_timestamp_ns: u64, ) -> Result { if existing_state.is_some() { return Err(AssociationError::MultipleCreate); } let account_address = self.account_address.clone(); - let recovered_signer = self.initial_address_signature.recover_signer().await?; + let recovered_signer = self.initial_address_signature.signer.clone(); if recovered_signer.ne(&MemberIdentifier::Address( account_address.clone().to_lowercase(), )) { return Err(AssociationError::MissingExistingMember); } - allowed_signature_for_kind( - &MemberKind::Address, - &self.initial_address_signature.signature_kind(), - )?; + allowed_signature_for_kind(&MemberKind::Address, &self.initial_address_signature.kind)?; - if self.initial_address_signature.signature_kind() == SignatureKind::LegacyDelegated - && self.nonce != 0 + if self.initial_address_signature.kind == SignatureKind::LegacyDelegated && self.nonce != 0 { return Err(AssociationError::LegacySignatureReuse); } @@ -98,31 +92,31 @@ impl IdentityAction for CreateInbox { } fn signatures(&self) -> Vec> { - vec![self.initial_address_signature.bytes()] + vec![self.initial_address_signature.raw_bytes.clone()] } } /// AddAssociation Action #[derive(Debug, Clone)] pub struct AddAssociation { - pub new_member_signature: Box, + pub new_member_signature: VerifiedSignature, pub new_member_identifier: MemberIdentifier, - pub existing_member_signature: Box, + pub existing_member_signature: VerifiedSignature, } -#[async_trait::async_trait] impl IdentityAction for AddAssociation { - async fn update_state( + fn update_state( &self, maybe_existing_state: Option, + client_timestamp_ns: u64, ) -> Result { let existing_state = maybe_existing_state.ok_or(AssociationError::NotCreated)?; self.replay_check(&existing_state)?; // Validate the new member signature and get the recovered signer - let new_member_address = self.new_member_signature.recover_signer().await?; + let new_member_address = &self.new_member_signature.signer; // Validate the existing member signature and get the recovedred signer - let existing_member_identifier = self.existing_member_signature.recover_signer().await?; + let existing_member_identifier = &self.existing_member_signature.signer; if new_member_address.ne(&self.new_member_identifier) { return Err(AssociationError::NewMemberIdSignatureMismatch); @@ -138,7 +132,7 @@ impl IdentityAction for AddAssociation { if (is_legacy_signature(&self.new_member_signature) || is_legacy_signature(&self.existing_member_signature)) && existing_state.inbox_id().ne(&generate_inbox_id( - &existing_member_identifier.to_string(), + existing_member_identifier.to_string().as_str(), &0, )) { @@ -147,10 +141,10 @@ impl IdentityAction for AddAssociation { allowed_signature_for_kind( &self.new_member_identifier.kind(), - &self.new_member_signature.signature_kind(), + &self.new_member_signature.kind, )?; - let existing_member = existing_state.get(&existing_member_identifier); + let existing_member = existing_state.get(existing_member_identifier); let existing_entity_id = match existing_member { // If there is an existing member of the XID, use that member's ID @@ -176,7 +170,7 @@ impl IdentityAction for AddAssociation { // Ensure that the existing member signature is correct for the existing member type allowed_signature_for_kind( &existing_entity_id.kind(), - &self.existing_member_signature.signature_kind(), + &self.existing_member_signature.kind, )?; // Ensure that the new member signature is correct for the new member type @@ -185,15 +179,19 @@ impl IdentityAction for AddAssociation { self.new_member_identifier.kind(), )?; - let new_member = Member::new(new_member_address, Some(existing_entity_id)); + let new_member = Member::new( + new_member_address.clone(), + Some(existing_entity_id), + Some(client_timestamp_ns), + ); Ok(existing_state.add(new_member)) } fn signatures(&self) -> Vec> { vec![ - self.existing_member_signature.bytes(), - self.new_member_signature.bytes(), + self.existing_member_signature.raw_bytes.clone(), + self.new_member_signature.raw_bytes.clone(), ] } } @@ -201,15 +199,15 @@ impl IdentityAction for AddAssociation { /// RevokeAssociation Action #[derive(Debug, Clone)] pub struct RevokeAssociation { - pub recovery_address_signature: Box, + pub recovery_address_signature: VerifiedSignature, pub revoked_member: MemberIdentifier, } -#[async_trait] impl IdentityAction for RevokeAssociation { - async fn update_state( + fn update_state( &self, maybe_existing_state: Option, + _client_timestamp_ns: u64, ) -> Result { let existing_state = maybe_existing_state.ok_or(AssociationError::NotCreated)?; self.replay_check(&existing_state)?; @@ -221,7 +219,7 @@ impl IdentityAction for RevokeAssociation { )); } // Don't need to check for replay here since revocation is idempotent - let recovery_signer = self.recovery_address_signature.recover_signer().await?; + let recovery_signer = &self.recovery_address_signature.signer; // Make sure there is a recovery address set on the state let state_recovery_address = existing_state.recovery_address(); @@ -250,22 +248,22 @@ impl IdentityAction for RevokeAssociation { } fn signatures(&self) -> Vec> { - vec![self.recovery_address_signature.bytes()] + vec![self.recovery_address_signature.raw_bytes.clone()] } } /// ChangeRecoveryAddress Action #[derive(Debug, Clone)] pub struct ChangeRecoveryAddress { - pub recovery_address_signature: Box, + pub recovery_address_signature: VerifiedSignature, pub new_recovery_address: String, } -#[async_trait] impl IdentityAction for ChangeRecoveryAddress { - async fn update_state( + fn update_state( &self, existing_state: Option, + _client_timestamp_ns: u64, ) -> Result { let existing_state = existing_state.ok_or(AssociationError::NotCreated)?; self.replay_check(&existing_state)?; @@ -277,7 +275,7 @@ impl IdentityAction for ChangeRecoveryAddress { )); } - let recovery_signer = self.recovery_address_signature.recover_signer().await?; + let recovery_signer = &self.recovery_address_signature.signer; if recovery_signer.ne(&existing_state.recovery_address().clone().into()) { return Err(AssociationError::MissingExistingMember); } @@ -286,7 +284,7 @@ impl IdentityAction for ChangeRecoveryAddress { } fn signatures(&self) -> Vec> { - vec![self.recovery_address_signature.bytes()] + vec![self.recovery_address_signature.raw_bytes.clone()] } } @@ -299,17 +297,23 @@ pub enum Action { ChangeRecoveryAddress(ChangeRecoveryAddress), } -#[async_trait] impl IdentityAction for Action { - async fn update_state( + fn update_state( &self, existing_state: Option, + client_timestamp_ns: u64, ) -> Result { match self { - Action::CreateInbox(event) => event.update_state(existing_state).await, - Action::AddAssociation(event) => event.update_state(existing_state).await, - Action::RevokeAssociation(event) => event.update_state(existing_state).await, - Action::ChangeRecoveryAddress(event) => event.update_state(existing_state).await, + Action::CreateInbox(event) => event.update_state(existing_state, client_timestamp_ns), + Action::AddAssociation(event) => { + event.update_state(existing_state, client_timestamp_ns) + } + Action::RevokeAssociation(event) => { + event.update_state(existing_state, client_timestamp_ns) + } + Action::ChangeRecoveryAddress(event) => { + event.update_state(existing_state, client_timestamp_ns) + } } } @@ -339,47 +343,22 @@ impl IdentityUpdate { client_timestamp_ns, } } - - pub fn to_proto(self) -> IdentityUpdateProto { - IdentityUpdateProto::from(self) - } - - pub fn from_proto(proto: IdentityUpdateProto) -> Result { - from_identity_update_proto(proto) - } -} - -impl TryFrom for IdentityUpdate { - type Error = DeserializationError; - - fn try_from(proto: IdentityUpdateProto) -> Result { - IdentityUpdate::from_proto(proto) - } -} - -impl TryFrom> for IdentityUpdate { - type Error = DeserializationError; - - fn try_from(bytes: Vec) -> Result { - let proto = IdentityUpdateProto::decode(bytes.as_slice())?; - IdentityUpdate::from_proto(proto) - } } -#[async_trait] impl IdentityAction for IdentityUpdate { - async fn update_state( + fn update_state( &self, existing_state: Option, + _client_timestamp_ns: u64, ) -> Result { let mut state = existing_state.clone(); for action in &self.actions { - state = Some(action.update_state(state).await?); + state = Some(action.update_state(state, self.client_timestamp_ns)?); } let new_state = state.ok_or(AssociationError::NotCreated)?; if new_state.inbox_id().ne(&self.inbox_id) { - log::error!( + tracing::error!( "state inbox id mismatch, old: {}, new: {}", self.inbox_id, new_state.inbox_id() @@ -401,8 +380,8 @@ impl IdentityAction for IdentityUpdate { } #[allow(clippy::borrowed_box)] -fn is_legacy_signature(signature: &Box) -> bool { - signature.signature_kind() == SignatureKind::LegacyDelegated +fn is_legacy_signature(signature: &VerifiedSignature) -> bool { + signature.kind == SignatureKind::LegacyDelegated } fn allowed_association( diff --git a/xmtp_id/src/associations/builder.rs b/xmtp_id/src/associations/builder.rs index 041b4aef4..553eb421e 100644 --- a/xmtp_id/src/associations/builder.rs +++ b/xmtp_id/src/associations/builder.rs @@ -4,17 +4,21 @@ use std::collections::{HashMap, HashSet}; -use crate::utils::now_ns; +use crate::{scw_verifier::SmartContractSignatureVerifier, utils::now_ns}; use thiserror::Error; use super::{ - association_log::{AddAssociation, ChangeRecoveryAddress, CreateInbox, RevokeAssociation}, unsigned_actions::{ SignatureTextCreator, UnsignedAction, UnsignedAddAssociation, UnsignedChangeRecoveryAddress, UnsignedCreateInbox, UnsignedIdentityUpdate, UnsignedRevokeAssociation, }, - Action, IdentityUpdate, MemberIdentifier, MemberKind, Signature, SignatureError, + unverified::{ + UnverifiedAction, UnverifiedAddAssociation, UnverifiedChangeRecoveryAddress, + UnverifiedCreateInbox, UnverifiedIdentityUpdate, UnverifiedRevokeAssociation, + UnverifiedSignature, + }, + MemberIdentifier, MemberKind, SignatureError, }; /// The SignatureField is used to map the signatures from a [SignatureRequest] back to the correct @@ -169,7 +173,7 @@ pub enum SignatureRequestError { pub struct SignatureRequest { pending_actions: Vec, signature_text: String, - signatures: HashMap>, + signatures: HashMap, client_timestamp_ns: u64, inbox_id: String, } @@ -218,19 +222,24 @@ impl SignatureRequest { pub async fn add_signature( &mut self, - signature: Box, + signature: UnverifiedSignature, + scw_verifier: &dyn SmartContractSignatureVerifier, ) -> Result<(), SignatureRequestError> { - let signer_identity = signature.recover_signer().await?; + let verified_sig = signature + .to_verified(self.signature_text.clone(), scw_verifier) + .await?; + let signer_identity = &verified_sig.signer; + let missing_signatures = self.missing_signatures(); - log::info!("Provided Signer: {}", signer_identity); - log::info!("Missing Signatures: {:?}", missing_signatures); + tracing::info!("Provided Signer: {}", signer_identity); + tracing::info!("Missing Signatures: {:?}", missing_signatures); // Make sure the signer is someone actually in the request - if !missing_signatures.contains(&signer_identity) { + if !missing_signatures.contains(signer_identity) { return Err(SignatureRequestError::UnknownSigner); } - self.signatures.insert(signer_identity, signature); + self.signatures.insert(verified_sig.signer, signature); Ok(()) } @@ -243,7 +252,7 @@ impl SignatureRequest { self.signature_text.clone() } - pub fn build_identity_update(self) -> Result { + pub fn build_identity_update(self) -> Result { if !self.is_ready() { return Err(SignatureRequestError::MissingSigner); } @@ -253,12 +262,12 @@ impl SignatureRequest { .clone() .into_iter() .map(|pending_action| build_action(pending_action, &self.signatures)) - .collect::, SignatureRequestError>>()?; + .collect::, SignatureRequestError>>()?; - Ok(IdentityUpdate::new( - actions, + Ok(UnverifiedIdentityUpdate::new( self.inbox_id, self.client_timestamp_ns, + actions, )) } @@ -269,8 +278,8 @@ impl SignatureRequest { fn build_action( pending_action: PendingIdentityAction, - signatures: &HashMap>, -) -> Result { + signatures: &HashMap, +) -> Result { match pending_action.unsigned_action { UnsignedAction::CreateInbox(unsigned_action) => { let signer_identity = pending_action @@ -282,9 +291,8 @@ fn build_action( .cloned() .ok_or(SignatureRequestError::MissingSigner)?; - Ok(Action::CreateInbox(CreateInbox { - nonce: unsigned_action.nonce, - account_address: unsigned_action.account_address, + Ok(UnverifiedAction::CreateInbox(UnverifiedCreateInbox { + unsigned_action, initial_address_signature, })) } @@ -308,8 +316,8 @@ fn build_action( .cloned() .ok_or(SignatureRequestError::MissingSigner)?; - Ok(Action::AddAssociation(AddAssociation { - new_member_identifier: unsigned_action.new_member_identifier, + Ok(UnverifiedAction::AddAssociation(UnverifiedAddAssociation { + unsigned_action, existing_member_signature, new_member_signature, })) @@ -324,10 +332,12 @@ fn build_action( .cloned() .ok_or(SignatureRequestError::MissingSigner)?; - Ok(Action::RevokeAssociation(RevokeAssociation { - recovery_address_signature, - revoked_member: unsigned_action.revoked_member, - })) + Ok(UnverifiedAction::RevokeAssociation( + UnverifiedRevokeAssociation { + recovery_address_signature, + unsigned_action, + }, + )) } UnsignedAction::ChangeRecoveryAddress(unsigned_action) => { let signer_identity = pending_action @@ -340,10 +350,12 @@ fn build_action( .cloned() .ok_or(SignatureRequestError::MissingSigner)?; - Ok(Action::ChangeRecoveryAddress(ChangeRecoveryAddress { - recovery_address_signature, - new_recovery_address: unsigned_action.new_recovery_address, - })) + Ok(UnverifiedAction::ChangeRecoveryAddress( + UnverifiedChangeRecoveryAddress { + recovery_address_signature, + unsigned_action, + }, + )) } } } @@ -364,84 +376,85 @@ fn get_signature_text( #[cfg(test)] mod tests { - use crate::associations::{ - get_state, - hashes::generate_inbox_id, - test_utils::{rand_string, rand_vec, MockSignature}, - MemberKind, SignatureKind, + use ethers::signers::{LocalWallet, Signer}; + + use crate::{ + associations::{ + get_state, + hashes::generate_inbox_id, + test_utils::{ + add_installation_key_signature, add_wallet_signature, + MockSmartContractSignatureVerifier, + }, + unverified::UnverifiedRecoverableEcdsaSignature, + IdentityUpdate, + }, + InboxOwner, }; + use ed25519_dalek::SigningKey as Ed25519SigningKey; use super::*; - // Helper function to add all the missing signatures, since we don't have real signers available - async fn add_missing_signatures_to_request(signature_request: &mut SignatureRequest) { - let missing_signatures = signature_request.missing_signatures(); - for member_identifier in missing_signatures { - let signature_kind = match member_identifier.kind() { - MemberKind::Address => SignatureKind::Erc191, - MemberKind::Installation => SignatureKind::InstallationKey, - }; - - signature_request - .add_signature(MockSignature::new_boxed( - true, - member_identifier.clone(), - signature_kind, - Some(signature_request.signature_text()), - )) - .await - .expect("should succeed"); - } + async fn convert_to_verified(identity_update: &UnverifiedIdentityUpdate) -> IdentityUpdate { + let scw_verifier = MockSmartContractSignatureVerifier::new(false); + identity_update + .to_verified(&scw_verifier) + .await + .expect("should be valid") } #[tokio::test] async fn create_inbox() { - let account_address = "account_address".to_string(); + let wallet = LocalWallet::new(&mut rand::thread_rng()); + let account_address = wallet.get_address(); let nonce = 0; let inbox_id = generate_inbox_id(&account_address, &nonce); + let mut signature_request = SignatureRequestBuilder::new(inbox_id) .create_inbox(account_address.into(), nonce) .build(); - add_missing_signatures_to_request(&mut signature_request).await; + add_wallet_signature(&mut signature_request, &wallet).await; let identity_update = signature_request .build_identity_update() .expect("should be valid"); - get_state(vec![identity_update]) - .await - .expect("should be valid"); + get_state(vec![convert_to_verified(&identity_update).await]).expect("should be valid"); } #[tokio::test] async fn create_and_add_identity() { - let account_address = "account_address".to_string(); + let wallet = LocalWallet::new(&mut rand::thread_rng()); + let installation_key = Ed25519SigningKey::generate(&mut rand::thread_rng()); + let account_address = wallet.get_address(); + let installation_key_id = installation_key.verifying_key().as_bytes().to_vec(); let nonce = 0; let inbox_id = generate_inbox_id(&account_address, &nonce); let existing_member_identifier: MemberIdentifier = account_address.into(); - let new_member_identifier: MemberIdentifier = rand_vec().into(); + let new_member_identifier: MemberIdentifier = installation_key_id.into(); let mut signature_request = SignatureRequestBuilder::new(inbox_id) .create_inbox(existing_member_identifier.clone(), nonce) .add_association(new_member_identifier, existing_member_identifier) .build(); - add_missing_signatures_to_request(&mut signature_request).await; + add_wallet_signature(&mut signature_request, &wallet).await; + add_installation_key_signature(&mut signature_request, &installation_key).await; let identity_update = signature_request .build_identity_update() .expect("should be valid"); - let state = get_state(vec![identity_update]) - .await - .expect("should be valid"); + let state = + get_state(vec![convert_to_verified(&identity_update).await]).expect("should be valid"); assert_eq!(state.members().len(), 2); } #[tokio::test] async fn create_and_revoke() { - let account_address = "account_address".to_string(); + let wallet = LocalWallet::new(&mut rand::thread_rng()); + let account_address = wallet.get_address(); let nonce = 0; let inbox_id = generate_inbox_id(&account_address, &nonce); let existing_member_identifier: MemberIdentifier = account_address.clone().into(); @@ -451,15 +464,14 @@ mod tests { .revoke_association(existing_member_identifier.clone(), account_address.into()) .build(); - add_missing_signatures_to_request(&mut signature_request).await; + add_wallet_signature(&mut signature_request, &wallet).await; let identity_update = signature_request .build_identity_update() .expect("should be valid"); - let state = get_state(vec![identity_update]) - .await - .expect("should be valid"); + let state = + get_state(vec![convert_to_verified(&identity_update).await]).expect("should be valid"); assert_eq!(state.members().len(), 0); } @@ -473,13 +485,20 @@ mod tests { .create_inbox(account_address.into(), nonce) .build(); + let rand_wallet = LocalWallet::new(&mut rand::thread_rng()); + + let signature_text = signature_request.signature_text(); + let sig = rand_wallet + .sign_message(signature_text) + .await + .unwrap() + .to_vec(); + let unverified_sig = + UnverifiedSignature::RecoverableEcdsa(UnverifiedRecoverableEcdsaSignature::new(sig)); + let scw_verifier = MockSmartContractSignatureVerifier::new(false); + let attempt_to_add_random_member = signature_request - .add_signature(MockSignature::new_boxed( - true, - rand_string().into(), - SignatureKind::Erc191, - None, - )) + .add_signature(unverified_sig, &scw_verifier) .await; assert!(matches!( diff --git a/xmtp_id/src/associations/member.rs b/xmtp_id/src/associations/member.rs index 44a31cde9..3159a6110 100644 --- a/xmtp_id/src/associations/member.rs +++ b/xmtp_id/src/associations/member.rs @@ -77,13 +77,19 @@ impl From> for MemberIdentifier { pub struct Member { pub identifier: MemberIdentifier, pub added_by_entity: Option, + pub client_timestamp_ns: Option, } impl Member { - pub fn new(identifier: MemberIdentifier, added_by_entity: Option) -> Self { + pub fn new( + identifier: MemberIdentifier, + added_by_entity: Option, + client_timestamp_ns: Option, + ) -> Self { Self { identifier, added_by_entity, + client_timestamp_ns, } } @@ -118,6 +124,7 @@ mod tests { Self { identifier: MemberIdentifier::default(), added_by_entity: None, + client_timestamp_ns: None, } } } diff --git a/xmtp_id/src/associations/mod.rs b/xmtp_id/src/associations/mod.rs index 198a84a26..022a948bf 100644 --- a/xmtp_id/src/associations/mod.rs +++ b/xmtp_id/src/associations/mod.rs @@ -8,6 +8,8 @@ mod state; #[cfg(any(test, feature = "test-utils"))] pub mod test_utils; pub mod unsigned_actions; +pub mod unverified; +pub mod verified_signature; pub use self::association_log::*; pub use self::hashes::generate_inbox_id; @@ -17,20 +19,20 @@ pub use self::signature::*; pub use self::state::{AssociationState, AssociationStateDiff}; // Apply a single IdentityUpdate to an existing AssociationState -pub async fn apply_update( +pub fn apply_update( initial_state: AssociationState, update: IdentityUpdate, ) -> Result { - update.update_state(Some(initial_state)).await + update.update_state(Some(initial_state), update.client_timestamp_ns) } // Get the current state from an array of `IdentityUpdate`s. Entire operation fails if any operation fails -pub async fn get_state>( +pub fn get_state>( updates: Updates, ) -> Result { let mut state = None; for update in updates.as_ref().iter() { - let res = update.update_state(state).await; + let res = update.update_state(state, update.client_timestamp_ns); state = Some(res?); } @@ -39,7 +41,11 @@ pub async fn get_state>( #[cfg(any(test, feature = "test-utils"))] pub mod test_defaults { - use self::test_utils::{rand_string, rand_u64, rand_vec, MockSignature}; + use self::{ + test_utils::{rand_string, rand_u64, rand_vec}, + unverified::{UnverifiedAction, UnverifiedIdentityUpdate}, + verified_signature::VerifiedSignature, + }; use super::*; impl IdentityUpdate { @@ -48,22 +54,26 @@ pub mod test_defaults { } } + impl UnverifiedIdentityUpdate { + pub fn new_test(actions: Vec, inbox_id: String) -> Self { + Self::new(inbox_id, rand_u64(), actions) + } + } + impl Default for AddAssociation { fn default() -> Self { let existing_member = rand_string(); let new_member = rand_vec(); Self { - existing_member_signature: MockSignature::new_boxed( - true, + existing_member_signature: VerifiedSignature::new( existing_member.into(), SignatureKind::Erc191, - None, + rand_vec(), ), - new_member_signature: MockSignature::new_boxed( - true, + new_member_signature: VerifiedSignature::new( new_member.clone().into(), SignatureKind::InstallationKey, - None, + rand_vec(), ), new_member_identifier: new_member.into(), } @@ -77,11 +87,10 @@ pub mod test_defaults { Self { nonce: rand_u64(), account_address: signer.clone(), - initial_address_signature: MockSignature::new_boxed( - true, + initial_address_signature: VerifiedSignature::new( signer.into(), SignatureKind::Erc191, - None, + rand_vec(), ), } } @@ -91,11 +100,10 @@ pub mod test_defaults { fn default() -> Self { let signer = rand_string(); Self { - recovery_address_signature: MockSignature::new_boxed( - true, + recovery_address_signature: VerifiedSignature::new( signer.into(), SignatureKind::Erc191, - None, + rand_vec(), ), revoked_member: rand_string().into(), } @@ -105,7 +113,10 @@ pub mod test_defaults { #[cfg(test)] mod tests { - use self::test_utils::{rand_string, rand_vec, MockSignature}; + use self::{ + test_utils::{rand_string, rand_vec}, + verified_signature::VerifiedSignature, + }; use super::*; pub async fn new_test_inbox() -> AssociationState { @@ -114,7 +125,7 @@ mod tests { let identity_update = IdentityUpdate::new_test(vec![Action::CreateInbox(create_request)], inbox_id); - get_state(vec![identity_update]).await.unwrap() + get_state(vec![identity_update]).unwrap() } pub async fn new_test_inbox_with_installation() -> AssociationState { @@ -124,11 +135,10 @@ mod tests { initial_state.recovery_address().clone().into(); let update = Action::AddAssociation(AddAssociation { - existing_member_signature: MockSignature::new_boxed( - true, + existing_member_signature: VerifiedSignature::new( initial_wallet_address.clone(), SignatureKind::Erc191, - None, + rand_vec(), ), ..Default::default() }); @@ -137,7 +147,6 @@ mod tests { initial_state, IdentityUpdate::new_test(vec![update], inbox_id.clone()), ) - .await .unwrap() } @@ -148,7 +157,7 @@ mod tests { let account_address = create_request.account_address.clone(); let identity_update = IdentityUpdate::new_test(vec![Action::CreateInbox(create_request)], inbox_id.clone()); - let state = get_state(vec![identity_update]).await.unwrap(); + let state = get_state(vec![identity_update]).unwrap(); assert_eq!(state.members().len(), 1); let existing_entity = state.get(&account_address.clone().into()).unwrap(); @@ -164,17 +173,15 @@ mod tests { let update = Action::AddAssociation(AddAssociation { new_member_identifier: new_installation_identifier.clone(), - new_member_signature: MockSignature::new_boxed( - true, + new_member_signature: VerifiedSignature::new( new_installation_identifier.clone(), SignatureKind::InstallationKey, - None, + rand_vec(), ), - existing_member_signature: MockSignature::new_boxed( - true, + existing_member_signature: VerifiedSignature::new( first_member.clone(), SignatureKind::Erc191, - None, + rand_vec(), ), }); @@ -182,7 +189,6 @@ mod tests { initial_state, IdentityUpdate::new_test(vec![update], inbox_id.clone()), ) - .await .unwrap(); assert_eq!(new_state.members().len(), 2); @@ -197,18 +203,16 @@ mod tests { let inbox_id = generate_inbox_id(&account_address, &create_action.nonce); let new_member_identifier: MemberIdentifier = rand_vec().into(); let add_action = AddAssociation { - existing_member_signature: MockSignature::new_boxed( - true, + existing_member_signature: VerifiedSignature::new( account_address.clone().into(), SignatureKind::Erc191, - None, + rand_vec(), ), // Add an installation ID - new_member_signature: MockSignature::new_boxed( - true, + new_member_signature: VerifiedSignature::new( new_member_identifier.clone(), SignatureKind::InstallationKey, - None, + rand_vec(), ), new_member_identifier: new_member_identifier.clone(), }; @@ -219,7 +223,7 @@ mod tests { ], inbox_id.clone(), ); - let state = get_state(vec![identity_update]).await.unwrap(); + let state = get_state(vec![identity_update]).unwrap(); assert_eq!(state.members().len(), 2); assert_eq!( state.get(&new_member_identifier).unwrap().added_by_entity, @@ -233,11 +237,10 @@ mod tests { let create_action = CreateInbox { nonce: 0, account_address: member_identifier.to_string(), - initial_address_signature: MockSignature::new_boxed( - true, + initial_address_signature: VerifiedSignature::new( member_identifier.clone(), SignatureKind::LegacyDelegated, - Some("0".to_string()), + "0".as_bytes().to_vec(), ), }; let inbox_id = generate_inbox_id(&member_identifier.to_string(), &0); @@ -245,26 +248,23 @@ mod tests { vec![Action::CreateInbox(create_action)], inbox_id.clone(), )]) - .await .unwrap(); assert_eq!(state.members().len(), 1); // The legacy key can only be used once. After this, subsequent updates should fail let update = Action::AddAssociation(AddAssociation { - existing_member_signature: MockSignature::new_boxed( - true, + existing_member_signature: VerifiedSignature::new( member_identifier, SignatureKind::LegacyDelegated, // All requests from the same legacy key will have the same signature nonce - Some("0".to_string()), + "0".as_bytes().to_vec(), ), ..Default::default() }); let update_result = apply_update( state, IdentityUpdate::new_test(vec![update], inbox_id.clone()), - ) - .await; + ); assert!(matches!(update_result, Err(AssociationError::Replay))); } @@ -282,17 +282,15 @@ mod tests { let new_wallet_address: MemberIdentifier = rand_string().into(); let add_association = Action::AddAssociation(AddAssociation { new_member_identifier: new_wallet_address.clone(), - new_member_signature: MockSignature::new_boxed( - true, + new_member_signature: VerifiedSignature::new( new_wallet_address.clone(), SignatureKind::Erc191, - None, + rand_vec(), ), - existing_member_signature: MockSignature::new_boxed( - true, + existing_member_signature: VerifiedSignature::new( installation_id.clone(), SignatureKind::InstallationKey, - None, + rand_vec(), ), }); @@ -300,29 +298,29 @@ mod tests { initial_state, IdentityUpdate::new_test(vec![add_association], inbox_id.clone()), ) - .await .expect("expected update to succeed"); assert_eq!(new_state.members().len(), 3); } #[tokio::test] async fn reject_invalid_signature_on_create() { + // Creates a signature with the wrong signer let bad_signature = - MockSignature::new_boxed(false, rand_string().into(), SignatureKind::Erc191, None); + VerifiedSignature::new(rand_string().into(), SignatureKind::Erc191, rand_vec()); let action = CreateInbox { - initial_address_signature: bad_signature.clone(), + initial_address_signature: bad_signature, ..Default::default() }; let state_result = get_state(vec![IdentityUpdate::new_test( vec![Action::CreateInbox(action)], rand_string(), - )]) - .await; + )]); + assert!(state_result.is_err()); assert!(matches!( state_result, - Err(AssociationError::Signature(SignatureError::Invalid)) + Err(AssociationError::MissingExistingMember) )); } @@ -330,8 +328,9 @@ mod tests { async fn reject_invalid_signature_on_update() { let initial_state = new_test_inbox().await; let inbox_id = initial_state.inbox_id().clone(); + // Signature is from a random address let bad_signature = - MockSignature::new_boxed(false, rand_string().into(), SignatureKind::Erc191, None); + VerifiedSignature::new(rand_string().into(), SignatureKind::Erc191, rand_vec()); let update_with_bad_existing_member = Action::AddAssociation(AddAssociation { existing_member_signature: bad_signature.clone(), @@ -341,20 +340,19 @@ mod tests { let update_result = apply_update( initial_state.clone(), IdentityUpdate::new_test(vec![update_with_bad_existing_member], inbox_id.clone()), - ) - .await; + ); + assert!(matches!( update_result, - Err(AssociationError::Signature(SignatureError::Invalid)) + Err(AssociationError::MissingExistingMember) )); let update_with_bad_new_member = Action::AddAssociation(AddAssociation { new_member_signature: bad_signature.clone(), - existing_member_signature: MockSignature::new_boxed( - true, + existing_member_signature: VerifiedSignature::new( initial_state.recovery_address().clone().into(), SignatureKind::Erc191, - None, + rand_vec(), ), ..Default::default() }); @@ -362,11 +360,10 @@ mod tests { let update_result_2 = apply_update( initial_state, IdentityUpdate::new_test(vec![update_with_bad_new_member], inbox_id.clone()), - ) - .await; + ); assert!(matches!( update_result_2, - Err(AssociationError::Signature(SignatureError::Invalid)) + Err(AssociationError::NewMemberIdSignatureMismatch) )); } @@ -378,11 +375,10 @@ mod tests { // The default here will create an AddAssociation from a random wallet let update = Action::AddAssociation(AddAssociation { // Existing member signature is coming from a random wallet - existing_member_signature: MockSignature::new_boxed( - true, + existing_member_signature: VerifiedSignature::new( rand_string().into(), SignatureKind::Erc191, - None, + rand_vec(), ), ..Default::default() }); @@ -390,8 +386,7 @@ mod tests { let state_result = get_state(vec![IdentityUpdate::new_test( vec![create_request, update], inbox_id.clone(), - )]) - .await; + )]); assert!(matches!( state_result, Err(AssociationError::MissingExistingMember) @@ -407,26 +402,23 @@ mod tests { let new_installation_id: MemberIdentifier = rand_vec().into(); let update = Action::AddAssociation(AddAssociation { - existing_member_signature: MockSignature::new_boxed( - true, + existing_member_signature: VerifiedSignature::new( existing_installation.identifier.clone(), SignatureKind::InstallationKey, - None, + rand_vec(), ), new_member_identifier: new_installation_id.clone(), - new_member_signature: MockSignature::new_boxed( - true, + new_member_signature: VerifiedSignature::new( new_installation_id.clone(), SignatureKind::InstallationKey, - None, + rand_vec(), ), }); let update_result = apply_update( existing_state, IdentityUpdate::new_test(vec![update], inbox_id.clone()), - ) - .await; + ); assert!(matches!( update_result, Err(AssociationError::MemberNotAllowed( @@ -447,11 +439,10 @@ mod tests { .unwrap() .identifier; let update = Action::RevokeAssociation(RevokeAssociation { - recovery_address_signature: MockSignature::new_boxed( - true, + recovery_address_signature: VerifiedSignature::new( initial_state.recovery_address().clone().into(), SignatureKind::Erc191, - None, + rand_vec(), ), revoked_member: installation_id.clone(), }); @@ -460,7 +451,6 @@ mod tests { initial_state, IdentityUpdate::new_test(vec![update], inbox_id.clone()), ) - .await .expect("expected update to succeed"); assert!(new_state.get(&installation_id).is_none()); } @@ -477,11 +467,10 @@ mod tests { .identifier; let add_second_installation = Action::AddAssociation(AddAssociation { - existing_member_signature: MockSignature::new_boxed( - true, + existing_member_signature: VerifiedSignature::new( wallet_address.clone(), SignatureKind::Erc191, - None, + rand_vec(), ), ..Default::default() }); @@ -490,16 +479,14 @@ mod tests { initial_state, IdentityUpdate::new_test(vec![add_second_installation], inbox_id.clone()), ) - .await .expect("expected update to succeed"); assert_eq!(new_state.members().len(), 3); let revocation = Action::RevokeAssociation(RevokeAssociation { - recovery_address_signature: MockSignature::new_boxed( - true, + recovery_address_signature: VerifiedSignature::new( wallet_address.clone(), SignatureKind::Erc191, - None, + rand_vec(), ), revoked_member: wallet_address.clone(), }); @@ -509,7 +496,6 @@ mod tests { new_state, IdentityUpdate::new_test(vec![revocation], inbox_id.clone()), ) - .await .expect("expected update to succeed"); assert_eq!(new_state.members().len(), 0); } @@ -529,26 +515,23 @@ mod tests { let second_wallet_address: MemberIdentifier = rand_string().into(); let add_second_wallet = Action::AddAssociation(AddAssociation { new_member_identifier: second_wallet_address.clone(), - new_member_signature: MockSignature::new_boxed( - true, + new_member_signature: VerifiedSignature::new( second_wallet_address.clone(), SignatureKind::Erc191, - None, + rand_vec(), ), - existing_member_signature: MockSignature::new_boxed( - true, + existing_member_signature: VerifiedSignature::new( wallet_address.clone(), SignatureKind::Erc191, - None, + rand_vec(), ), }); let revoke_second_wallet = Action::RevokeAssociation(RevokeAssociation { - recovery_address_signature: MockSignature::new_boxed( - true, + recovery_address_signature: VerifiedSignature::new( wallet_address.clone(), SignatureKind::Erc191, - None, + rand_vec(), ), revoked_member: second_wallet_address.clone(), }); @@ -560,23 +543,20 @@ mod tests { inbox_id.clone(), ), ) - .await .expect("expected update to succeed"); assert_eq!(state_after_remove.members().len(), 1); let add_second_wallet_again = Action::AddAssociation(AddAssociation { new_member_identifier: second_wallet_address.clone(), - new_member_signature: MockSignature::new_boxed( - true, + new_member_signature: VerifiedSignature::new( second_wallet_address.clone(), SignatureKind::Erc191, - None, + rand_vec(), ), - existing_member_signature: MockSignature::new_boxed( - true, + existing_member_signature: VerifiedSignature::new( wallet_address, SignatureKind::Erc191, - None, + rand_vec(), ), }); @@ -584,7 +564,6 @@ mod tests { state_after_remove, IdentityUpdate::new_test(vec![add_second_wallet_again], inbox_id.clone()), ) - .await .expect("expected update to succeed"); assert_eq!(state_after_re_add.members().len(), 2); } @@ -598,11 +577,10 @@ mod tests { let new_recovery_address = rand_string(); let update_recovery = Action::ChangeRecoveryAddress(ChangeRecoveryAddress { new_recovery_address: new_recovery_address.clone(), - recovery_address_signature: MockSignature::new_boxed( - true, + recovery_address_signature: VerifiedSignature::new( initial_state.recovery_address().clone().into(), SignatureKind::Erc191, - None, + rand_vec(), ), }); @@ -610,16 +588,14 @@ mod tests { initial_state, IdentityUpdate::new_test(vec![update_recovery], inbox_id.clone()), ) - .await .expect("expected update to succeed"); assert_eq!(new_state.recovery_address(), &new_recovery_address); let attempted_revoke = Action::RevokeAssociation(RevokeAssociation { - recovery_address_signature: MockSignature::new_boxed( - true, + recovery_address_signature: VerifiedSignature::new( initial_recovery_address.clone(), SignatureKind::Erc191, - None, + rand_vec(), ), revoked_member: initial_recovery_address.clone(), }); @@ -627,8 +603,7 @@ mod tests { let revoke_result = apply_update( new_state, IdentityUpdate::new_test(vec![attempted_revoke], inbox_id.clone()), - ) - .await; + ); assert!(revoke_result.is_err()); assert!(matches!( revoke_result, diff --git a/xmtp_id/src/associations/serialization.rs b/xmtp_id/src/associations/serialization.rs index 10de98549..2f34f33e0 100644 --- a/xmtp_id/src/associations/serialization.rs +++ b/xmtp_id/src/associations/serialization.rs @@ -1,26 +1,26 @@ use std::collections::{HashMap, HashSet}; use super::{ - association_log::{ - Action, AddAssociation, ChangeRecoveryAddress, CreateInbox, RevokeAssociation, - }, member::Member, - signature::{ - AccountId, InstallationKeySignature, LegacyDelegatedSignature, RecoverableEcdsaSignature, - SmartContractWalletSignature, ValidatedLegacySignedPublicKey, - }, + signature::{AccountId, ValidatedLegacySignedPublicKey}, state::{AssociationState, AssociationStateDiff}, unsigned_actions::{ - SignatureTextCreator, UnsignedAction, UnsignedAddAssociation, - UnsignedChangeRecoveryAddress, UnsignedCreateInbox, UnsignedIdentityUpdate, + UnsignedAddAssociation, UnsignedChangeRecoveryAddress, UnsignedCreateInbox, UnsignedRevokeAssociation, }, - IdentityUpdate, MemberIdentifier, Signature, SignatureError, + unverified::{ + UnverifiedAction, UnverifiedAddAssociation, UnverifiedChangeRecoveryAddress, + UnverifiedCreateInbox, UnverifiedIdentityUpdate, UnverifiedInstallationKeySignature, + UnverifiedLegacyDelegatedSignature, UnverifiedRecoverableEcdsaSignature, + UnverifiedRevokeAssociation, UnverifiedSignature, UnverifiedSmartContractWalletSignature, + }, + verified_signature::VerifiedSignature, + MemberIdentifier, SignatureError, }; use prost::{DecodeError, Message}; use regex::Regex; use thiserror::Error; -use xmtp_cryptography::signature::{sanitize_evm_addresses, RecoverableSignature}; +use xmtp_cryptography::signature::sanitize_evm_addresses; use xmtp_proto::xmtp::{ identity::associations::{ identity_action::Kind as IdentityActionKindProto, @@ -30,9 +30,12 @@ use xmtp_proto::xmtp::{ AssociationStateDiff as AssociationStateDiffProto, ChangeRecoveryAddress as ChangeRecoveryAddressProto, CreateInbox as CreateInboxProto, IdentityAction as IdentityActionProto, IdentityUpdate as IdentityUpdateProto, - Member as MemberProto, MemberIdentifier as MemberIdentifierProto, - MemberMap as MemberMapProto, RevokeAssociation as RevokeAssociationProto, - Signature as SignatureWrapperProto, + LegacyDelegatedSignature as LegacyDelegatedSignatureProto, Member as MemberProto, + MemberIdentifier as MemberIdentifierProto, MemberMap as MemberMapProto, + RecoverableEcdsaSignature as RecoverableEcdsaSignatureProto, + RecoverableEd25519Signature as RecoverableEd25519SignatureProto, + RevokeAssociation as RevokeAssociationProto, Signature as SignatureWrapperProto, + SmartContractWalletSignature as SmartContractWalletSignatureProto, }, message_contents::{ signature::{Union, WalletEcdsaCompact}, @@ -62,252 +65,250 @@ pub enum DeserializationError { InvalidAccountId, } -pub fn from_identity_update_proto( - proto: IdentityUpdateProto, -) -> Result { - let client_timestamp_ns = proto.client_timestamp_ns; - let inbox_id = proto.inbox_id; - let all_actions = proto - .actions - .into_iter() - .map(|action| match action.kind { - Some(action) => Ok(action), - None => Err(DeserializationError::MissingAction), - }) - .collect::, DeserializationError>>()?; +impl TryFrom for UnverifiedIdentityUpdate { + type Error = DeserializationError; + + fn try_from(proto: IdentityUpdateProto) -> Result { + let IdentityUpdateProto { + client_timestamp_ns, + inbox_id, + actions, + } = proto; + let all_actions = actions + .into_iter() + .map(|action| match action.kind { + Some(action) => Ok(action), + None => Err(DeserializationError::MissingAction), + }) + .collect::, DeserializationError>>()?; + + let processed_actions: Vec = all_actions + .into_iter() + .map(UnverifiedAction::try_from) + .collect::, DeserializationError>>()?; + + Ok(UnverifiedIdentityUpdate::new( + inbox_id, + client_timestamp_ns, + processed_actions, + )) + } +} - let signature_text = get_signature_text(&all_actions, inbox_id.clone(), client_timestamp_ns)?; +impl TryFrom for UnverifiedAction { + type Error = DeserializationError; - let processed_actions: Vec = all_actions - .into_iter() - .map(|action| match action { + fn try_from(action: IdentityActionKindProto) -> Result { + Ok(match action { IdentityActionKindProto::Add(add_action) => { - Ok(Action::AddAssociation(AddAssociation { - new_member_signature: from_signature_proto_option( - add_action.new_member_signature, - signature_text.clone(), - )?, - existing_member_signature: from_signature_proto_option( - add_action.existing_member_signature, - signature_text.clone(), - )?, - new_member_identifier: from_member_identifier_proto_option( - add_action.new_member_identifier, - )?, - })) + UnverifiedAction::AddAssociation(UnverifiedAddAssociation { + new_member_signature: add_action.new_member_signature.try_into()?, + existing_member_signature: add_action.existing_member_signature.try_into()?, + unsigned_action: UnsignedAddAssociation { + new_member_identifier: add_action + .new_member_identifier + .ok_or(DeserializationError::MissingMemberIdentifier)? + .try_into()?, + }, + }) } - IdentityActionKindProto::CreateInbox(create_inbox_action) => { - Ok(Action::CreateInbox(CreateInbox { - nonce: create_inbox_action.nonce, - account_address: create_inbox_action.initial_address, - initial_address_signature: from_signature_proto_option( - create_inbox_action.initial_address_signature, - signature_text.clone(), - )?, - })) + IdentityActionKindProto::CreateInbox(action_proto) => { + UnverifiedAction::CreateInbox(UnverifiedCreateInbox { + initial_address_signature: action_proto.initial_address_signature.try_into()?, + unsigned_action: UnsignedCreateInbox { + nonce: action_proto.nonce, + account_address: action_proto.initial_address, + }, + }) } - IdentityActionKindProto::ChangeRecoveryAddress(change_recovery_address_action) => { - Ok(Action::ChangeRecoveryAddress(ChangeRecoveryAddress { - new_recovery_address: change_recovery_address_action.new_recovery_address, - recovery_address_signature: from_signature_proto_option( - change_recovery_address_action.existing_recovery_address_signature, - signature_text.clone(), - )?, - })) + IdentityActionKindProto::ChangeRecoveryAddress(action_proto) => { + UnverifiedAction::ChangeRecoveryAddress(UnverifiedChangeRecoveryAddress { + recovery_address_signature: action_proto + .existing_recovery_address_signature + .try_into()?, + unsigned_action: UnsignedChangeRecoveryAddress { + new_recovery_address: action_proto.new_recovery_address, + }, + }) } - IdentityActionKindProto::Revoke(revoke_action) => { - Ok(Action::RevokeAssociation(RevokeAssociation { - revoked_member: from_member_identifier_proto_option( - revoke_action.member_to_revoke, - )?, - recovery_address_signature: from_signature_proto_option( - revoke_action.recovery_address_signature, - signature_text.clone(), - )?, - })) + IdentityActionKindProto::Revoke(action_proto) => { + UnverifiedAction::RevokeAssociation(UnverifiedRevokeAssociation { + recovery_address_signature: action_proto + .recovery_address_signature + .try_into()?, + unsigned_action: UnsignedRevokeAssociation { + revoked_member: action_proto + .member_to_revoke + .ok_or(DeserializationError::MissingMember)? + .try_into()?, + }, + }) } }) - .collect::, DeserializationError>>()?; - - Ok(IdentityUpdate::new( - processed_actions, - inbox_id, - client_timestamp_ns, - )) + } } -fn get_signature_text( - actions: &[IdentityActionKindProto], - inbox_id: String, - client_timestamp_ns: u64, -) -> Result { - let unsigned_actions: Vec = actions - .iter() - .map(|action| match action { - IdentityActionKindProto::Add(add_action) => { - Ok(UnsignedAction::AddAssociation(UnsignedAddAssociation { - new_member_identifier: from_member_identifier_proto_option( - add_action.new_member_identifier.clone(), - )?, - })) - } - IdentityActionKindProto::CreateInbox(create_inbox_action) => { - Ok(UnsignedAction::CreateInbox(UnsignedCreateInbox { - nonce: create_inbox_action.nonce, - account_address: create_inbox_action.initial_address.clone(), - })) +impl TryFrom for UnverifiedSignature { + type Error = DeserializationError; + + fn try_from(proto: SignatureWrapperProto) -> Result { + let signature = unwrap_proto_signature(proto)?; + let unverified_sig = match signature { + SignatureKindProto::Erc191(sig) => UnverifiedSignature::RecoverableEcdsa( + UnverifiedRecoverableEcdsaSignature::new(sig.bytes), + ), + SignatureKindProto::DelegatedErc191(sig) => { + UnverifiedSignature::LegacyDelegated(UnverifiedLegacyDelegatedSignature::new( + UnverifiedRecoverableEcdsaSignature::new( + sig.signature.ok_or(DeserializationError::Signature)?.bytes, + ), + sig.delegated_key.ok_or(DeserializationError::Signature)?, + )) } - IdentityActionKindProto::ChangeRecoveryAddress(change_recovery_address_action) => Ok( - UnsignedAction::ChangeRecoveryAddress(UnsignedChangeRecoveryAddress { - new_recovery_address: change_recovery_address_action - .new_recovery_address - .clone(), - }), + SignatureKindProto::InstallationKey(sig) => UnverifiedSignature::InstallationKey( + UnverifiedInstallationKeySignature::new(sig.bytes, sig.public_key), ), - IdentityActionKindProto::Revoke(revoke_action) => Ok( - UnsignedAction::RevokeAssociation(UnsignedRevokeAssociation { - revoked_member: from_member_identifier_proto_option( - revoke_action.member_to_revoke.clone(), - )?, - }), + SignatureKindProto::Erc6492(sig) => UnverifiedSignature::SmartContractWallet( + UnverifiedSmartContractWalletSignature::new( + sig.signature, + sig.account_id.try_into()?, + sig.block_number, + ), ), - }) - .collect::, DeserializationError>>()?; + }; - let unsigned_update = - UnsignedIdentityUpdate::new(unsigned_actions, inbox_id, client_timestamp_ns); + Ok(unverified_sig) + } +} + +impl TryFrom> for UnverifiedSignature { + type Error = DeserializationError; - Ok(unsigned_update.signature_text()) + fn try_from(value: Option) -> Result { + value + .ok_or_else(|| DeserializationError::Signature)? + .try_into() + } } -fn from_member_identifier_proto_option( - proto: Option, -) -> Result { - match proto { - None => Err(DeserializationError::MissingMemberIdentifier), - Some(identifier_proto) => match identifier_proto.kind { - Some(identifier) => Ok(identifier.into()), - None => Err(DeserializationError::MissingMemberIdentifier), - }, +fn unwrap_proto_signature( + value: SignatureWrapperProto, +) -> Result { + match value.signature { + Some(inner) => Ok(inner), + None => Err(DeserializationError::Signature), } } -impl From for MemberIdentifier { - fn from(proto: MemberIdentifierKindProto) -> Self { - match proto { - MemberIdentifierKindProto::Address(address) => address.into(), - MemberIdentifierKindProto::InstallationPublicKey(public_key) => public_key.into(), +impl From for IdentityUpdateProto { + fn from(value: UnverifiedIdentityUpdate) -> Self { + Self { + inbox_id: value.inbox_id, + client_timestamp_ns: value.client_timestamp_ns, + actions: map_vec(value.actions), } } } -fn from_signature_proto_option( - proto: Option, - signature_text: String, -) -> Result, DeserializationError> { - match proto { - None => Err(DeserializationError::Signature), - Some(signature_proto) => match signature_proto.signature { - Some(signature) => Ok(from_signature_kind_proto(signature, signature_text)?), - None => Err(DeserializationError::Signature), - }, +impl From for IdentityActionProto { + fn from(value: UnverifiedAction) -> Self { + let kind: IdentityActionKindProto = match value { + UnverifiedAction::CreateInbox(action) => { + IdentityActionKindProto::CreateInbox(CreateInboxProto { + nonce: action.unsigned_action.nonce, + initial_address: action.unsigned_action.account_address, + initial_address_signature: Some(action.initial_address_signature.into()), + }) + } + UnverifiedAction::AddAssociation(action) => { + IdentityActionKindProto::Add(AddAssociationProto { + new_member_identifier: Some( + action.unsigned_action.new_member_identifier.into(), + ), + existing_member_signature: Some(action.existing_member_signature.into()), + new_member_signature: Some(action.new_member_signature.into()), + }) + } + UnverifiedAction::ChangeRecoveryAddress(action) => { + IdentityActionKindProto::ChangeRecoveryAddress(ChangeRecoveryAddressProto { + new_recovery_address: action.unsigned_action.new_recovery_address, + existing_recovery_address_signature: Some( + action.recovery_address_signature.into(), + ), + }) + } + UnverifiedAction::RevokeAssociation(action) => { + IdentityActionKindProto::Revoke(RevokeAssociationProto { + recovery_address_signature: Some(action.recovery_address_signature.into()), + member_to_revoke: Some(action.unsigned_action.revoked_member.into()), + }) + } + }; + + IdentityActionProto { kind: Some(kind) } } } -fn from_signature_kind_proto( - proto: SignatureKindProto, - signature_text: String, -) -> Result, DeserializationError> { - Ok(match proto { - SignatureKindProto::InstallationKey(installation_key_signature) => { - Box::new(InstallationKeySignature::new( - signature_text, - installation_key_signature.bytes, - installation_key_signature.public_key, - )) - } - SignatureKindProto::Erc191(erc191_signature) => Box::new(RecoverableEcdsaSignature::new( - signature_text, - erc191_signature.bytes, - )), - SignatureKindProto::Erc6492(signature) => Box::new(SmartContractWalletSignature::new( - signature_text, - signature.signature, - signature.account_id.try_into()?, - signature.chain_rpc_url, - signature.block_number, - )), - SignatureKindProto::DelegatedErc191(delegated_erc191_signature) => { - let signature_value = delegated_erc191_signature - .signature - .ok_or(DeserializationError::Signature)?; - let recoverable_ecdsa_signature = - RecoverableEcdsaSignature::new(signature_text, signature_value.bytes); - - Box::new(LegacyDelegatedSignature::new( - recoverable_ecdsa_signature, - delegated_erc191_signature - .delegated_key - .ok_or(DeserializationError::Signature)?, - )) +impl From for SignatureWrapperProto { + fn from(value: UnverifiedSignature) -> Self { + let signature = match value { + UnverifiedSignature::SmartContractWallet(sig) => { + SignatureKindProto::Erc6492(SmartContractWalletSignatureProto { + account_id: sig.account_id.into(), + block_number: sig.block_number, + signature: sig.signature_bytes, + // TOOD:nm Remove this field altogether + chain_rpc_url: "".to_string(), + }) + } + UnverifiedSignature::InstallationKey(sig) => { + SignatureKindProto::InstallationKey(RecoverableEd25519SignatureProto { + bytes: sig.signature_bytes, + public_key: sig.verifying_key, + }) + } + UnverifiedSignature::LegacyDelegated(sig) => { + SignatureKindProto::DelegatedErc191(LegacyDelegatedSignatureProto { + delegated_key: Some(sig.signed_public_key_proto), + signature: Some(RecoverableEcdsaSignatureProto { + bytes: sig.legacy_key_signature.signature_bytes, + }), + }) + } + UnverifiedSignature::RecoverableEcdsa(sig) => { + SignatureKindProto::Erc191(RecoverableEcdsaSignatureProto { + bytes: sig.signature_bytes, + }) + } + }; + + Self { + signature: Some(signature), } - }) + } } -impl From for IdentityUpdateProto { - fn from(update: IdentityUpdate) -> IdentityUpdateProto { - let actions: Vec = - update.actions.into_iter().map(Into::into).collect(); +impl TryFrom> for UnverifiedIdentityUpdate { + type Error = DeserializationError; - IdentityUpdateProto { - client_timestamp_ns: update.client_timestamp_ns, - inbox_id: update.inbox_id, - actions, - } + fn try_from(value: Vec) -> Result { + let update_proto: IdentityUpdateProto = IdentityUpdateProto::decode(value.as_slice())?; + UnverifiedIdentityUpdate::try_from(update_proto) } } -impl From for IdentityActionProto { - fn from(action: Action) -> IdentityActionProto { - match action { - Action::AddAssociation(add_association) => IdentityActionProto { - kind: Some(IdentityActionKindProto::Add(AddAssociationProto { - new_member_identifier: Some(add_association.new_member_identifier.into()), - new_member_signature: Some(add_association.new_member_signature.to_proto()), - existing_member_signature: Some( - add_association.existing_member_signature.to_proto(), - ), - })), - }, - Action::CreateInbox(create_inbox) => IdentityActionProto { - kind: Some(IdentityActionKindProto::CreateInbox(CreateInboxProto { - nonce: create_inbox.nonce, - initial_address: create_inbox.account_address, - initial_address_signature: Some( - create_inbox.initial_address_signature.to_proto(), - ), - })), - }, - Action::RevokeAssociation(revoke_association) => IdentityActionProto { - kind: Some(IdentityActionKindProto::Revoke(RevokeAssociationProto { - member_to_revoke: Some(revoke_association.revoked_member.into()), - recovery_address_signature: Some( - revoke_association.recovery_address_signature.to_proto(), - ), - })), - }, - Action::ChangeRecoveryAddress(change_recovery_address) => IdentityActionProto { - kind: Some(IdentityActionKindProto::ChangeRecoveryAddress( - ChangeRecoveryAddressProto { - new_recovery_address: change_recovery_address.new_recovery_address, - existing_recovery_address_signature: Some( - change_recovery_address - .recovery_address_signature - .to_proto(), - ), - }, - )), - }, +impl From for Vec { + fn from(value: UnverifiedIdentityUpdate) -> Self { + let proto: IdentityUpdateProto = value.into(); + proto.encode_to_vec() + } +} + +impl From for MemberIdentifier { + fn from(proto: MemberIdentifierKindProto) -> Self { + match proto { + MemberIdentifierKindProto::Address(address) => address.into(), + MemberIdentifierKindProto::InstallationPublicKey(public_key) => public_key.into(), } } } @@ -317,6 +318,7 @@ impl From for MemberProto { MemberProto { identifier: Some(member.identifier.into()), added_by_entity: member.added_by_entity.map(Into::into), + client_timestamp_ns: member.client_timestamp_ns, } } } @@ -331,6 +333,7 @@ impl TryFrom for Member { .ok_or(DeserializationError::MissingMemberIdentifier)? .try_into()?, added_by_entity: proto.added_by_entity.map(TryInto::try_into).transpose()?, + client_timestamp_ns: proto.client_timestamp_ns, }) } } @@ -432,6 +435,7 @@ pub fn try_map_vec>(other: Vec) -> Result, for ValidatedLegacySignedPublicKey { type Error = SignatureError; @@ -460,9 +464,12 @@ impl TryFrom for ValidatedLegacySignedPublicKey { signature } }; - let wallet_signature = RecoverableSignature::Eip191Signature(wallet_signature); - let account_address = - wallet_signature.recover_address(&Self::text(&serialized_key_data))?; + let verified_wallet_signature = VerifiedSignature::from_recoverable_ecdsa( + Self::text(&serialized_key_data), + &wallet_signature, + )?; + + let account_address = verified_wallet_signature.signer.to_string(); let account_address = sanitize_evm_addresses(vec![account_address])?[0].clone(); let legacy_unsigned_public_key_proto = @@ -480,7 +487,7 @@ impl TryFrom for ValidatedLegacySignedPublicKey { Ok(Self { account_address, - wallet_signature, + wallet_signature: verified_wallet_signature, serialized_key_data, public_key_bytes, created_ns, @@ -490,7 +497,7 @@ impl TryFrom for ValidatedLegacySignedPublicKey { impl From for LegacySignedPublicKeyProto { fn from(validated: ValidatedLegacySignedPublicKey) -> Self { - let RecoverableSignature::Eip191Signature(signature) = validated.wallet_signature; + let signature = validated.wallet_signature.raw_bytes; Self { key_bytes: validated.serialized_key_data, signature: Some(SignedPublicKeySignatureProto { @@ -518,6 +525,7 @@ impl TryFrom for AccountId { if !chain_id_regex.is_match(&chain_id) || !account_address_regex.is_match(account_address) { return Err(DeserializationError::InvalidAccountId); } + Ok(AccountId { chain_id: chain_id.to_string(), account_address: account_address.to_string(), @@ -543,28 +551,58 @@ impl From for String { mod tests { use crate::associations::{ hashes::generate_inbox_id, - test_utils::{rand_string, rand_u64}, + test_utils::{rand_string, rand_u64, rand_vec}, }; use super::*; #[test] - fn test_round_trip() { + fn test_round_trip_unverified() { let account_address = rand_string(); let nonce = rand_u64(); let inbox_id = generate_inbox_id(&account_address, &nonce); + let client_timestamp_ns = rand_u64(); + let signature_bytes = rand_vec(); - let identity_update = IdentityUpdate::new( - vec![Action::CreateInbox(CreateInbox { - nonce, - account_address, - initial_address_signature: Box::new(RecoverableEcdsaSignature::new( - "foo".to_string(), - vec![1, 2, 3], - )), - })], + let identity_update = UnverifiedIdentityUpdate::new( inbox_id, - rand_u64(), + client_timestamp_ns, + vec![ + UnverifiedAction::CreateInbox(UnverifiedCreateInbox { + initial_address_signature: UnverifiedSignature::RecoverableEcdsa( + UnverifiedRecoverableEcdsaSignature::new(signature_bytes), + ), + unsigned_action: UnsignedCreateInbox { + nonce, + account_address, + }, + }), + UnverifiedAction::AddAssociation(UnverifiedAddAssociation { + new_member_signature: UnverifiedSignature::new_recoverable_ecdsa(vec![1, 2, 3]), + existing_member_signature: UnverifiedSignature::new_recoverable_ecdsa(vec![ + 4, 5, 6, + ]), + unsigned_action: UnsignedAddAssociation { + new_member_identifier: rand_string().into(), + }, + }), + UnverifiedAction::ChangeRecoveryAddress(UnverifiedChangeRecoveryAddress { + recovery_address_signature: UnverifiedSignature::new_recoverable_ecdsa(vec![ + 7, 8, 9, + ]), + unsigned_action: UnsignedChangeRecoveryAddress { + new_recovery_address: rand_string(), + }, + }), + UnverifiedAction::RevokeAssociation(UnverifiedRevokeAssociation { + recovery_address_signature: UnverifiedSignature::new_recoverable_ecdsa(vec![ + 10, 11, 12, + ]), + unsigned_action: UnsignedRevokeAssociation { + revoked_member: rand_string().into(), + }, + }), + ], ); let serialized_update = IdentityUpdateProto::from(identity_update.clone()); @@ -573,10 +611,14 @@ mod tests { serialized_update.client_timestamp_ns, identity_update.client_timestamp_ns ); - assert_eq!(serialized_update.actions.len(), 1); + assert_eq!(serialized_update.actions.len(), 4); + + let deserialized_update: UnverifiedIdentityUpdate = serialized_update + .clone() + .try_into() + .expect("deserialization error"); - let deserialized_update = from_identity_update_proto(serialized_update.clone()) - .expect("deserialization should succeed"); + assert_eq!(deserialized_update, identity_update); let reserialized = IdentityUpdateProto::from(deserialized_update); @@ -684,4 +726,13 @@ mod tests { Err(DeserializationError::InvalidAccountId) )); } + + #[test] + fn test_account_id_create() { + let address = "0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb".to_string(); + let chain_id = 12; + let account_id = AccountId::new_evm(chain_id, address.clone()); + assert_eq!(account_id.account_address, address); + assert_eq!(account_id.chain_id, "eip155:12"); + } } diff --git a/xmtp_id/src/associations/signature.rs b/xmtp_id/src/associations/signature.rs index 7a4190d51..ef49e9ef7 100644 --- a/xmtp_id/src/associations/signature.rs +++ b/xmtp_id/src/associations/signature.rs @@ -1,32 +1,14 @@ -use super::MemberIdentifier; -use crate::constants::INSTALLATION_KEY_SIGNATURE_CONTEXT; -use async_trait::async_trait; -use ed25519_dalek::{Signature as Ed25519Signature, VerifyingKey}; -use ethers::{ - core::k256, - providers::{Http, Middleware, Provider}, - signers::{LocalWallet, Signer}, - types::{BlockNumber, U64}, - utils::{hash_message, public_key_to_address}, -}; +use ethers::signers::{LocalWallet, Signer}; use prost::Message; -use sha2::{Digest, Sha512}; use std::array::TryFromSliceError; use thiserror::Error; -use xmtp_cryptography::signature::{h160addr_to_string, RecoverableSignature}; -use xmtp_proto::xmtp::{ - identity::associations::{ - signature::Signature as SignatureKindProto, - LegacyDelegatedSignature as LegacyDelegatedSignatureProto, - RecoverableEcdsaSignature as RecoverableEcdsaSignatureProto, - RecoverableEd25519Signature as RecoverableEd25519SignatureProto, - Signature as SignatureProto, - SmartContractWalletSignature as SmartContractWalletSignatureProto, - }, - message_contents::{ - signed_private_key, SignedPrivateKey as LegacySignedPrivateKeyProto, - SignedPublicKey as LegacySignedPublicKeyProto, - }, +use xmtp_proto::xmtp::message_contents::{ + signed_private_key, SignedPrivateKey as LegacySignedPrivateKeyProto, +}; + +use super::{ + unverified::{UnverifiedLegacyDelegatedSignature, UnverifiedRecoverableEcdsaSignature}, + verified_signature::VerifiedSignature, }; #[derive(Debug, Error)] @@ -38,7 +20,6 @@ pub enum SignatureError { WalletError(#[from] ethers::signers::WalletError), #[error(transparent)] ECDSAError(#[from] ethers::types::SignatureError), - #[error("Malformed legacy key: {0}")] MalformedLegacyKey(String), #[error(transparent)] @@ -80,78 +61,8 @@ impl std::fmt::Display for SignatureKind { } } } - -#[async_trait] -pub trait Signature: SignatureClone + std::fmt::Debug + Send + Sync + 'static { - async fn recover_signer(&self) -> Result; - fn signature_kind(&self) -> SignatureKind; - fn bytes(&self) -> Vec; - fn to_proto(&self) -> SignatureProto; -} - -pub trait SignatureClone { - fn clone_box(&self) -> Box; -} - -impl SignatureClone for T -where - T: 'static + Signature + Clone, -{ - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } -} - -impl Clone for Box { - fn clone(&self) -> Box { - self.clone_box() - } -} - -#[allow(dead_code)] -#[derive(Debug, Clone)] -pub struct RecoverableEcdsaSignature { - signature_text: String, - signature_bytes: Vec, -} - -impl RecoverableEcdsaSignature { - pub fn new(signature_text: String, signature_bytes: Vec) -> Self { - RecoverableEcdsaSignature { - signature_text, - signature_bytes, - } - } -} - -#[async_trait] -impl Signature for RecoverableEcdsaSignature { - async fn recover_signer(&self) -> Result { - let signature = ethers::types::Signature::try_from(self.bytes().as_slice())?; - Ok(MemberIdentifier::Address(h160addr_to_string( - signature.recover(self.signature_text.clone())?, - ))) - } - - fn signature_kind(&self) -> SignatureKind { - SignatureKind::Erc191 - } - - fn bytes(&self) -> Vec { - self.signature_bytes.clone() - } - - fn to_proto(&self) -> SignatureProto { - SignatureProto { - signature: Some(SignatureKindProto::Erc191(RecoverableEcdsaSignatureProto { - bytes: self.bytes(), - })), - } - } -} - // CAIP-10[https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-10.md] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct AccountId { pub(crate) chain_id: String, pub(crate) account_address: String, @@ -164,224 +75,17 @@ impl AccountId { account_address, } } - pub fn is_evm_chain(&self) -> bool { - self.chain_id.starts_with("eip155") - } - pub fn get_account_address(&self) -> &str { - &self.account_address - } -} - -/// A ERC-6492 signature. -#[derive(Debug, Clone)] -pub struct SmartContractWalletSignature { - signature_text: String, - signature_bytes: Vec, - account_id: AccountId, - block_number: u64, - chain_rpc_url: String, -} - -unsafe impl Send for SmartContractWalletSignature {} - -impl SmartContractWalletSignature { - pub fn new( - signature_text: String, - signature_bytes: Vec, - account_id: AccountId, - chain_rpc_url: String, - block_number: u64, - ) -> Self { - SmartContractWalletSignature { - signature_text, - signature_bytes, - account_id, - chain_rpc_url, - block_number, - } - } - - /// Fetch Chain ID & block number from the RPC URL and create the new ERC1271 Signature - /// This could be used by platform SDK who only needs to provide the RPC URL and account address. - pub async fn new_with_rpc( - signature_text: String, - signature_bytes: Vec, - account_address: String, - chain_rpc_url: String, - ) -> Result { - let provider = Provider::::try_from(&chain_rpc_url)?; - let block_number = provider.get_block_number().await?; - let chain_id = provider.get_chainid().await?; - let account_id = AccountId::new(chain_id.to_string(), account_address); - Ok(SmartContractWalletSignature::new( - signature_text, - signature_bytes, - account_id, - chain_rpc_url, - block_number.as_u64(), - )) - } -} - -#[async_trait] -impl Signature for SmartContractWalletSignature { - async fn recover_signer(&self) -> Result { - let verifier = - crate::scw_verifier::SmartContractWalletVerifier::new(self.chain_rpc_url.clone()); - let is_valid = verifier - .is_valid_signature( - self.account_id.get_account_address().parse()?, - hash_message(self.signature_text.clone()).into(), // the hash function should match the one used by the user wallet - &self.bytes().into(), - Some(BlockNumber::Number(U64::from(self.block_number))), - ) - .await?; - if is_valid { - Ok(MemberIdentifier::Address( - self.account_id - .get_account_address() - .to_string() - .to_lowercase(), - )) - } else { - Err(SignatureError::Invalid) - } - } - - fn signature_kind(&self) -> SignatureKind { - SignatureKind::Erc1271 - } - - fn bytes(&self) -> Vec { - self.signature_bytes.clone() - } - - fn to_proto(&self) -> SignatureProto { - SignatureProto { - signature: Some(SignatureKindProto::Erc6492( - SmartContractWalletSignatureProto { - account_id: self.account_id.clone().into(), - block_number: self.block_number, - signature: self.bytes(), - chain_rpc_url: self.chain_rpc_url.clone(), - }, - )), - } - } -} - -#[derive(Clone, Debug)] -pub struct InstallationKeySignature { - signature_text: String, - signature_bytes: Vec, - verifying_key: Vec, -} - -impl InstallationKeySignature { - pub fn new(signature_text: String, signature_bytes: Vec, verifying_key: Vec) -> Self { - InstallationKeySignature { - signature_text, - signature_bytes, - verifying_key, - } - } -} - -#[async_trait] -impl Signature for InstallationKeySignature { - async fn recover_signer(&self) -> Result { - let signature: Ed25519Signature = - Ed25519Signature::from_bytes(self.bytes().as_slice().try_into()?); - let verifying_key: VerifyingKey = - VerifyingKey::from_bytes(&self.verifying_key.as_slice().try_into()?)?; - let mut prehashed: Sha512 = Sha512::new(); - prehashed.update(self.signature_text.clone()); - verifying_key.verify_prehashed( - prehashed, - Some(INSTALLATION_KEY_SIGNATURE_CONTEXT), - &signature, - )?; - Ok(MemberIdentifier::Installation(self.verifying_key.clone())) - } - - fn signature_kind(&self) -> SignatureKind { - SignatureKind::InstallationKey - } - - fn bytes(&self) -> Vec { - self.signature_bytes.clone() - } - fn to_proto(&self) -> SignatureProto { - SignatureProto { - signature: Some(SignatureKindProto::InstallationKey( - RecoverableEd25519SignatureProto { - bytes: self.bytes(), - public_key: self.verifying_key.clone(), - }, - )), - } - } -} - -#[derive(Debug, Clone)] -pub struct LegacyDelegatedSignature { - legacy_key_signature: RecoverableEcdsaSignature, // signature from the legacy key(delegatee) - signed_public_key_proto: LegacySignedPublicKeyProto, // signature from the wallet(delegator) -} - -impl LegacyDelegatedSignature { - pub fn new( - legacy_key_signature: RecoverableEcdsaSignature, - signed_public_key_proto: LegacySignedPublicKeyProto, - ) -> Self { - LegacyDelegatedSignature { - legacy_key_signature, - signed_public_key_proto, - } + pub fn new_evm(chain_id: u64, account_address: String) -> Self { + Self::new(format!("eip155:{}", chain_id), account_address) } -} -#[async_trait] -impl Signature for LegacyDelegatedSignature { - async fn recover_signer(&self) -> Result { - // Recover the RecoverableEcdsaSignature of the legacy signer(address of the legacy key) - let legacy_signer = self.legacy_key_signature.recover_signer().await?; - // Derive the address from legacy public key and compare it with legacy_signer. - // Note that it's not recovering the address from the __signed__ public key. - let signed_public_key: ValidatedLegacySignedPublicKey = - self.signed_public_key_proto.clone().try_into()?; - let public_key: k256::ecdsa::VerifyingKey = - k256::ecdsa::VerifyingKey::from_sec1_bytes(&signed_public_key.public_key_bytes)?; - let address = h160addr_to_string(public_key_to_address(&public_key)); - if MemberIdentifier::Address(address) != legacy_signer { - return Err(SignatureError::Invalid); - } - - Ok(MemberIdentifier::Address( - signed_public_key.account_address().to_lowercase(), - )) - } - - fn signature_kind(&self) -> SignatureKind { - SignatureKind::LegacyDelegated - } - - fn bytes(&self) -> Vec { - self.legacy_key_signature.bytes() + pub fn is_evm_chain(&self) -> bool { + self.chain_id.starts_with("eip155") } - fn to_proto(&self) -> SignatureProto { - SignatureProto { - signature: Some(SignatureKindProto::DelegatedErc191( - LegacyDelegatedSignatureProto { - delegated_key: Some(self.signed_public_key_proto.clone()), - signature: Some(RecoverableEcdsaSignatureProto { - bytes: self.bytes(), - }), - }, - )), - } + pub fn get_account_address(&self) -> &str { + &self.account_address } } @@ -389,7 +93,7 @@ impl Signature for LegacyDelegatedSignature { pub async fn sign_with_legacy_key( signature_text: String, legacy_signed_private_key: Vec, -) -> Result { +) -> Result { let legacy_signed_private_key_proto = LegacySignedPrivateKeyProto::decode(legacy_signed_private_key.as_slice())?; let signed_private_key::Union::Secp256k1(secp256k1) = legacy_signed_private_key_proto @@ -408,10 +112,8 @@ pub async fn sign_with_legacy_key( "Missing public_key field".to_string(), ))?; - let recoverable_sig = RecoverableEcdsaSignature::new(signature_text, signature.to_vec()); - - Ok(LegacyDelegatedSignature::new( - recoverable_sig, + Ok(UnverifiedLegacyDelegatedSignature::new( + UnverifiedRecoverableEcdsaSignature::new(signature.to_vec()), legacy_signed_public_key_proto, )) } @@ -420,7 +122,7 @@ pub async fn sign_with_legacy_key( pub struct ValidatedLegacySignedPublicKey { pub(crate) account_address: String, pub(crate) serialized_key_data: Vec, - pub(crate) wallet_signature: RecoverableSignature, + pub(crate) wallet_signature: VerifiedSignature, pub(crate) public_key_bytes: Vec, pub(crate) created_ns: u64, } @@ -461,165 +163,3 @@ impl ValidatedLegacySignedPublicKey { self.created_ns } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - associations::{ - signature::Signature, - test_utils::{rand_u64, MockSignature}, - unsigned_actions::{SignatureTextCreator, UnsignedAddAssociation, UnsignedCreateInbox}, - }, - InboxOwner, - }; - use ed25519_dalek::SigningKey; - use ethers::prelude::*; - - use prost::Message; - use sha2::{Digest, Sha512}; - use xmtp_proto::xmtp::message_contents::SignedPublicKey as LegacySignedPublicKeyProto; - use xmtp_v2::k256_helper::sign_sha256; - - #[test] - fn validate_good_key_round_trip() { - let proto_bytes = vec![ - 10, 79, 8, 192, 195, 165, 174, 203, 153, 231, 213, 23, 26, 67, 10, 65, 4, 216, 84, 174, - 252, 198, 225, 219, 168, 239, 166, 62, 233, 206, 108, 53, 155, 87, 132, 8, 43, 91, 36, - 91, 81, 93, 213, 67, 241, 69, 5, 31, 249, 186, 129, 119, 144, 4, 44, 54, 76, 185, 95, - 61, 23, 231, 72, 7, 169, 18, 70, 113, 79, 173, 82, 13, 37, 146, 201, 43, 174, 180, 33, - 125, 43, 18, 70, 18, 68, 10, 64, 7, 136, 100, 172, 155, 247, 230, 255, 253, 247, 78, - 50, 212, 226, 41, 78, 239, 183, 136, 247, 122, 88, 155, 245, 219, 183, 215, 202, 42, - 89, 162, 128, 96, 96, 120, 131, 17, 70, 38, 231, 2, 27, 91, 29, 66, 110, 128, 140, 1, - 42, 217, 185, 2, 181, 208, 100, 143, 143, 219, 159, 174, 1, 233, 191, 16, 1, - ]; - let account_address = "0x220ca99fb7fafa18cb623d924794dde47b4bc2e9"; - - let proto = LegacySignedPublicKeyProto::decode(proto_bytes.as_slice()).unwrap(); - let validated_key = ValidatedLegacySignedPublicKey::try_from(proto) - .expect("Key should validate successfully"); - let proto: LegacySignedPublicKeyProto = validated_key.into(); - let validated_key = ValidatedLegacySignedPublicKey::try_from(proto) - .expect("Key should still validate successfully"); - assert_eq!(validated_key.account_address(), account_address); - } - - #[test] - fn validate_malformed_key() { - let proto_bytes = vec![ - 10, 79, 8, 192, 195, 165, 174, 203, 153, 231, 213, 23, 26, 67, 10, 65, 4, 216, 84, 174, - 252, 198, 225, 219, 168, 239, 166, 62, 233, 206, 108, 53, 155, 87, 132, 8, 43, 91, 36, - 91, 81, 93, 213, 67, 241, 69, 5, 31, 249, 186, 129, 119, 144, 4, 44, 54, 76, 185, 95, - 61, 23, 231, 72, 7, 169, 18, 70, 113, 79, 173, 82, 13, 37, 146, 201, 43, 174, 180, 33, - 125, 43, 18, 70, 18, 68, 10, 64, 7, 136, 100, 172, 155, 247, 230, 255, 253, 247, 78, - 50, 212, 226, 41, 78, 239, 183, 136, 247, 122, 88, 155, 245, 219, 183, 215, 202, 42, - 89, 162, 128, 96, 96, 120, 131, 17, 70, 38, 231, 2, 27, 91, 29, 66, 110, 128, 140, 1, - 42, 217, 185, 2, 181, 208, 100, 143, 143, 219, 159, 174, 1, 233, 191, 16, 1, - ]; - let mut proto = LegacySignedPublicKeyProto::decode(proto_bytes.as_slice()).unwrap(); - proto.key_bytes[0] += 1; // Corrupt the serialized key data - assert!(matches!( - ValidatedLegacySignedPublicKey::try_from(proto), - Err(super::SignatureError::Invalid) - )); - } - - #[tokio::test] - async fn recover_signer_ecdsa() { - let wallet: LocalWallet = LocalWallet::new(&mut rand::thread_rng()); - let unsigned_action = UnsignedCreateInbox { - nonce: rand_u64(), - account_address: wallet.get_address(), - }; - let signature_text = unsigned_action.signature_text(); - let signature_bytes: Vec = wallet - .sign_message(signature_text.clone()) - .await - .unwrap() - .to_vec(); - let signature = RecoverableEcdsaSignature::new(signature_text.clone(), signature_bytes); - let expected = MemberIdentifier::Address(wallet.get_address()); - let actual = signature.recover_signer().await.unwrap(); - - assert_eq!(expected, actual); - } - - #[tokio::test] - async fn recover_signer_erc1271() { - let wallet: LocalWallet = LocalWallet::new(&mut rand::thread_rng()); - - let mock_erc1271 = MockSignature::new_boxed( - true, - MemberIdentifier::Address(wallet.get_address()), - SignatureKind::Erc1271, - None, - ); - - let expected = MemberIdentifier::Address(wallet.get_address()); - let actual = mock_erc1271.recover_signer().await.unwrap(); - assert_eq!(expected, actual); - } - - #[tokio::test] - async fn recover_signer_installation() { - let signing_key: SigningKey = SigningKey::generate(&mut rand::thread_rng()); - let verifying_key = signing_key.verifying_key(); - - let unsigned_action = UnsignedAddAssociation { - new_member_identifier: MemberIdentifier::Address("0x123456789abcdef".to_string()), - }; - let signature_text = unsigned_action.signature_text(); - let mut prehashed: Sha512 = Sha512::new(); - prehashed.update(signature_text.clone()); - let sig = signing_key - .sign_prehashed(prehashed, Some(INSTALLATION_KEY_SIGNATURE_CONTEXT)) - .unwrap(); - let installation_key_sig = InstallationKeySignature::new( - signature_text.clone(), - sig.to_vec(), - verifying_key.as_bytes().to_vec(), - ); - let expected = MemberIdentifier::Installation(verifying_key.as_bytes().to_vec()); - let actual = installation_key_sig.recover_signer().await.unwrap(); - assert_eq!(expected, actual); - } - - // Test the happy path with LocalWallet & fail path with a secp256k1 signer. - #[tokio::test] - async fn recover_signer_legacy() { - let signature_text = "test_legacy_signature".to_string(); - let account_address = "0x0bd00b21af9a2d538103c3aaf95cb507f8af1b28".to_string(); - let legacy_signed_private_key = hex::decode("0880bdb7a8b3f6ede81712220a20ad528ea38ce005268c4fb13832cfed13c2b2219a378e9099e48a38a30d66ef991a96010a4c08aaa8e6f5f9311a430a41047fd90688ca39237c2899281cdf2756f9648f93767f91c0e0f74aed7e3d3a8425e9eaa9fa161341c64aa1c782d004ff37ffedc887549ead4a40f18d1179df9dff124612440a403c2cb2338fb98bfe5f6850af11f6a7e97a04350fc9d37877060f8d18e8f66de31c77b3504c93cf6a47017ea700a48625c4159e3f7e75b52ff4ea23bc13db77371001").unwrap(); - - // happy path - let legacy_signature = - sign_with_legacy_key(signature_text.clone(), legacy_signed_private_key.clone()) - .await - .unwrap(); - let expected = MemberIdentifier::Address(account_address.clone()); - let actual = legacy_signature.recover_signer().await.unwrap(); - assert_eq!(expected, actual); - - // fail path - let legacy_signed_private_key_proto = - LegacySignedPrivateKeyProto::decode(legacy_signed_private_key.as_slice()).unwrap(); - let signed_private_key::Union::Secp256k1(secp256k1) = - legacy_signed_private_key_proto.union.unwrap(); - let legacy_private_key = secp256k1.bytes; - let (mut legacy_signature, recovery_id) = sign_sha256( - &legacy_private_key, // secret_key - signature_text.as_bytes(), // message - ) - .unwrap(); - legacy_signature.push(recovery_id); - let legacy_signature = RecoverableEcdsaSignature::new(signature_text, legacy_signature); - let legacy_signed_public_key_proto = legacy_signed_private_key_proto.public_key.unwrap(); - let legacy_signature: LegacyDelegatedSignature = - LegacyDelegatedSignature::new(legacy_signature, legacy_signed_public_key_proto); - - assert!(matches!( - legacy_signature.recover_signer().await, - Err(super::SignatureError::Invalid) - )); - } -} diff --git a/xmtp_id/src/associations/state.rs b/xmtp_id/src/associations/state.rs index 83cc8752e..a22e4b659 100644 --- a/xmtp_id/src/associations/state.rs +++ b/xmtp_id/src/associations/state.rs @@ -13,6 +13,12 @@ pub struct AssociationStateDiff { pub removed_members: Vec, } +#[derive(Debug)] +pub struct Installation { + pub id: Vec, + pub client_timestamp_ns: Option, +} + impl AssociationStateDiff { pub fn new_installations(&self) -> Vec> { self.new_members @@ -128,6 +134,19 @@ impl AssociationState { .collect() } + pub fn installations(&self) -> Vec { + self.members() + .into_iter() + .filter_map(|member| match member.identifier { + MemberIdentifier::Address(_) => None, + MemberIdentifier::Installation(id) => Some(Installation { + id, + client_timestamp_ns: member.client_timestamp_ns, + }), + }) + .collect() + } + pub fn diff(&self, new_state: &Self) -> AssociationStateDiff { let new_members: Vec = new_state .members @@ -163,13 +182,9 @@ impl AssociationState { pub fn new(account_address: String, nonce: u64) -> Self { let inbox_id = generate_inbox_id(&account_address, &nonce); let identifier = MemberIdentifier::Address(account_address.clone()); - let new_member = Member::new(identifier.clone(), None); + let new_member = Member::new(identifier.clone(), None, None); Self { - members: { - let mut members = HashMap::new(); - members.insert(identifier, new_member); - members - }, + members: HashMap::from_iter([(identifier, new_member)]), seen_signatures: HashSet::new(), recovery_address: account_address.to_lowercase(), inbox_id, diff --git a/xmtp_id/src/associations/test_utils.rs b/xmtp_id/src/associations/test_utils.rs index f3cd5f2b7..6f7b624a6 100644 --- a/xmtp_id/src/associations/test_utils.rs +++ b/xmtp_id/src/associations/test_utils.rs @@ -1,19 +1,20 @@ -use rand::{distributions::Alphanumeric, Rng}; -use xmtp_proto::xmtp::{ - identity::associations::{ - signature::Signature as SignatureKindProto, - LegacyDelegatedSignature as LegacyDelegatedSignatureProto, - RecoverableEcdsaSignature as RecoverableEcdsaSignatureProto, - RecoverableEd25519Signature as RecoverableEd25519SignatureProto, - Signature as SignatureProto, - SmartContractWalletSignature as SmartContractWalletSignatureProto, - }, - message_contents::{ - Signature as LegacySignatureProto, SignedPublicKey as LegacySignedPublicKeyProto, - }, +use super::{ + builder::SignatureRequest, + unsigned_actions::UnsignedCreateInbox, + unverified::{UnverifiedAction, UnverifiedCreateInbox, UnverifiedSignature}, + AccountId, }; - -use super::{MemberIdentifier, Signature, SignatureError, SignatureKind}; +use crate::{ + constants::INSTALLATION_KEY_SIGNATURE_CONTEXT, + scw_verifier::{SmartContractSignatureVerifier, VerifierError}, +}; +use ed25519_dalek::SigningKey as Ed25519SigningKey; +use ethers::{ + signers::{LocalWallet, Signer}, + types::{BlockNumber, Bytes}, +}; +use rand::{distributions::Alphanumeric, Rng}; +use sha2::{Digest, Sha512}; pub fn rand_string() -> String { let v: String = rand::thread_rng() @@ -36,86 +37,75 @@ pub fn rand_vec() -> Vec { } #[derive(Debug, Clone)] -pub struct MockSignature { - is_valid: bool, - signer_identity: MemberIdentifier, - signature_kind: SignatureKind, - signature_nonce: String, +pub struct MockSmartContractSignatureVerifier { + is_valid_signature: bool, } -impl MockSignature { - pub fn new_boxed( - is_valid: bool, - signer_identity: MemberIdentifier, - signature_kind: SignatureKind, - // Signature nonce is used to control what the signature bytes are - // Defaults to random - signature_nonce: Option, - ) -> Box { - let nonce = signature_nonce.unwrap_or(rand_string()); - Box::new(Self { - is_valid, - signer_identity, - signature_kind, - signature_nonce: nonce, - }) +impl MockSmartContractSignatureVerifier { + pub fn new(is_valid_signature: bool) -> Self { + Self { is_valid_signature } } } #[async_trait::async_trait] -impl Signature for MockSignature { - fn signature_kind(&self) -> SignatureKind { - self.signature_kind.clone() +impl SmartContractSignatureVerifier for MockSmartContractSignatureVerifier { + async fn is_valid_signature( + &self, + _account_id: AccountId, + _hash: [u8; 32], + _signature: &Bytes, + _block_number: Option, + ) -> Result { + Ok(self.is_valid_signature) } +} - async fn recover_signer(&self) -> Result { - match self.is_valid { - true => Ok(self.signer_identity.clone()), - false => Err(SignatureError::Invalid), - } - } +pub async fn add_wallet_signature(signature_request: &mut SignatureRequest, wallet: &LocalWallet) { + let signature_text = signature_request.signature_text(); + let sig = wallet.sign_message(signature_text).await.unwrap().to_vec(); + let unverified_sig = UnverifiedSignature::new_recoverable_ecdsa(sig); + let scw_verifier = MockSmartContractSignatureVerifier::new(false); - fn bytes(&self) -> Vec { - let sig = format!("{}{}", self.signer_identity, self.signature_nonce); - sig.as_bytes().to_vec() - } + signature_request + .add_signature(unverified_sig, &scw_verifier) + .await + .expect("should succeed"); +} - fn to_proto(&self) -> SignatureProto { - match self.signature_kind { - SignatureKind::Erc191 => SignatureProto { - signature: Some(SignatureKindProto::Erc191(RecoverableEcdsaSignatureProto { - bytes: vec![0], - })), - }, - SignatureKind::Erc1271 => SignatureProto { - signature: Some(SignatureKindProto::Erc6492( - SmartContractWalletSignatureProto { - account_id: "eip155:1:0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb".into(), - block_number: 0, - signature: vec![0], - chain_rpc_url: "https://example.com".to_string(), - }, - )), - }, - SignatureKind::InstallationKey => SignatureProto { - signature: Some(SignatureKindProto::InstallationKey( - RecoverableEd25519SignatureProto { - bytes: vec![0], - public_key: vec![0], - }, - )), - }, - SignatureKind::LegacyDelegated => SignatureProto { - signature: Some(SignatureKindProto::DelegatedErc191( - LegacyDelegatedSignatureProto { - delegated_key: Some(LegacySignedPublicKeyProto { - key_bytes: vec![0], - signature: Some(LegacySignatureProto { union: None }), - }), - signature: Some(RecoverableEcdsaSignatureProto { bytes: vec![0] }), - }, - )), +pub async fn add_installation_key_signature( + signature_request: &mut SignatureRequest, + installation_key: &Ed25519SigningKey, +) { + let signature_text = signature_request.signature_text(); + let verifying_key = installation_key.verifying_key(); + let mut prehashed: Sha512 = Sha512::new(); + prehashed.update(signature_text); + + let sig = installation_key + .sign_prehashed(prehashed, Some(INSTALLATION_KEY_SIGNATURE_CONTEXT)) + .unwrap(); + let unverified_sig = UnverifiedSignature::new_installation_key( + sig.to_bytes().to_vec(), + verifying_key.as_bytes().to_vec(), + ); + + signature_request + .add_signature( + unverified_sig, + &MockSmartContractSignatureVerifier::new(false), + ) + .await + .expect("should succeed"); +} + +impl UnverifiedAction { + pub fn new_test_create_inbox(account_address: &str, nonce: &u64) -> Self { + Self::CreateInbox(UnverifiedCreateInbox::new( + UnsignedCreateInbox { + account_address: account_address.to_owned(), + nonce: *nonce, }, - } + UnverifiedSignature::new_recoverable_ecdsa(vec![1, 2, 3]), + )) } } diff --git a/xmtp_id/src/associations/unsigned_actions.rs b/xmtp_id/src/associations/unsigned_actions.rs index 4c89e2d31..b0c21f9bf 100644 --- a/xmtp_id/src/associations/unsigned_actions.rs +++ b/xmtp_id/src/associations/unsigned_actions.rs @@ -11,7 +11,7 @@ pub trait SignatureTextCreator { fn signature_text(&self) -> String; } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct UnsignedCreateInbox { pub nonce: u64, pub account_address: String, @@ -23,7 +23,7 @@ impl SignatureTextCreator for UnsignedCreateInbox { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct UnsignedAddAssociation { pub new_member_identifier: MemberIdentifier, } @@ -40,7 +40,7 @@ impl SignatureTextCreator for UnsignedAddAssociation { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct UnsignedRevokeAssociation { pub revoked_member: MemberIdentifier, } @@ -57,7 +57,7 @@ impl SignatureTextCreator for UnsignedRevokeAssociation { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct UnsignedChangeRecoveryAddress { pub new_recovery_address: String, } @@ -73,7 +73,7 @@ impl SignatureTextCreator for UnsignedChangeRecoveryAddress { } #[allow(dead_code)] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum UnsignedAction { CreateInbox(UnsignedCreateInbox), AddAssociation(UnsignedAddAssociation), @@ -92,7 +92,7 @@ impl SignatureTextCreator for UnsignedAction { } } -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct UnsignedIdentityUpdate { pub inbox_id: String, pub client_timestamp_ns: u64, diff --git a/xmtp_id/src/associations/unverified.rs b/xmtp_id/src/associations/unverified.rs new file mode 100644 index 000000000..b9120758c --- /dev/null +++ b/xmtp_id/src/associations/unverified.rs @@ -0,0 +1,415 @@ +#![allow(dead_code)] +use crate::scw_verifier::SmartContractSignatureVerifier; + +use super::{ + unsigned_actions::{ + SignatureTextCreator, UnsignedAction, UnsignedAddAssociation, + UnsignedChangeRecoveryAddress, UnsignedCreateInbox, UnsignedIdentityUpdate, + UnsignedRevokeAssociation, + }, + verified_signature::VerifiedSignature, + AccountId, Action, AddAssociation, CreateInbox, IdentityUpdate, RevokeAssociation, + SignatureError, +}; +use futures::future::try_join_all; +use xmtp_proto::xmtp::message_contents::SignedPublicKey as LegacySignedPublicKeyProto; + +#[derive(Debug, Clone, PartialEq)] +pub struct UnverifiedIdentityUpdate { + pub inbox_id: String, + pub client_timestamp_ns: u64, + pub actions: Vec, +} + +impl UnverifiedIdentityUpdate { + pub fn new(inbox_id: String, client_timestamp_ns: u64, actions: Vec) -> Self { + Self { + inbox_id, + client_timestamp_ns, + actions, + } + } + + fn signature_text(&self) -> String { + let unsigned_actions = self + .actions + .iter() + .map(|action| action.unsigned_action()) + .collect(); + let unsigned_identity_update = UnsignedIdentityUpdate::new( + unsigned_actions, + self.inbox_id.clone(), + self.client_timestamp_ns, + ); + + unsigned_identity_update.signature_text() + } + + fn signatures(&self) -> Vec { + self.actions + .iter() + .flat_map(|action| action.signatures()) + .collect() + } + + pub async fn to_verified( + &self, + scw_verifier: &dyn SmartContractSignatureVerifier, + ) -> Result { + let signature_text = self.signature_text(); + + let actions: Vec = try_join_all( + self.actions + .iter() + .map(|action| async { action.to_verified(&signature_text, scw_verifier).await }), + ) + .await?; + + Ok(IdentityUpdate::new( + actions, + self.inbox_id.clone(), + self.client_timestamp_ns, + )) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum UnverifiedAction { + CreateInbox(UnverifiedCreateInbox), + AddAssociation(UnverifiedAddAssociation), + RevokeAssociation(UnverifiedRevokeAssociation), + ChangeRecoveryAddress(UnverifiedChangeRecoveryAddress), +} + +impl UnverifiedAction { + fn unsigned_action(&self) -> UnsignedAction { + match self { + UnverifiedAction::CreateInbox(action) => { + UnsignedAction::CreateInbox(action.unsigned_action.clone()) + } + UnverifiedAction::AddAssociation(action) => { + UnsignedAction::AddAssociation(action.unsigned_action.clone()) + } + UnverifiedAction::RevokeAssociation(action) => { + UnsignedAction::RevokeAssociation(action.unsigned_action.clone()) + } + UnverifiedAction::ChangeRecoveryAddress(action) => { + UnsignedAction::ChangeRecoveryAddress(action.unsigned_action.clone()) + } + } + } + + fn signatures(&self) -> Vec { + match self { + UnverifiedAction::CreateInbox(action) => vec![action.initial_address_signature.clone()], + UnverifiedAction::AddAssociation(action) => vec![ + action.existing_member_signature.clone(), + action.new_member_signature.clone(), + ], + UnverifiedAction::RevokeAssociation(action) => { + vec![action.recovery_address_signature.clone()] + } + UnverifiedAction::ChangeRecoveryAddress(action) => { + vec![action.recovery_address_signature.clone()] + } + } + } + + pub async fn to_verified>( + &self, + signature_text: Text, + scw_verifier: &dyn SmartContractSignatureVerifier, + ) -> Result { + let action = match self { + UnverifiedAction::CreateInbox(action) => Action::CreateInbox(CreateInbox { + nonce: action.unsigned_action.nonce, + account_address: action.unsigned_action.account_address.clone(), + initial_address_signature: action + .initial_address_signature + .to_verified(signature_text.as_ref(), scw_verifier) + .await?, + }), + UnverifiedAction::AddAssociation(action) => Action::AddAssociation(AddAssociation { + new_member_signature: action + .new_member_signature + .to_verified(signature_text.as_ref(), scw_verifier) + .await?, + new_member_identifier: action.unsigned_action.new_member_identifier.clone(), + existing_member_signature: action + .existing_member_signature + .to_verified(signature_text.as_ref(), scw_verifier) + .await?, + }), + UnverifiedAction::RevokeAssociation(action) => { + Action::RevokeAssociation(RevokeAssociation { + recovery_address_signature: action + .recovery_address_signature + .to_verified(signature_text.as_ref(), scw_verifier) + .await?, + revoked_member: action.unsigned_action.revoked_member.clone(), + }) + } + UnverifiedAction::ChangeRecoveryAddress(action) => { + Action::ChangeRecoveryAddress(super::ChangeRecoveryAddress { + recovery_address_signature: action + .recovery_address_signature + .to_verified(signature_text.as_ref(), scw_verifier) + .await?, + new_recovery_address: action.unsigned_action.new_recovery_address.clone(), + }) + } + }; + + Ok(action) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct UnverifiedCreateInbox { + pub(crate) unsigned_action: UnsignedCreateInbox, + pub(crate) initial_address_signature: UnverifiedSignature, +} + +impl UnverifiedCreateInbox { + pub fn new( + unsigned_action: UnsignedCreateInbox, + initial_address_signature: UnverifiedSignature, + ) -> Self { + Self { + unsigned_action, + initial_address_signature, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct UnverifiedAddAssociation { + pub(crate) unsigned_action: UnsignedAddAssociation, + pub(crate) new_member_signature: UnverifiedSignature, + pub(crate) existing_member_signature: UnverifiedSignature, +} + +impl UnverifiedAddAssociation { + pub fn new( + unsigned_action: UnsignedAddAssociation, + new_member_signature: UnverifiedSignature, + existing_member_signature: UnverifiedSignature, + ) -> Self { + Self { + unsigned_action, + new_member_signature, + existing_member_signature, + } + } +} +#[derive(Debug, Clone, PartialEq)] +pub struct UnverifiedRevokeAssociation { + pub(crate) recovery_address_signature: UnverifiedSignature, + pub(crate) unsigned_action: UnsignedRevokeAssociation, +} + +impl UnverifiedRevokeAssociation { + pub fn new( + unsigned_action: UnsignedRevokeAssociation, + recovery_address_signature: UnverifiedSignature, + ) -> Self { + Self { + unsigned_action, + recovery_address_signature, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct UnverifiedChangeRecoveryAddress { + pub(crate) recovery_address_signature: UnverifiedSignature, + pub(crate) unsigned_action: UnsignedChangeRecoveryAddress, +} + +impl UnverifiedChangeRecoveryAddress { + pub fn new( + unsigned_action: UnsignedChangeRecoveryAddress, + recovery_address_signature: UnverifiedSignature, + ) -> Self { + Self { + unsigned_action, + recovery_address_signature, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum UnverifiedSignature { + InstallationKey(UnverifiedInstallationKeySignature), + RecoverableEcdsa(UnverifiedRecoverableEcdsaSignature), + SmartContractWallet(UnverifiedSmartContractWalletSignature), + LegacyDelegated(UnverifiedLegacyDelegatedSignature), +} + +impl UnverifiedSignature { + pub async fn to_verified>( + &self, + signature_text: Text, + scw_verifier: &dyn SmartContractSignatureVerifier, + ) -> Result { + match self { + UnverifiedSignature::InstallationKey(sig) => VerifiedSignature::from_installation_key( + signature_text, + &sig.signature_bytes, + &sig.verifying_key, + ), + UnverifiedSignature::RecoverableEcdsa(sig) => { + VerifiedSignature::from_recoverable_ecdsa(signature_text, &sig.signature_bytes) + } + UnverifiedSignature::SmartContractWallet(sig) => { + VerifiedSignature::from_smart_contract_wallet( + signature_text, + scw_verifier, + &sig.signature_bytes, + sig.account_id.clone(), + sig.block_number, + ) + .await + } + UnverifiedSignature::LegacyDelegated(sig) => VerifiedSignature::from_legacy_delegated( + signature_text, + &sig.legacy_key_signature.signature_bytes, + sig.signed_public_key_proto.clone(), + ), + } + } + + pub fn new_recoverable_ecdsa(signature: Vec) -> Self { + Self::RecoverableEcdsa(UnverifiedRecoverableEcdsaSignature::new(signature)) + } + + pub fn new_installation_key(signature: Vec, verifying_key: Vec) -> Self { + Self::InstallationKey(UnverifiedInstallationKeySignature::new( + signature, + verifying_key, + )) + } + + pub fn new_smart_contract_wallet( + signature: Vec, + account_id: AccountId, + block_number: u64, + ) -> Self { + Self::SmartContractWallet(UnverifiedSmartContractWalletSignature::new( + signature, + account_id, + block_number, + )) + } + + pub fn new_legacy_delegated( + signature: Vec, + signed_public_key_proto: LegacySignedPublicKeyProto, + ) -> Self { + Self::LegacyDelegated(UnverifiedLegacyDelegatedSignature::new( + UnverifiedRecoverableEcdsaSignature::new(signature), + signed_public_key_proto, + )) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct UnverifiedInstallationKeySignature { + pub(crate) signature_bytes: Vec, + pub(crate) verifying_key: Vec, +} + +impl UnverifiedInstallationKeySignature { + pub fn new(signature_bytes: Vec, verifying_key: Vec) -> Self { + Self { + signature_bytes, + verifying_key, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct UnverifiedRecoverableEcdsaSignature { + pub(crate) signature_bytes: Vec, +} + +impl UnverifiedRecoverableEcdsaSignature { + pub fn new(signature_bytes: Vec) -> Self { + Self { signature_bytes } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct UnverifiedSmartContractWalletSignature { + pub(crate) signature_bytes: Vec, + pub(crate) account_id: AccountId, + pub(crate) block_number: u64, +} + +impl UnverifiedSmartContractWalletSignature { + pub fn new(signature_bytes: Vec, account_id: AccountId, block_number: u64) -> Self { + Self { + signature_bytes, + account_id, + block_number, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct UnverifiedLegacyDelegatedSignature { + pub(crate) legacy_key_signature: UnverifiedRecoverableEcdsaSignature, + pub(crate) signed_public_key_proto: LegacySignedPublicKeyProto, +} + +impl UnverifiedLegacyDelegatedSignature { + pub fn new( + legacy_key_signature: UnverifiedRecoverableEcdsaSignature, + signed_public_key_proto: LegacySignedPublicKeyProto, + ) -> Self { + Self { + legacy_key_signature, + signed_public_key_proto, + } + } +} + +#[cfg(test)] +mod tests { + use crate::associations::{ + generate_inbox_id, test_utils::rand_string, unsigned_actions::UnsignedCreateInbox, + }; + + use super::{ + UnverifiedAction, UnverifiedCreateInbox, UnverifiedIdentityUpdate, + UnverifiedRecoverableEcdsaSignature, UnverifiedSignature, + }; + + #[test] + fn create_identity_update() { + let account_address = rand_string(); + let nonce = 1; + let update = UnverifiedIdentityUpdate { + inbox_id: generate_inbox_id(account_address.as_str(), &nonce), + client_timestamp_ns: 10, + actions: vec![UnverifiedAction::CreateInbox(UnverifiedCreateInbox { + unsigned_action: UnsignedCreateInbox { + account_address: account_address.to_string(), + nonce, + }, + initial_address_signature: UnverifiedSignature::RecoverableEcdsa( + UnverifiedRecoverableEcdsaSignature { + signature_bytes: vec![1, 2, 3], + }, + ), + })], + }; + assert!( + update + .signature_text() + .contains(format!("(Owner: {})", account_address).as_str()), + "could not find account address in signature text: {}", + update.signature_text() + ); + } +} diff --git a/xmtp_id/src/associations/verified_signature.rs b/xmtp_id/src/associations/verified_signature.rs new file mode 100644 index 000000000..9e21b9026 --- /dev/null +++ b/xmtp_id/src/associations/verified_signature.rs @@ -0,0 +1,342 @@ +#![allow(dead_code)] + +use ed25519_dalek::{Signature as Ed25519Signature, VerifyingKey as Ed25519VerifyingKey}; +use ethers::types::{BlockNumber, Signature as EthersSignature, U64}; +use ethers::utils::hash_message; +use ethers::{core::k256::ecdsa::VerifyingKey as EcdsaVerifyingKey, utils::public_key_to_address}; +use sha2::{Digest, Sha512}; +use xmtp_cryptography::signature::h160addr_to_string; +use xmtp_proto::xmtp::message_contents::SignedPublicKey as LegacySignedPublicKeyProto; + +use crate::constants::INSTALLATION_KEY_SIGNATURE_CONTEXT; +use crate::scw_verifier::SmartContractSignatureVerifier; + +use super::{ + AccountId, MemberIdentifier, SignatureError, SignatureKind, ValidatedLegacySignedPublicKey, +}; + +#[derive(Debug, Clone)] +pub struct VerifiedSignature { + pub signer: MemberIdentifier, + pub kind: SignatureKind, + pub raw_bytes: Vec, +} + +impl VerifiedSignature { + pub fn new(signer: MemberIdentifier, kind: SignatureKind, raw_bytes: Vec) -> Self { + Self { + signer, + kind, + raw_bytes, + } + } + + /** + * Verifies an ECDSA signature against the provided signature text. + * Returns a VerifiedSignature if the signature is valid, otherwise returns an error. + */ + pub fn from_recoverable_ecdsa>( + signature_text: Text, + signature_bytes: &[u8], + ) -> Result { + let signature = EthersSignature::try_from(signature_bytes)?; + let address = h160addr_to_string(signature.recover(signature_text.as_ref())?); + + Ok(Self::new( + MemberIdentifier::Address(address), + SignatureKind::Erc191, + signature_bytes.to_vec(), + )) + } + + pub fn from_recoverable_ecdsa_with_expected_address>( + signature_text: Text, + signature_bytes: &[u8], + expected_address: Text, + ) -> Result { + let partially_verified = Self::from_recoverable_ecdsa(signature_text, signature_bytes)?; + if partially_verified + .signer + .address() + .ok_or(SignatureError::Invalid)? + .to_lowercase() + != expected_address.as_ref().to_lowercase() + { + return Err(SignatureError::Invalid); + } + + Ok(partially_verified) + } + + /** + * Verifies an installation key signature against the provided signature text and verifying key bytes. + * Returns a VerifiedSignature if the signature is valid, otherwise returns an error. + */ + pub fn from_installation_key>( + signature_text: Text, + signature_bytes: &[u8], + verifying_key_bytes: &[u8], + ) -> Result { + let signature = Ed25519Signature::from_bytes(signature_bytes.try_into()?); + let verifying_key = Ed25519VerifyingKey::from_bytes(verifying_key_bytes.try_into()?)?; + let mut prehashed = Sha512::new(); + prehashed.update(signature_text.as_ref()); + + verifying_key.verify_prehashed( + prehashed, + Some(INSTALLATION_KEY_SIGNATURE_CONTEXT), + &signature, + )?; + + Ok(Self::new( + MemberIdentifier::Installation(verifying_key_bytes.to_vec()), + SignatureKind::InstallationKey, + signature_bytes.to_vec(), + )) + } + + pub fn from_legacy_delegated>( + signature_text: Text, + signature_bytes: &[u8], + signed_public_key_proto: LegacySignedPublicKeyProto, + ) -> Result { + let verified_legacy_signature = + Self::from_recoverable_ecdsa(signature_text, signature_bytes)?; + let signed_public_key: ValidatedLegacySignedPublicKey = + signed_public_key_proto.try_into()?; + let public_key = EcdsaVerifyingKey::from_sec1_bytes(&signed_public_key.public_key_bytes)?; + let address = h160addr_to_string(public_key_to_address(&public_key)); + + if MemberIdentifier::Address(address) != verified_legacy_signature.signer { + return Err(SignatureError::Invalid); + } + + Ok(Self::new( + MemberIdentifier::Address(signed_public_key.account_address.to_lowercase()), + SignatureKind::LegacyDelegated, + // Must use the wallet signature bytes, since those are the ones we care about making unique. + // This protects against using the legacy key more than once in the Identity Update Log + signed_public_key.wallet_signature.raw_bytes, + )) + } + + pub async fn from_smart_contract_wallet>( + signature_text: Text, + signature_verifier: &dyn SmartContractSignatureVerifier, + signature_bytes: &[u8], + account_id: AccountId, + block_number: u64, + ) -> Result { + let is_valid = signature_verifier + .is_valid_signature( + account_id.clone(), + hash_message(signature_text.as_ref()).into(), + &signature_bytes.to_vec().into(), + Some(BlockNumber::Number(U64::from(block_number))), + ) + .await?; + + if is_valid { + Ok(Self::new( + MemberIdentifier::Address(account_id.into()), + SignatureKind::Erc1271, + signature_bytes.to_vec(), + )) + } else { + Err(SignatureError::Invalid) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + associations::{ + sign_with_legacy_key, verified_signature::VerifiedSignature, MemberIdentifier, + SignatureKind, + }, + constants::INSTALLATION_KEY_SIGNATURE_CONTEXT, + InboxOwner, + }; + use ed25519_dalek::SigningKey as Ed25519SigningKey; + use ethers::signers::{LocalWallet, Signer}; + use prost::Message; + use xmtp_proto::xmtp::message_contents::{ + signature::Union as SignatureUnion, signed_private_key, + SignedPrivateKey as LegacySignedPrivateKeyProto, + }; + use xmtp_v2::k256_helper::sign_sha256; + + #[tokio::test] + async fn test_recoverable_ecdsa() { + let wallet: LocalWallet = LocalWallet::new(&mut rand::thread_rng()); + let signature_text = "test signature body"; + + let sig_bytes: Vec = wallet.sign_message(signature_text).await.unwrap().to_vec(); + let verified_sig = VerifiedSignature::from_recoverable_ecdsa(signature_text, &sig_bytes) + .expect("should succeed"); + + assert_eq!(verified_sig.signer.address().unwrap(), wallet.get_address()); + assert_eq!(verified_sig.kind, SignatureKind::Erc191); + assert_eq!(verified_sig.raw_bytes, sig_bytes); + } + + #[tokio::test] + async fn test_recoverable_ecdsa_incorrect() { + let wallet: LocalWallet = LocalWallet::new(&mut rand::thread_rng()); + let signature_text = "test signature body"; + + let sig_bytes: Vec = wallet.sign_message(signature_text).await.unwrap().to_vec(); + + let verified_sig = + VerifiedSignature::from_recoverable_ecdsa("wrong text again", &sig_bytes).unwrap(); + assert_ne!(verified_sig.signer, wallet.get_address().into()); + } + + #[tokio::test] + async fn test_installation_key() { + let signing_key = Ed25519SigningKey::generate(&mut rand::thread_rng()); + let verifying_key = signing_key.verifying_key(); + + let signature_text = "test signature text"; + let mut prehashed: Sha512 = Sha512::new(); + prehashed.update(signature_text); + + let sig = signing_key + .sign_prehashed(prehashed, Some(INSTALLATION_KEY_SIGNATURE_CONTEXT)) + .unwrap(); + + let verified_sig = VerifiedSignature::from_installation_key( + signature_text, + sig.to_bytes().as_slice(), + verifying_key.as_bytes(), + ) + .expect("should succeed"); + let expected = MemberIdentifier::Installation(verifying_key.as_bytes().to_vec()); + assert_eq!(expected, verified_sig.signer); + assert_eq!(SignatureKind::InstallationKey, verified_sig.kind); + assert_eq!(verified_sig.raw_bytes, sig.to_bytes().as_slice()); + + // Make sure it fails with the wrong signature text + VerifiedSignature::from_installation_key( + "wrong signature text", + sig.to_bytes().as_slice(), + verifying_key.as_bytes(), + ) + .expect_err("should fail with incorrect signature text"); + + // Make sure it fails with the wrong verifying key + VerifiedSignature::from_installation_key( + signature_text, + sig.to_bytes().as_slice(), + &[verifying_key.as_bytes().as_slice(), vec![2, 3].as_slice()].concat(), + ) + .expect_err("should fail with incorrect verifying key"); + } + + #[test] + fn validate_good_key_round_trip() { + let proto_bytes = vec![ + 10, 79, 8, 192, 195, 165, 174, 203, 153, 231, 213, 23, 26, 67, 10, 65, 4, 216, 84, 174, + 252, 198, 225, 219, 168, 239, 166, 62, 233, 206, 108, 53, 155, 87, 132, 8, 43, 91, 36, + 91, 81, 93, 213, 67, 241, 69, 5, 31, 249, 186, 129, 119, 144, 4, 44, 54, 76, 185, 95, + 61, 23, 231, 72, 7, 169, 18, 70, 113, 79, 173, 82, 13, 37, 146, 201, 43, 174, 180, 33, + 125, 43, 18, 70, 18, 68, 10, 64, 7, 136, 100, 172, 155, 247, 230, 255, 253, 247, 78, + 50, 212, 226, 41, 78, 239, 183, 136, 247, 122, 88, 155, 245, 219, 183, 215, 202, 42, + 89, 162, 128, 96, 96, 120, 131, 17, 70, 38, 231, 2, 27, 91, 29, 66, 110, 128, 140, 1, + 42, 217, 185, 2, 181, 208, 100, 143, 143, 219, 159, 174, 1, 233, 191, 16, 1, + ]; + let account_address = "0x220ca99fb7fafa18cb623d924794dde47b4bc2e9"; + + let proto = LegacySignedPublicKeyProto::decode(proto_bytes.as_slice()).unwrap(); + let validated_key = ValidatedLegacySignedPublicKey::try_from(proto) + .expect("Key should validate successfully"); + let proto: LegacySignedPublicKeyProto = validated_key.into(); + let validated_key = ValidatedLegacySignedPublicKey::try_from(proto) + .expect("Key should still validate successfully"); + assert_eq!(validated_key.account_address(), account_address); + } + + #[test] + fn validate_malformed_key() { + let proto_bytes = vec![ + 10, 79, 8, 192, 195, 165, 174, 203, 153, 231, 213, 23, 26, 67, 10, 65, 4, 216, 84, 174, + 252, 198, 225, 219, 168, 239, 166, 62, 233, 206, 108, 53, 155, 87, 132, 8, 43, 91, 36, + 91, 81, 93, 213, 67, 241, 69, 5, 31, 249, 186, 129, 119, 144, 4, 44, 54, 76, 185, 95, + 61, 23, 231, 72, 7, 169, 18, 70, 113, 79, 173, 82, 13, 37, 146, 201, 43, 174, 180, 33, + 125, 43, 18, 70, 18, 68, 10, 64, 7, 136, 100, 172, 155, 247, 230, 255, 253, 247, 78, + 50, 212, 226, 41, 78, 239, 183, 136, 247, 122, 88, 155, 245, 219, 183, 215, 202, 42, + 89, 162, 128, 96, 96, 120, 131, 17, 70, 38, 231, 2, 27, 91, 29, 66, 110, 128, 140, 1, + 42, 217, 185, 2, 181, 208, 100, 143, 143, 219, 159, 174, 1, 233, 191, 16, 1, + ]; + let mut proto = LegacySignedPublicKeyProto::decode(proto_bytes.as_slice()).unwrap(); + proto.key_bytes[0] += 1; // Corrupt the serialized key data + assert!(matches!( + ValidatedLegacySignedPublicKey::try_from(proto), + Err(super::SignatureError::Invalid) + )); + } + + #[tokio::test] + async fn test_legacy_delegated() { + let signature_text = "test_legacy_signature"; + let account_address = "0x0bd00b21af9a2d538103c3aaf95cb507f8af1b28".to_string(); + let legacy_signed_private_key = hex::decode("0880bdb7a8b3f6ede81712220a20ad528ea38ce005268c4fb13832cfed13c2b2219a378e9099e48a38a30d66ef991a96010a4c08aaa8e6f5f9311a430a41047fd90688ca39237c2899281cdf2756f9648f93767f91c0e0f74aed7e3d3a8425e9eaa9fa161341c64aa1c782d004ff37ffedc887549ead4a40f18d1179df9dff124612440a403c2cb2338fb98bfe5f6850af11f6a7e97a04350fc9d37877060f8d18e8f66de31c77b3504c93cf6a47017ea700a48625c4159e3f7e75b52ff4ea23bc13db77371001").unwrap(); + + // happy path + let legacy_signature = sign_with_legacy_key( + signature_text.to_string(), + legacy_signed_private_key.clone(), + ) + .await + .unwrap(); + let expected = MemberIdentifier::Address(account_address.clone()); + let verified_sig = VerifiedSignature::from_legacy_delegated( + signature_text, + &legacy_signature.legacy_key_signature.signature_bytes, + legacy_signature.signed_public_key_proto.clone(), + ) + .expect("should succeed"); + + let legacy_signature_bytes = match legacy_signature + .signed_public_key_proto + .signature + .unwrap() + .union + .unwrap() + { + SignatureUnion::WalletEcdsaCompact(legacy_wallet_ecdsa) => [ + legacy_wallet_ecdsa.bytes, + vec![legacy_wallet_ecdsa.recovery as u8], + ] + .concat(), + _ => panic!("Invalid signature type"), + }; + + assert_eq!(verified_sig.signer, expected); + assert_eq!(verified_sig.kind, SignatureKind::LegacyDelegated); + assert_eq!(verified_sig.raw_bytes, legacy_signature_bytes,); + + // fail path + let legacy_signed_private_key_proto = + LegacySignedPrivateKeyProto::decode(legacy_signed_private_key.as_slice()).unwrap(); + let signed_private_key::Union::Secp256k1(secp256k1) = + legacy_signed_private_key_proto.union.unwrap(); + let legacy_private_key = secp256k1.bytes; + let (mut legacy_signature, recovery_id) = sign_sha256( + &legacy_private_key, // secret_key + signature_text.as_bytes(), // message + ) + .unwrap(); + legacy_signature.push(recovery_id); + let legacy_signed_public_key_proto = legacy_signed_private_key_proto.public_key.unwrap(); + + let res = VerifiedSignature::from_legacy_delegated( + signature_text, + &legacy_signature, + legacy_signed_public_key_proto, + ); + assert!(matches!(res, Err(super::SignatureError::Invalid))); + } +} diff --git a/xmtp_id/src/lib.rs b/xmtp_id/src/lib.rs index 050681d34..f6fa1ad29 100644 --- a/xmtp_id/src/lib.rs +++ b/xmtp_id/src/lib.rs @@ -8,7 +8,6 @@ use ethers::{ signers::{LocalWallet, Signer}, types::Address, }; -use futures::executor; use openmls_traits::types::CryptoError; use thiserror::Error; use xmtp_cryptography::signature::{h160addr_to_string, RecoverableSignature, SignatureError}; @@ -54,7 +53,8 @@ impl InboxOwner for LocalWallet { } fn sign(&self, text: &str) -> Result { - Ok(executor::block_on(self.sign_message(text))?.to_vec().into()) + let message_hash = ethers::core::utils::hash_message(text); + Ok(self.sign_hash(message_hash)?.to_vec().into()) } } diff --git a/xmtp_id/src/scw_verifier.rs b/xmtp_id/src/scw_verifier/chain_rpc_verifier.rs similarity index 90% rename from xmtp_id/src/scw_verifier.rs rename to xmtp_id/src/scw_verifier/chain_rpc_verifier.rs index 9e94a1dd0..9a92218ec 100644 --- a/xmtp_id/src/scw_verifier.rs +++ b/xmtp_id/src/scw_verifier/chain_rpc_verifier.rs @@ -1,26 +1,17 @@ //! Interaction with [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) smart contracts. +use crate::scw_verifier::SmartContractSignatureVerifier; +use async_trait::async_trait; use ethers::abi::{Constructor, Param, ParamType, Token}; use ethers::contract::abigen; use ethers::providers::{Http, Middleware, Provider}; use ethers::types::transaction::eip2718::TypedTransaction; use ethers::types::{Address, BlockId, BlockNumber, Bytes, TransactionRequest}; -use hex::FromHex; +use hex::{FromHex, FromHexError}; use std::sync::Arc; -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum VerifierError { - #[error("calling smart contract {0}")] - Contract(#[from] ethers::contract::ContractError>), - #[error("unexpected result from ERC-6492 {0}")] - UnexpectedERC6492Result(String), - #[error(transparent)] - FromHex(#[from] hex::FromHexError), - #[error(transparent)] - Abi(#[from] ethers::abi::Error), - #[error(transparent)] - Provider(#[from] ethers::providers::ProviderError), -} + +use crate::associations::AccountId; + +use super::VerifierError; // https://github.com/AmbireTech/signature-validator/blob/7706bda/index.ts#L13 // Contract from AmbireTech that is also used by Viem. @@ -37,16 +28,19 @@ abigen!( ); #[derive(Debug)] -pub struct SmartContractWalletVerifier { +pub struct RpcSmartContractWalletVerifier { pub provider: Arc>, } -impl SmartContractWalletVerifier { +impl RpcSmartContractWalletVerifier { pub fn new(url: String) -> Self { let provider = Arc::new(Provider::::try_from(url).unwrap()); Self { provider } } +} +#[async_trait] +impl SmartContractSignatureVerifier for RpcSmartContractWalletVerifier { /// Verifies an ERC-6492 signature. /// /// # Arguments @@ -55,14 +49,18 @@ impl SmartContractWalletVerifier { /// * `signer` - can be the smart wallet address or EOA address. /// * `hash` - Message digest for the signature. /// * `signature` - Could be encoded smart wallet signature or raw ECDSA signature. - pub async fn is_valid_signature( + async fn is_valid_signature( &self, - signer: Address, + signer: AccountId, hash: [u8; 32], signature: &Bytes, block_number: Option, ) -> Result { let code = hex::decode(VALIDATE_SIG_OFFCHAIN_BYTECODE).unwrap(); + let account_address: Address = signer + .account_address + .parse() + .map_err(|_| FromHexError::InvalidStringLength)?; // ABI of the ValidateSigOffchain constructor // constructor (address _signer, bytes32 _hash, bytes memory _signature) let inputs: Vec = vec![ @@ -84,7 +82,7 @@ impl SmartContractWalletVerifier { ]; let constructor = Constructor { inputs }; let tokens = &[ - Token::Address(signer), + Token::Address(account_address), Token::FixedBytes(hash.to_vec()), Token::Bytes(signature.to_vec()), ]; @@ -203,6 +201,7 @@ pub mod tests { .get_address(owners_addresses.clone(), nonce) .await .unwrap(); + let contract_call = factory.create_account(owners_addresses.clone(), nonce); let pending_tx = contract_call.send().await.unwrap(); pending_tx.await.unwrap(); @@ -214,13 +213,15 @@ pub mod tests { ); let hash: [u8; 32] = H256::random().into(); let replay_safe_hash = smart_wallet.replay_safe_hash(hash).call().await.unwrap(); - let verifier = SmartContractWalletVerifier::new(anvil.endpoint()); + let verifier = RpcSmartContractWalletVerifier::new(anvil.endpoint()); // verify owner0 is a valid owner let sig0 = owner0.sign_hash(replay_safe_hash.into()).unwrap(); + let account_id = + AccountId::new_evm(anvil.chain_id(), format!("{:?}", smart_wallet_address)); let res = verifier .is_valid_signature( - smart_wallet_address, + account_id.clone(), hash, &abi::encode(&[Token::Tuple(vec![ Token::Uint(U256::from(0)), @@ -236,7 +237,7 @@ pub mod tests { let sig1 = owner1.sign_hash(replay_safe_hash.into()).unwrap(); let res = verifier .is_valid_signature( - smart_wallet_address, + account_id.clone(), hash, &abi::encode(&[Token::Tuple(vec![ Token::Uint(U256::from(1)), @@ -251,7 +252,7 @@ pub mod tests { // owner0 siganture must not be used to verify owner1 let res = verifier .is_valid_signature( - smart_wallet_address, + account_id.clone(), hash, &abi::encode(&[Token::Tuple(vec![ Token::Uint(U256::from(1)), @@ -275,7 +276,7 @@ pub mod tests { let res = verifier .is_valid_signature( - smart_wallet_address, + account_id.clone(), hash, &abi::encode(&[Token::Tuple(vec![ Token::Uint(U256::from(1)), @@ -290,7 +291,7 @@ pub mod tests { // time travel to the pre-removel block number and verify owner1 WAS a valid owner let res = verifier .is_valid_signature( - smart_wallet_address, + account_id.clone(), hash, &abi::encode(&[Token::Tuple(vec![ Token::Uint(U256::from(1)), @@ -344,34 +345,39 @@ pub mod tests { ])]) .into(); - let verifier = SmartContractWalletVerifier::new(anvil.endpoint()); + let verifier = RpcSmartContractWalletVerifier::new(anvil.endpoint()); + + let account_id = + AccountId::new_evm(anvil.chain_id(), format!("{:?}", smart_wallet_address)); // Testing ERC-6492 signatures with deployed ERC-1271. assert!(verifier - .is_valid_signature(smart_wallet_address, hash, &signature, None,) + .is_valid_signature(account_id.clone(), hash, &signature, None,) .await .unwrap()); assert!(!verifier - .is_valid_signature( - smart_wallet_address, - H256::random().into(), - &signature, - None, - ) + .is_valid_signature(account_id.clone(), H256::random().into(), &signature, None,) .await .unwrap()); // Testing if EOA wallet signature is valid on ERC-6492 let signature = owner.sign_hash(hash.into()).unwrap(); + let owner_account_id = + AccountId::new_evm(anvil.chain_id(), format!("{:?}", owner.address())); assert!(verifier - .is_valid_signature(owner.address(), hash, &signature.to_vec().into(), None,) + .is_valid_signature( + owner_account_id.clone(), + hash, + &signature.to_vec().into(), + None, + ) .await .unwrap()); assert!(!verifier .is_valid_signature( - owner.address(), + owner_account_id, H256::random().into(), &signature.to_vec().into(), None, @@ -384,18 +390,18 @@ pub mod tests { // This aims to verify a wrapped ERC-6492 signature that has magic bytes against an ERC-1271 ambire wallet deployed on Polygon. // This doesn't cover the undeployed case. + #[ignore] // This test is temporarily being ignored as it relies on an external service #[tokio::test] async fn test_erc6492_ambire_wallet() { - let signer: Address = "0x4836a472ab1dd406ecb8d0f933a985541ee3921f" - .parse() - .unwrap(); + let signer = "0x4836a472ab1dd406ecb8d0f933a985541ee3921f".to_string(); + let hash = hex::decode("787177").unwrap(); let hash = hash_message(hash); let signature = Bytes::from_hex("0x000000000000000000000000bf07a0df119ca234634588fbdb5625594e2a5bca00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000038449c81579000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000004836a472ab1dd406ecb8d0f933a985541ee3921f0000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000007a7f00000000000000000000000000000000000000000000000000000000000000017f7f0f292b79d9ce101861526459da50f62368077ae24affe97b792bf4bdd2e171553d602d80604d3d3981f3363d3d373d3d3d363d732a2b85eb1054d6f0c6c2e37da05ed3e5fea684ef5af43d82803e903d91602b57fd5bf300000000000000000000000000000000000000000000000000000000000000000000000002246171d1c9000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000004836a472ab1dd406ecb8d0f933a985541ee3921f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000942f9ce5d9a33a82f88d233aeb3292e6802303480000000000000000000000000000000000000000000000000014c3c6ef1cdc01000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042f2eaaebf45fc0340eb55f11c52a30e2ca7f48539d0a1f1cdc240482210326494545def903e8ed4441bd5438109abe950f1f79baf032f184728ba2d4161dea32e1b0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042c0f8db6019888d87a0afc1299e81ef45d3abce64f63072c8d7a6ef00f5f82c1522958ff110afa98b8c0d23b558376db1d2fbab4944e708f8bf6dc7b977ee07201b000000000000000000000000000000000000000000000000000000000000006492649264926492649264926492649264926492649264926492649264926492").unwrap(); - let verifier = SmartContractWalletVerifier::new("https://polygon-rpc.com".to_string()); + let verifier = RpcSmartContractWalletVerifier::new("https://polygon-rpc.com".to_string()); assert!(verifier - .is_valid_signature(signer, hash.into(), &signature, None) + .is_valid_signature(AccountId::new_evm(1, signer), hash.into(), &signature, None) .await .unwrap()); } diff --git a/xmtp_id/src/scw_verifier/mod.rs b/xmtp_id/src/scw_verifier/mod.rs new file mode 100644 index 000000000..6f7b1c6f4 --- /dev/null +++ b/xmtp_id/src/scw_verifier/mod.rs @@ -0,0 +1,37 @@ +mod chain_rpc_verifier; + +use async_trait::async_trait; +use ethers::{ + providers::{Http, Provider}, + types::{BlockNumber, Bytes}, +}; +use thiserror::Error; + +use crate::associations::AccountId; + +pub use self::chain_rpc_verifier::*; + +#[derive(Debug, Error)] +pub enum VerifierError { + #[error("calling smart contract {0}")] + Contract(#[from] ethers::contract::ContractError>), + #[error("unexpected result from ERC-6492 {0}")] + UnexpectedERC6492Result(String), + #[error(transparent)] + FromHex(#[from] hex::FromHexError), + #[error(transparent)] + Abi(#[from] ethers::abi::Error), + #[error(transparent)] + Provider(#[from] ethers::providers::ProviderError), +} + +#[async_trait] +pub trait SmartContractSignatureVerifier: std::fmt::Debug + Send + Sync + 'static { + async fn is_valid_signature( + &self, + account_id: AccountId, + hash: [u8; 32], + signature: &Bytes, + block_number: Option, + ) -> Result; +} diff --git a/xmtp_mls/Cargo.toml b/xmtp_mls/Cargo.toml index 7901f6cc4..dff1d5bd5 100644 --- a/xmtp_mls/Cargo.toml +++ b/xmtp_mls/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2021" name = "xmtp_mls" -version = "0.1.0" +version.workspace = true [[bin]] doc = false @@ -9,79 +9,88 @@ name = "update-schema" path = "src/bin/update-schema.rs" [features] +bench = [ + "test-utils", + "indicatif", + "tracing-subscriber", + "anyhow", + "tracing-flame", + "once_cell", + "xmtp_api_grpc", +] default = ["native"] -grpc = ["xmtp_proto/grpc"] -native = ["libsqlite3-sys/bundled-sqlcipher-vendored-openssl"] -test-utils = ["xmtp_api_grpc"] -bench = ["test-utils", "indicatif", "tracing-subscriber", "anyhow", "tracing-flame", "once_cell"] +http-api = ["xmtp_api_http"] +message-history = [] +native = ["libsqlite3-sys/bundled-sqlcipher-vendored-openssl", "xmtp_api_grpc"] +test-utils = ["xmtp_id/test-utils"] [dependencies] -aes = "0.8.4" aes-gcm = { version = "0.10.3", features = ["std"] } -async-trait.workspace = true -chrono = { workspace = true } -diesel = { version = "2.2", features = [ +async-stream.workspace = true +bincode = "1.3.3" +diesel = { version = "2.2.2", features = [ "sqlite", "r2d2", "returning_clauses_for_sqlite_3_35", ] } -diesel_migrations = { version = "2.1.0", features = ["sqlite"] } +diesel_migrations = { version = "2.2.0", features = ["sqlite"] } ed25519-dalek = "2.1.1" -ethers.workspace = true -ethers-core.workspace = true futures.workspace = true hex.workspace = true -libsqlite3-sys = { version = "0.28.0", optional = true } -log.workspace = true -tracing.workspace = true +libsqlite3-sys = { version = "0.29.0", optional = true } openmls = { workspace = true, features = ["test-utils"] } openmls_basic_credential = { workspace = true } openmls_rust_crypto = { workspace = true } openmls_traits = { workspace = true } +parking_lot = "0.12.3" prost = { workspace = true, features = ["prost-derive"] } rand = { workspace = true } reqwest = { version = "0.12.4", features = ["stream"] } -ring = "0.17.8" serde = { workspace = true } serde_json.workspace = true sha2.workspace = true -smart-default = "0.7.1" thiserror = { workspace = true } tls_codec = { workspace = true } -tokio = { workspace = true, features = ["rt-multi-thread"] } +tokio = { workspace = true, features = [ + "macros", + "rt-multi-thread", + "tracing", +] } +tokio-stream = { version = "0.1", features = ["sync"] } toml = "0.8.4" +tracing = { workspace = true, features = ["release_max_level_debug"] } +trait-variant.workspace = true xmtp_cryptography = { workspace = true } xmtp_id = { path = "../xmtp_id" } xmtp_proto = { workspace = true, features = ["proto_full", "convert"] } -xmtp_v2 = { path = "../xmtp_v2" } # Test/Bench Utils -xmtp_api_grpc = { path = "../xmtp_api_grpc", optional = true } -tracing-subscriber = { workspace = true, optional = true } -indicatif = { version = "0.17", optional = true } anyhow = { workspace = true, optional = true } -tracing-flame = { version = "0.2", optional = true } +indicatif = { version = "0.17", optional = true } once_cell = { version = "1.19", optional = true } +tracing-flame = { version = "0.2", optional = true } +tracing-subscriber = { workspace = true, optional = true } +xmtp_api_grpc = { path = "../xmtp_api_grpc", optional = true } +xmtp_api_http = { path = "../xmtp_api_http", optional = true } [dev-dependencies] +anyhow.workspace = true +async-barrier = "1.1" +criterion = { version = "0.5", features = ["html_reports", "async_tokio"] } ctor.workspace = true -flume = "0.11" -mockall = "0.11.4" +ethers.workspace = true +mockall = "0.13.0" mockito = "1.4.0" tempfile = "3.5.0" -tracing-log = "0.2.0" -tracing-test = "0.2.4" +tracing-subscriber = { workspace = true, features = ["env-filter"] } +tracing.workspace = true xmtp_api_grpc = { path = "../xmtp_api_grpc" } xmtp_id = { path = "../xmtp_id", features = ["test-utils"] } -async-barrier = "1.1" -anyhow.workspace = true -criterion = { version = "0.5", features = ["html_reports", "async_tokio"] } [[bench]] -name = "group_limit" harness = false +name = "group_limit" [[bench]] -name = "crypto" harness = false - +name = "crypto" diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit.rs index 135bfc519..7319f5364 100755 --- a/xmtp_mls/benches/group_limit.rs +++ b/xmtp_mls/benches/group_limit.rs @@ -3,11 +3,9 @@ //! using `RUST_LOG=trace` will additionally output a `tracing.folded` file, which //! may be used to generate a flamegraph of execution from tracing logs. use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput}; -use ethers::signers::LocalWallet; use std::{collections::HashMap, sync::Arc}; use tokio::runtime::{Builder, Handle, Runtime}; use tracing::{trace_span, Instrument}; -use xmtp_cryptography::utils::rng; use xmtp_mls::{ builder::ClientBuilder, groups::GroupMetadataOptions, @@ -15,13 +13,16 @@ use xmtp_mls::{ bench::{create_identities_if_dont_exist, init_logging, Identity, BENCH_ROOT_SPAN}, test::TestClient, }, + Client, }; +pub type BenchClient = Client; + pub const IDENTITY_SAMPLES: [usize; 9] = [10, 20, 40, 80, 100, 200, 300, 400, 450]; pub const MAX_IDENTITIES: usize = 1_000; pub const SAMPLE_SIZE: usize = 10; -fn setup() -> (Arc, Vec, Runtime) { +fn setup() -> (Arc, Vec, Runtime) { let runtime = Builder::new_multi_thread() .enable_time() .enable_io() @@ -30,13 +31,13 @@ fn setup() -> (Arc, Vec, Runtime) { .unwrap(); let (client, identities) = runtime.block_on(async { - let wallet = LocalWallet::new(&mut rng()); + let wallet = xmtp_cryptography::utils::generate_local_wallet(); // use dev network if `DEV_GRPC` is set let dev = std::env::var("DEV_GRPC"); let is_dev_network = matches!(dev, Ok(d) if d == "true" || d == "1"); let client = if is_dev_network { - log::info!("Using Dev GRPC"); + tracing::info!("Using Dev GRPC"); Arc::new(ClientBuilder::new_dev_client(&wallet).await) } else { Arc::new(ClientBuilder::new_test_client(&wallet).await) diff --git a/xmtp_mls/migrations/2024-05-11-004236_cache_association_state/up.sql b/xmtp_mls/migrations/2024-05-11-004236_cache_association_state/up.sql index ab63aba77..825bd9a1e 100644 --- a/xmtp_mls/migrations/2024-05-11-004236_cache_association_state/up.sql +++ b/xmtp_mls/migrations/2024-05-11-004236_cache_association_state/up.sql @@ -1,3 +1,5 @@ +-- Caches the computed association state at a given sequence ID in an inbox log, +-- so that we don't need to replay the whole log. CREATE TABLE association_state ( "inbox_id" TEXT NOT NULL, "sequence_id" BIGINT NOT NULL, diff --git a/xmtp_mls/migrations/2024-05-15-145138_new_schema/up.sql b/xmtp_mls/migrations/2024-05-15-145138_new_schema/up.sql index 4c0cb3a57..8b4030ab3 100644 --- a/xmtp_mls/migrations/2024-05-15-145138_new_schema/up.sql +++ b/xmtp_mls/migrations/2024-05-15-145138_new_schema/up.sql @@ -87,6 +87,8 @@ CREATE TABLE group_intents( CREATE INDEX group_intents_group_id_state ON group_intents(group_id, state); +-- Caches the identity update payload at a given sequence ID, so that API calls +-- don't need to be repeated. CREATE TABLE identity_updates( -- The inbox_id the update refers to "inbox_id" text NOT NULL, diff --git a/xmtp_mls/migrations/2024-08-07-213816_create-private-preference-store/down.sql b/xmtp_mls/migrations/2024-08-07-213816_create-private-preference-store/down.sql new file mode 100644 index 000000000..dd3043403 --- /dev/null +++ b/xmtp_mls/migrations/2024-08-07-213816_create-private-preference-store/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE IF EXISTS "consent_records"; diff --git a/xmtp_mls/migrations/2024-08-07-213816_create-private-preference-store/up.sql b/xmtp_mls/migrations/2024-08-07-213816_create-private-preference-store/up.sql new file mode 100644 index 000000000..76e7f7f27 --- /dev/null +++ b/xmtp_mls/migrations/2024-08-07-213816_create-private-preference-store/up.sql @@ -0,0 +1,9 @@ +CREATE TABLE "consent_records"( + -- Enum of the CONSENT_TYPE (GROUP_ID, INBOX_ID, etc..) + "entity_type" int NOT NULL, + -- Enum of CONSENT_STATE (ALLOWED, DENIED, etc..) + "state" int NOT NULL, + -- The entity of what has consent (0x00 etc..) + "entity" text NOT NULL, + PRIMARY KEY (entity_type, entity) +); \ No newline at end of file diff --git a/xmtp_mls/migrations/2024-08-22-044745_add_staged_commit/down.sql b/xmtp_mls/migrations/2024-08-22-044745_add_staged_commit/down.sql new file mode 100644 index 000000000..d37dd7c23 --- /dev/null +++ b/xmtp_mls/migrations/2024-08-22-044745_add_staged_commit/down.sql @@ -0,0 +1,6 @@ +ALTER TABLE group_intents + DROP COLUMN staged_commit; + +ALTER TABLE group_intents + DROP COLUMN published_in_epoch; + diff --git a/xmtp_mls/migrations/2024-08-22-044745_add_staged_commit/up.sql b/xmtp_mls/migrations/2024-08-22-044745_add_staged_commit/up.sql new file mode 100644 index 000000000..48c090c3f --- /dev/null +++ b/xmtp_mls/migrations/2024-08-22-044745_add_staged_commit/up.sql @@ -0,0 +1,6 @@ +ALTER TABLE group_intents + ADD COLUMN staged_commit BLOB; + +ALTER TABLE group_intents + ADD COLUMN published_in_epoch BIGINT; + diff --git a/xmtp_mls/migrations/2024-09-16-221459_key_package_history/down.sql b/xmtp_mls/migrations/2024-09-16-221459_key_package_history/down.sql new file mode 100644 index 000000000..2f4a654c3 --- /dev/null +++ b/xmtp_mls/migrations/2024-09-16-221459_key_package_history/down.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS key_package_history; + diff --git a/xmtp_mls/migrations/2024-09-16-221459_key_package_history/up.sql b/xmtp_mls/migrations/2024-09-16-221459_key_package_history/up.sql new file mode 100644 index 000000000..f5a0a78a1 --- /dev/null +++ b/xmtp_mls/migrations/2024-09-16-221459_key_package_history/up.sql @@ -0,0 +1,6 @@ +CREATE TABLE key_package_history( + "id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, + "key_package_hash_ref" BLOB UNIQUE NOT NULL, + "created_at_ns" bigint NOT NULL +); + diff --git a/xmtp_mls/migrations/2024-09-18-185314_clear_association_states_cache/down.sql b/xmtp_mls/migrations/2024-09-18-185314_clear_association_states_cache/down.sql new file mode 100644 index 000000000..e69de29bb diff --git a/xmtp_mls/migrations/2024-09-18-185314_clear_association_states_cache/up.sql b/xmtp_mls/migrations/2024-09-18-185314_clear_association_states_cache/up.sql new file mode 100644 index 000000000..7a6ba7017 --- /dev/null +++ b/xmtp_mls/migrations/2024-09-18-185314_clear_association_states_cache/up.sql @@ -0,0 +1 @@ +DELETE FROM association_state; diff --git a/xmtp_mls/src/api/identity.rs b/xmtp_mls/src/api/identity.rs index cfb685392..5a7081ce3 100644 --- a/xmtp_mls/src/api/identity.rs +++ b/xmtp_mls/src/api/identity.rs @@ -4,7 +4,7 @@ use super::{ApiClientWrapper, WrappedApiError}; use crate::XmtpApi; use futures::future::try_join_all; use xmtp_id::{ - associations::{DeserializationError, IdentityUpdate}, + associations::{unverified::UnverifiedIdentityUpdate, DeserializationError}, InboxId, }; use xmtp_proto::xmtp::identity::api::v1::{ @@ -17,6 +17,7 @@ use xmtp_proto::xmtp::identity::api::v1::{ const GET_IDENTITY_UPDATES_CHUNK_SIZE: usize = 50; +#[derive(Debug)] /// A filter for querying identity updates. `sequence_id` is the starting sequence, and only later updates will be returned. pub struct GetIdentityUpdatesV2Filter { pub inbox_id: InboxId, @@ -36,7 +37,7 @@ impl From<&GetIdentityUpdatesV2Filter> for GetIdentityUpdatesV2RequestProto { pub struct InboxUpdate { pub sequence_id: u64, pub server_timestamp_ns: u64, - pub update: IdentityUpdate, + pub update: UnverifiedIdentityUpdate, } impl TryFrom for InboxUpdate { @@ -67,7 +68,7 @@ where { pub async fn publish_identity_update( &self, - update: IdentityUpdate, + update: UnverifiedIdentityUpdate, ) -> Result<(), WrappedApiError> { self.api_client .publish_identity_update(PublishIdentityUpdateRequest { @@ -121,7 +122,7 @@ where &self, account_addresses: Vec, ) -> Result { - log::info!( + tracing::info!( "Getting inbox_ids for account addresses: {:?}", &account_addresses ); @@ -139,7 +140,12 @@ where .responses .into_iter() .filter(|inbox_id| inbox_id.inbox_id.is_some()) - .map(|inbox_id| (inbox_id.address, inbox_id.inbox_id.unwrap())) + .map(|inbox_id| { + ( + inbox_id.address, + inbox_id.inbox_id.expect("Checked for some"), + ) + }) .collect()) } } @@ -149,7 +155,7 @@ mod tests { use super::super::test_utils::*; use super::GetIdentityUpdatesV2Filter; use crate::{api::ApiClientWrapper, retry::Retry}; - use xmtp_id::associations::{test_utils::rand_string, Action, CreateInbox, IdentityUpdate}; + use xmtp_id::associations::{test_utils::rand_string, unverified::UnverifiedIdentityUpdate}; use xmtp_proto::xmtp::identity::api::v1::{ get_identity_updates_response::{ IdentityUpdateLog, Response as GetIdentityUpdatesResponseItem, @@ -158,8 +164,12 @@ mod tests { GetIdentityUpdatesResponse, GetInboxIdsResponse, PublishIdentityUpdateResponse, }; - fn create_identity_update(inbox_id: String) -> IdentityUpdate { - IdentityUpdate::new_test(vec![Action::CreateInbox(CreateInbox::default())], inbox_id) + fn create_identity_update(inbox_id: String) -> UnverifiedIdentityUpdate { + UnverifiedIdentityUpdate::new_test( + // TODO:nm Add default actions + vec![], + inbox_id, + ) } #[tokio::test] @@ -196,7 +206,7 @@ mod tests { updates: vec![IdentityUpdateLog { sequence_id: 1, server_timestamp_ns: 1, - update: Some(identity_update.to_proto()), + update: Some(identity_update.into()), }], }], }) diff --git a/xmtp_mls/src/api/mls.rs b/xmtp_mls/src/api/mls.rs index eaf3af6b4..dd79a1a76 100644 --- a/xmtp_mls/src/api/mls.rs +++ b/xmtp_mls/src/api/mls.rs @@ -2,19 +2,15 @@ use std::collections::HashMap; use super::ApiClientWrapper; use crate::{retry_async, XmtpApi}; -use xmtp_proto::api_client::{ - Error as ApiError, ErrorKind, GroupMessageStream, WelcomeMessageStream, -}; +use xmtp_proto::api_client::{Error as ApiError, ErrorKind}; use xmtp_proto::xmtp::mls::api::v1::{ - get_identity_updates_response::update::Kind as UpdateKind, group_message_input::{Version as GroupMessageInputVersion, V1 as GroupMessageInputV1}, subscribe_group_messages_request::Filter as GroupFilterProto, subscribe_welcome_messages_request::Filter as WelcomeFilterProto, - FetchKeyPackagesRequest, GetIdentityUpdatesRequest, GroupMessage, GroupMessageInput, - KeyPackageUpload, PagingInfo, QueryGroupMessagesRequest, QueryWelcomeMessagesRequest, - RegisterInstallationRequest, SendGroupMessagesRequest, SendWelcomeMessagesRequest, - SortDirection, SubscribeGroupMessagesRequest, SubscribeWelcomeMessagesRequest, - UploadKeyPackageRequest, WelcomeMessage, WelcomeMessageInput, + FetchKeyPackagesRequest, GroupMessage, GroupMessageInput, KeyPackageUpload, PagingInfo, + QueryGroupMessagesRequest, QueryWelcomeMessagesRequest, SendGroupMessagesRequest, + SendWelcomeMessagesRequest, SortDirection, SubscribeGroupMessagesRequest, + SubscribeWelcomeMessagesRequest, UploadKeyPackageRequest, WelcomeMessage, WelcomeMessageInput, }; /// A filter for querying group messages @@ -63,8 +59,6 @@ pub enum IdentityUpdate { type KeyPackageMap = HashMap, Vec>; -type IdentityUpdatesMap = HashMap>; - impl ApiClientWrapper where ApiClient: XmtpApi, @@ -157,33 +151,6 @@ where Ok(out) } - /// Register an XMTP KeyPackage with the network. - /// New InboxID clients should set `is_inbox_id_credential` to true. - /// V3 clients should have `is_inbox_id_credential` to `false`. - /// Not indicating your client version will result in validation failure. - #[tracing::instrument(level = "trace", skip_all)] - pub async fn register_installation( - &self, - key_package: Vec, - is_inbox_id_credential: bool, - ) -> Result, ApiError> { - let res = retry_async!( - self.retry_strategy, - (async { - self.api_client - .register_installation(RegisterInstallationRequest { - key_package: Some(KeyPackageUpload { - key_package_tls_serialized: key_package.to_vec(), - }), - is_inbox_id_credential, - }) - .await - }) - )?; - - Ok(res.installation_key) - } - /// Upload a KeyPackage to the network /// New InboxID clients should set `is_inbox_id_credential` to true. /// V3 clients should have `is_inbox_id_credential` to `false`. @@ -266,66 +233,6 @@ where Ok(()) } - #[tracing::instrument(level = "trace", skip_all)] - pub async fn get_identity_updates( - &self, - start_time_ns: u64, - account_addresses: Vec, - ) -> Result { - let result = retry_async!( - self.retry_strategy, - (async { - self.api_client - .get_identity_updates(GetIdentityUpdatesRequest { - start_time_ns, - account_addresses: account_addresses.clone(), - }) - .await - }) - )?; - - if result.updates.len() != account_addresses.len() { - println!("mismatched number of results"); - return Err(ApiError::new(ErrorKind::MlsError)); - } - - let mapping: IdentityUpdatesMap = result - .updates - .into_iter() - .zip(account_addresses.into_iter()) - .map(|(update, account_address)| { - ( - account_address, - update - .updates - .into_iter() - .map(|update| match update.kind { - Some(UpdateKind::NewInstallation(new_installation)) => { - IdentityUpdate::NewInstallation(NewInstallation { - timestamp_ns: update.timestamp_ns, - installation_key: new_installation.installation_key, - credential_bytes: new_installation.credential_identity, - }) - } - Some(UpdateKind::RevokedInstallation(revoke_installation)) => { - IdentityUpdate::RevokeInstallation(RevokeInstallation { - timestamp_ns: update.timestamp_ns, - installation_key: revoke_installation.installation_key, - }) - } - None => { - println!("no update kind"); - IdentityUpdate::Invalid - } - }) - .collect(), - ) - }) - .collect(); - - Ok(mapping) - } - #[tracing::instrument(level = "trace", skip_all)] pub async fn send_group_messages(&self, group_messages: Vec<&[u8]>) -> Result<(), ApiError> { let to_send: Vec = group_messages @@ -355,7 +262,7 @@ where pub async fn subscribe_group_messages( &self, filters: Vec, - ) -> Result { + ) -> Result> + '_, ApiError> { self.api_client .subscribe_group_messages(SubscribeGroupMessagesRequest { filters: filters.into_iter().map(|f| f.into()).collect(), @@ -367,7 +274,7 @@ where &self, installation_key: Vec, id_cursor: Option, - ) -> Result { + ) -> Result> + '_, ApiError> { self.api_client .subscribe_welcome_messages(SubscribeWelcomeMessagesRequest { filters: vec![WelcomeFilterProto { @@ -387,31 +294,11 @@ pub mod tests { use xmtp_proto::{ api_client::{Error, ErrorKind}, xmtp::mls::api::v1::{ - fetch_key_packages_response::KeyPackage, - get_identity_updates_response::{ - update::Kind as UpdateKind, NewInstallationUpdate, Update, WalletUpdates, - }, - FetchKeyPackagesResponse, GetIdentityUpdatesResponse, PagingInfo, - QueryGroupMessagesResponse, RegisterInstallationResponse, + fetch_key_packages_response::KeyPackage, FetchKeyPackagesResponse, PagingInfo, + QueryGroupMessagesResponse, }, }; - #[tokio::test] - async fn test_register_installation() { - let mut mock_api = MockApiClient::new(); - mock_api.expect_register_installation().returning(move |_| { - Ok(RegisterInstallationResponse { - installation_key: vec![1, 2, 3], - }) - }); - let wrapper = ApiClientWrapper::new(mock_api, Retry::default()); - let result = wrapper - .register_installation(vec![2, 3, 4], false) - .await - .unwrap(); - assert_eq!(result, vec![1, 2, 3]); - } - #[tokio::test] async fn test_upload_key_package() { let mut mock_api = MockApiClient::new(); @@ -465,81 +352,6 @@ pub mod tests { } } - #[tokio::test] - async fn test_get_identity_updates() { - let mut mock_api = MockApiClient::new(); - let start_time_ns = 12; - let account_addresses = vec!["wallet1".to_string(), "wallet2".to_string()]; - // account_addresses gets moved below but needs to be used for assertions later - let account_addresses_clone = account_addresses.clone(); - mock_api - .expect_get_identity_updates() - .withf(move |req| { - req.start_time_ns.eq(&start_time_ns) && req.account_addresses.eq(&account_addresses) - }) - .returning(move |_| { - Ok(GetIdentityUpdatesResponse { - updates: { - vec![ - WalletUpdates { - updates: vec![Update { - timestamp_ns: 1, - kind: Some(UpdateKind::NewInstallation( - NewInstallationUpdate { - installation_key: vec![1, 2, 3], - credential_identity: vec![4, 5, 6], - }, - )), - }], - }, - WalletUpdates { - updates: vec![Update { - timestamp_ns: 2, - kind: Some(UpdateKind::NewInstallation( - NewInstallationUpdate { - installation_key: vec![7, 8, 9], - credential_identity: vec![10, 11, 12], - }, - )), - }], - }, - ] - }, - }) - }); - - let wrapper = ApiClientWrapper::new(mock_api, Retry::default()); - let result = wrapper - .get_identity_updates(start_time_ns, account_addresses_clone.clone()) - .await - .unwrap(); - assert_eq!(result.len(), 2); - - for (k, v) in result { - if k.eq(&account_addresses_clone[0]) { - assert_eq!(v.len(), 1); - assert_eq!( - v[0], - super::IdentityUpdate::NewInstallation(super::NewInstallation { - installation_key: vec![1, 2, 3], - credential_bytes: vec![4, 5, 6], - timestamp_ns: 1, - }) - ); - } else { - assert_eq!(v.len(), 1); - assert_eq!( - v[0], - super::IdentityUpdate::NewInstallation(super::NewInstallation { - installation_key: vec![7, 8, 9], - credential_bytes: vec![10, 11, 12], - timestamp_ns: 2, - }) - ); - } - } - } - #[tokio::test] async fn test_read_group_messages_single_page() { let mut mock_api = MockApiClient::new(); @@ -610,7 +422,7 @@ pub mod tests { // Set expectation for first request with no cursor mock_api .expect_query_group_messages() - .withf(move |req| match req.paging_info.clone() { + .withf(move |req| match req.paging_info { Some(paging_info) => paging_info.id_cursor == 0, None => true, }) @@ -629,7 +441,7 @@ pub mod tests { // Set expectation for requests with a cursor mock_api .expect_query_group_messages() - .withf(|req| match req.paging_info.clone() { + .withf(|req| match req.paging_info { Some(paging_info) => paging_info.id_cursor > 0, None => false, }) diff --git a/xmtp_mls/src/api/mod.rs b/xmtp_mls/src/api/mod.rs index 2f1ba91ef..802bebe84 100644 --- a/xmtp_mls/src/api/mod.rs +++ b/xmtp_mls/src/api/mod.rs @@ -3,7 +3,10 @@ pub mod mls; #[cfg(test)] pub mod test_utils; -use crate::{retry::Retry, XmtpApi}; +use crate::{ + retry::{Retry, RetryableError}, + XmtpApi, +}; use thiserror::Error; use xmtp_id::associations::DeserializationError as AssociationDeserializationError; use xmtp_proto::api_client::Error as ApiError; @@ -19,6 +22,12 @@ pub enum WrappedApiError { AssociationDeserialization(#[from] AssociationDeserializationError), } +impl RetryableError for WrappedApiError { + fn is_retryable(&self) -> bool { + matches!(self, Self::Api(_)) + } +} + #[derive(Debug)] pub struct ApiClientWrapper { api_client: ApiClient, diff --git a/xmtp_mls/src/api/test_utils.rs b/xmtp_mls/src/api/test_utils.rs index f1a7ccec2..604804aa5 100644 --- a/xmtp_mls/src/api/test_utils.rs +++ b/xmtp_mls/src/api/test_utils.rs @@ -1,37 +1,27 @@ -use async_trait::async_trait; use mockall::mock; -use xmtp_api_grpc::grpc_api_helper::Client as GrpcClient; use xmtp_proto::{ - api_client::{ - Error, GroupMessageStream, WelcomeMessageStream, XmtpIdentityClient, XmtpMlsClient, - }, - xmtp::identity::api::v1::{ - GetIdentityUpdatesRequest as GetIdentityUpdatesV2Request, - GetIdentityUpdatesResponse as GetIdentityUpdatesV2Response, GetInboxIdsRequest, - GetInboxIdsResponse, PublishIdentityUpdateRequest, PublishIdentityUpdateResponse, - }, - xmtp::mls::api::v1::{ - group_message::{Version as GroupMessageVersion, V1 as GroupMessageV1}, - FetchKeyPackagesRequest, FetchKeyPackagesResponse, GetIdentityUpdatesRequest, - GetIdentityUpdatesResponse, GroupMessage, QueryGroupMessagesRequest, - QueryGroupMessagesResponse, QueryWelcomeMessagesRequest, QueryWelcomeMessagesResponse, - RegisterInstallationRequest, RegisterInstallationResponse, SendGroupMessagesRequest, - SendWelcomeMessagesRequest, SubscribeGroupMessagesRequest, SubscribeWelcomeMessagesRequest, - UploadKeyPackageRequest, + api_client::{ClientWithMetadata, Error, XmtpIdentityClient, XmtpMlsClient, XmtpMlsStreams}, + xmtp::{ + identity::api::v1::{ + GetIdentityUpdatesRequest as GetIdentityUpdatesV2Request, + GetIdentityUpdatesResponse as GetIdentityUpdatesV2Response, GetInboxIdsRequest, + GetInboxIdsResponse, PublishIdentityUpdateRequest, PublishIdentityUpdateResponse, + }, + mls::api::v1::{ + group_message::{Version as GroupMessageVersion, V1 as GroupMessageV1}, + FetchKeyPackagesRequest, FetchKeyPackagesResponse, GroupMessage, + QueryGroupMessagesRequest, QueryGroupMessagesResponse, QueryWelcomeMessagesRequest, + QueryWelcomeMessagesResponse, SendGroupMessagesRequest, SendWelcomeMessagesRequest, + SubscribeGroupMessagesRequest, SubscribeWelcomeMessagesRequest, + UploadKeyPackageRequest, + }, }, }; -use super::ApiClientWrapper; -use crate::retry::Retry; +#[cfg(feature = "http-api")] +use xmtp_proto::xmtp::mls::api::v1::WelcomeMessage; -pub async fn get_test_api_client() -> ApiClientWrapper { - ApiClientWrapper::new( - GrpcClient::create("http://localhost:5556".to_string(), false) - .await - .unwrap(), - Retry::default(), - ) -} +use crate::XmtpTestClient; pub fn build_group_messages(num_messages: usize, group_id: Vec) -> Vec { let mut out: Vec = vec![]; @@ -53,12 +43,12 @@ pub fn build_group_messages(num_messages: usize, group_id: Vec) -> Vec Result<(), Error>; + fn set_app_version(&mut self, version: String) -> Result<(), Error>; + } + impl XmtpMlsClient for ApiClient { - async fn register_installation( - &self, - request: RegisterInstallationRequest, - ) -> Result; async fn upload_key_package(&self, request: UploadKeyPackageRequest) -> Result<(), Error>; async fn fetch_key_packages( &self, @@ -66,20 +56,34 @@ mock! { ) -> Result; async fn send_group_messages(&self, request: SendGroupMessagesRequest) -> Result<(), Error>; async fn send_welcome_messages(&self, request: SendWelcomeMessagesRequest) -> Result<(), Error>; - async fn get_identity_updates( - &self, - request: GetIdentityUpdatesRequest, - ) -> Result; async fn query_group_messages(&self, request: QueryGroupMessagesRequest) -> Result; async fn query_welcome_messages(&self, request: QueryWelcomeMessagesRequest) -> Result; - async fn subscribe_group_messages(&self, request: SubscribeGroupMessagesRequest) -> Result; - async fn subscribe_welcome_messages(&self, request: SubscribeWelcomeMessagesRequest) -> Result; } - #[async_trait] + impl XmtpMlsStreams for ApiClient { + #[cfg(not(feature = "http-api"))] + type GroupMessageStream<'a> = xmtp_api_grpc::GroupMessageStream; + #[cfg(not(feature = "http-api"))] + type WelcomeMessageStream<'a> = xmtp_api_grpc::WelcomeMessageStream; + + #[cfg(feature = "http-api")] + type GroupMessageStream<'a> = futures::stream::BoxStream<'static, Result>; + #[cfg(feature = "http-api")] + type WelcomeMessageStream<'a> = futures::stream::BoxStream<'static, Result>; + + + async fn subscribe_group_messages(&self, request: SubscribeGroupMessagesRequest) -> Result<::GroupMessageStream<'static>, Error>; + async fn subscribe_welcome_messages(&self, request: SubscribeWelcomeMessagesRequest) -> Result<::WelcomeMessageStream<'static>, Error>; + } + impl XmtpIdentityClient for ApiClient { async fn publish_identity_update(&self, request: PublishIdentityUpdateRequest) -> Result; async fn get_identity_updates_v2(&self, request: GetIdentityUpdatesV2Request) -> Result; async fn get_inbox_ids(&self, request: GetInboxIdsRequest) -> Result; } + + impl XmtpTestClient for ApiClient { + async fn create_local() -> Self { ApiClient } + async fn create_dev() -> Self { ApiClient } + } } diff --git a/xmtp_mls/src/builder.rs b/xmtp_mls/src/builder.rs index 63e8833c1..55abd4d19 100644 --- a/xmtp_mls/src/builder.rs +++ b/xmtp_mls/src/builder.rs @@ -1,7 +1,8 @@ -use log::debug; use thiserror::Error; +use tracing::debug; use xmtp_cryptography::signature::AddressValidationError; +use xmtp_id::scw_verifier::{RpcSmartContractWalletVerifier, SmartContractSignatureVerifier}; use crate::{ api::ApiClientWrapper, @@ -38,6 +39,10 @@ pub enum ClientBuilderError { Identity(#[from] crate::identity::IdentityError), #[error(transparent)] WrappedApiError(#[from] crate::api::WrappedApiError), + #[error(transparent)] + GroupError(#[from] crate::groups::GroupError), + #[error(transparent)] + ApiError(#[from] xmtp_proto::api_client::Error), } pub struct ClientBuilder { @@ -45,18 +50,24 @@ pub struct ClientBuilder { identity: Option, store: Option, identity_strategy: IdentityStrategy, + history_sync_url: Option, + app_version: Option, + scw_verifier: Option>, } impl ClientBuilder where ApiClient: XmtpApi, { - pub fn new(strat: IdentityStrategy) -> Self { + pub fn new(strategy: IdentityStrategy) -> Self { Self { api_client: None, identity: None, store: None, - identity_strategy: strat, + identity_strategy: strategy, + history_sync_url: None, + app_version: None, + scw_verifier: None, } } @@ -75,14 +86,41 @@ where self } + pub fn history_sync_url(mut self, url: &str) -> Self { + self.history_sync_url = Some(url.into()); + self + } + + pub fn app_version(mut self, version: String) -> Self { + self.app_version = Some(version); + self + } + + pub fn scw_signatuer_verifier(mut self, verifier: impl SmartContractSignatureVerifier) -> Self { + self.scw_verifier = Some(Box::new(verifier)); + self + } + pub async fn build(mut self) -> Result, ClientBuilderError> { debug!("Building client"); - let api_client = self - .api_client - .take() - .ok_or(ClientBuilderError::MissingParameter { - parameter: "api_client", - })?; + let mut api_client = + self.api_client + .take() + .ok_or(ClientBuilderError::MissingParameter { + parameter: "api_client", + })?; + api_client.set_libxmtp_version(env!("CARGO_PKG_VERSION").to_string())?; + if let Some(app_version) = self.app_version { + api_client.set_app_version(app_version)?; + } + + let scw_verifier = self.scw_verifier.take().unwrap_or_else(|| { + // TODO:nm Enforce that everyone provides this + Box::new(RpcSmartContractWalletVerifier::new( + "https://fixme.com".to_string(), + )) + }); + let api_client_wrapper = ApiClientWrapper::new(api_client, Retry::default()); let store = self .store @@ -91,7 +129,7 @@ where debug!("Initializing identity"); let identity = self .identity_strategy - .initialize_identity(&api_client_wrapper, &store) + .initialize_identity(&api_client_wrapper, &store, scw_verifier.as_ref()) .await?; // get sequence_id from identity updates and loaded into the DB @@ -102,33 +140,40 @@ where ) .await?; - Ok(Client::new(api_client_wrapper, identity, store)) + #[cfg(feature = "message-history")] + let client = Client::new(api_client_wrapper, identity, store, self.history_sync_url); + + #[cfg(not(feature = "message-history"))] + let client = Client::new(api_client_wrapper, identity, store); + + Ok(client) } } #[cfg(test)] mod tests { + use std::sync::atomic::AtomicBool; + use crate::api::ApiClientWrapper; use crate::builder::ClientBuilderError; use crate::identity::IdentityError; use crate::retry::Retry; + use crate::XmtpApi; use crate::{ api::test_utils::*, identity::Identity, storage::identity::StoredIdentity, utils::test::rand_vec, Store, }; - use ethers::signers::Signer; - use ethers_core::k256; use openmls::credentials::{Credential, CredentialType}; use openmls_basic_credential::SignatureKeyPair; use openmls_traits::types::SignatureScheme; use prost::Message; - use xmtp_api_grpc::grpc_api_helper::Client as GrpcClient; - use xmtp_cryptography::signature::h160addr_to_string; use xmtp_cryptography::utils::{generate_local_wallet, rng}; - use xmtp_id::associations::ValidatedLegacySignedPublicKey; - use xmtp_id::associations::{ - generate_inbox_id, test_utils::rand_u64, RecoverableEcdsaSignature, + use xmtp_id::associations::test_utils::MockSmartContractSignatureVerifier; + use xmtp_id::associations::unverified::{ + UnverifiedRecoverableEcdsaSignature, UnverifiedSignature, }; + use xmtp_id::associations::ValidatedLegacySignedPublicKey; + use xmtp_id::associations::{generate_inbox_id, test_utils::rand_u64}; use xmtp_proto::xmtp::identity::api::v1::{ get_inbox_ids_response::Response as GetInboxIdsResponseItem, GetInboxIdsResponse, }; @@ -146,14 +191,17 @@ mod tests { Client, InboxOwner, }; - async fn register_client(client: &Client, owner: &impl InboxOwner) { + async fn register_client(client: &Client, owner: &impl InboxOwner) { let mut signature_request = client.context.signature_request().unwrap(); let signature_text = signature_request.signature_text(); + let scw_verifier = MockSmartContractSignatureVerifier::new(true); signature_request - .add_signature(Box::new(RecoverableEcdsaSignature::new( - signature_text.clone(), - owner.sign(&signature_text).unwrap().into(), - ))) + .add_signature( + UnverifiedSignature::RecoverableEcdsa(UnverifiedRecoverableEcdsaSignature::new( + owner.sign(&signature_text).unwrap().into(), + )), + &scw_verifier, + ) .await .unwrap(); @@ -163,10 +211,10 @@ mod tests { /// Generate a random legacy key proto bytes and corresponding account address. async fn generate_random_legacy_key() -> (Vec, String) { let wallet = generate_local_wallet(); - let address = h160addr_to_string(wallet.address()); + let address = wallet.get_address(); let created_ns = rand_u64(); - let secret_key = k256::ecdsa::SigningKey::random(&mut rng()); - let public_key = k256::ecdsa::VerifyingKey::from(&secret_key); + let secret_key = ethers::core::k256::ecdsa::SigningKey::random(&mut rng()); + let public_key = ethers::core::k256::ecdsa::VerifyingKey::from(&secret_key); let public_key_bytes = public_key.to_sec1_bytes().to_vec(); let mut public_key_buf = vec![]; UnsignedPublicKey { @@ -180,7 +228,7 @@ mod tests { .encode(&mut public_key_buf) .unwrap(); let message = ValidatedLegacySignedPublicKey::text(&public_key_buf); - let signed_public_key = wallet.sign_message(message).await.unwrap().to_vec(); + let signed_public_key: Vec = wallet.sign(&message).unwrap().into(); let (bytes, recovery_id) = signed_public_key.as_slice().split_at(64); let recovery_id = recovery_id[0]; let signed_private_key: SignedPrivateKey = SignedPrivateKey { @@ -300,7 +348,7 @@ mod tests { for test_case in identity_strategies_test_cases { let result = ClientBuilder::new(test_case.strategy) .temp_store() - .local_grpc() + .local_client() .await .build() .await; @@ -330,21 +378,24 @@ mod tests { 0, Some(legacy_key.clone()), ); - let store = - EncryptedMessageStore::new_unencrypted(StorageOption::Persistent(tmp_path())).unwrap(); + let store = EncryptedMessageStore::new( + StorageOption::Persistent(tmp_path()), + EncryptedMessageStore::generate_enc_key(), + ) + .unwrap(); - let client1: Client = ClientBuilder::new(identity_strategy.clone()) + let client1 = ClientBuilder::new(identity_strategy.clone()) .store(store.clone()) - .local_grpc() + .local_client() .await .build() .await .unwrap(); assert!(client1.context.signature_request().is_none()); - let client2: Client = ClientBuilder::new(IdentityStrategy::CachedOnly) + let client2 = ClientBuilder::new(IdentityStrategy::CachedOnly) .store(store.clone()) - .local_grpc() + .local_client() .await .build() .await @@ -353,14 +404,14 @@ mod tests { assert!(client1.inbox_id() == client2.inbox_id()); assert!(client1.installation_public_key() == client2.installation_public_key()); - let client3: Client = ClientBuilder::new(IdentityStrategy::CreateIfNotFound( + let client3 = ClientBuilder::new(IdentityStrategy::CreateIfNotFound( generate_inbox_id(&legacy_account_address, &0), legacy_account_address.to_string(), 0, None, )) .store(store.clone()) - .local_grpc() + .local_client() .await .build() .await @@ -369,14 +420,14 @@ mod tests { assert!(client1.inbox_id() == client3.inbox_id()); assert!(client1.installation_public_key() == client3.installation_public_key()); - let client4: Client = ClientBuilder::new(IdentityStrategy::CreateIfNotFound( + let client4 = ClientBuilder::new(IdentityStrategy::CreateIfNotFound( generate_inbox_id(&legacy_account_address, &0), legacy_account_address.to_string(), 0, Some(legacy_key), )) .temp_store() - .local_grpc() + .local_client() .await .build() .await @@ -391,9 +442,13 @@ mod tests { async fn api_identity_mismatch() { let mut mock_api = MockApiClient::new(); let tmpdb = tmp_path(); + let scw_verifier = MockSmartContractSignatureVerifier::new(true); - let store = - EncryptedMessageStore::new_unencrypted(StorageOption::Persistent(tmpdb)).unwrap(); + let store = EncryptedMessageStore::new( + StorageOption::Persistent(tmpdb), + EncryptedMessageStore::generate_enc_key(), + ) + .unwrap(); let nonce = 0; let address = generate_local_wallet().get_address(); let inbox_id = generate_inbox_id(&address, &nonce); @@ -415,7 +470,7 @@ mod tests { IdentityStrategy::CreateIfNotFound("other_inbox_id".to_string(), address, nonce, None); assert!(matches!( identity - .initialize_identity(&wrapper, &store) + .initialize_identity(&wrapper, &store, &scw_verifier) .await .unwrap_err(), IdentityError::NewIdentity(msg) if msg == "Inbox ID mismatch" @@ -427,9 +482,13 @@ mod tests { async fn api_identity_happy_path() { let mut mock_api = MockApiClient::new(); let tmpdb = tmp_path(); + let scw_verifier = MockSmartContractSignatureVerifier::new(true); - let store = - EncryptedMessageStore::new_unencrypted(StorageOption::Persistent(tmpdb)).unwrap(); + let store = EncryptedMessageStore::new( + StorageOption::Persistent(tmpdb), + EncryptedMessageStore::generate_enc_key(), + ) + .unwrap(); let nonce = 0; let address = generate_local_wallet().get_address(); let inbox_id = generate_inbox_id(&address, &nonce); @@ -448,7 +507,12 @@ mod tests { let wrapper = ApiClientWrapper::new(mock_api, Retry::default()); let identity = IdentityStrategy::CreateIfNotFound(inbox_id.clone(), address, nonce, None); - assert!(dbg!(identity.initialize_identity(&wrapper, &store).await).is_ok()); + assert!(dbg!( + identity + .initialize_identity(&wrapper, &store, &scw_verifier) + .await + ) + .is_ok()); } // Use a stored identity as long as the inbox_id matches the one provided. @@ -456,9 +520,13 @@ mod tests { async fn stored_identity_happy_path() { let mock_api = MockApiClient::new(); let tmpdb = tmp_path(); + let scw_verifier = MockSmartContractSignatureVerifier::new(true); - let store = - EncryptedMessageStore::new_unencrypted(StorageOption::Persistent(tmpdb)).unwrap(); + let store = EncryptedMessageStore::new( + StorageOption::Persistent(tmpdb), + EncryptedMessageStore::generate_enc_key(), + ) + .unwrap(); let nonce = 0; let address = generate_local_wallet().get_address(); let inbox_id = generate_inbox_id(&address, &nonce); @@ -468,34 +536,45 @@ mod tests { installation_keys: SignatureKeyPair::new(SignatureScheme::ED25519).unwrap(), credential: Credential::new(CredentialType::Basic, rand_vec()), signature_request: None, + is_ready: AtomicBool::new(true), }) - .into(); + .try_into() + .unwrap(); stored.store(&store.conn().unwrap()).unwrap(); let wrapper = ApiClientWrapper::new(mock_api, Retry::default()); let identity = IdentityStrategy::CreateIfNotFound(inbox_id.clone(), address, nonce, None); - assert!(identity.initialize_identity(&wrapper, &store).await.is_ok()); + assert!(identity + .initialize_identity(&wrapper, &store, &scw_verifier) + .await + .is_ok()); } #[tokio::test] async fn stored_identity_mismatch() { let mock_api = MockApiClient::new(); + let scw_verifier = MockSmartContractSignatureVerifier::new(true); let nonce = 0; let address = generate_local_wallet().get_address(); let stored_inbox_id = generate_inbox_id(&address, &nonce); let tmpdb = tmp_path(); - let store = - EncryptedMessageStore::new_unencrypted(StorageOption::Persistent(tmpdb)).unwrap(); + let store = EncryptedMessageStore::new( + StorageOption::Persistent(tmpdb), + EncryptedMessageStore::generate_enc_key(), + ) + .unwrap(); let stored: StoredIdentity = (&Identity { inbox_id: stored_inbox_id.clone(), installation_keys: SignatureKeyPair::new(SignatureScheme::ED25519).unwrap(), credential: Credential::new(CredentialType::Basic, rand_vec()), signature_request: None, + is_ready: AtomicBool::new(true), }) - .into(); + .try_into() + .unwrap(); stored.store(&store.conn().unwrap()).unwrap(); @@ -505,7 +584,7 @@ mod tests { let identity = IdentityStrategy::CreateIfNotFound(inbox_id.clone(), address.clone(), nonce, None); let err = identity - .initialize_identity(&wrapper, &store) + .initialize_identity(&wrapper, &store, &scw_verifier) .await .unwrap_err(); @@ -518,11 +597,11 @@ mod tests { async fn identity_persistence_test() { let tmpdb = tmp_path(); let wallet = &generate_local_wallet(); + let db_key = EncryptedMessageStore::generate_enc_key(); // Generate a new Wallet + Store let store_a = - EncryptedMessageStore::new_unencrypted(StorageOption::Persistent(tmpdb.clone())) - .unwrap(); + EncryptedMessageStore::new(StorageOption::Persistent(tmpdb.clone()), db_key).unwrap(); let nonce = 1; let inbox_id = generate_inbox_id(&wallet.get_address(), &nonce); @@ -532,7 +611,7 @@ mod tests { nonce, None, )) - .local_grpc() + .local_client() .await .store(store_a) .build() @@ -540,14 +619,14 @@ mod tests { .unwrap(); register_client(&client_a, wallet).await; + assert!(client_a.identity().is_ready()); let keybytes_a = client_a.installation_public_key(); drop(client_a); // Reload the existing store and wallet let store_b = - EncryptedMessageStore::new_unencrypted(StorageOption::Persistent(tmpdb.clone())) - .unwrap(); + EncryptedMessageStore::new(StorageOption::Persistent(tmpdb.clone()), db_key).unwrap(); let client_b = ClientBuilder::new(IdentityStrategy::CreateIfNotFound( inbox_id, @@ -555,7 +634,7 @@ mod tests { nonce, None, )) - .local_grpc() + .local_client() .await .store(store_b) .build() @@ -577,7 +656,7 @@ mod tests { // generate_local_wallet().get_address(), // None, // )) - // .local_grpc() + // .local_client() // .await // .store(store_c) // .build() @@ -586,10 +665,9 @@ mod tests { // Use cached only strategy let store_d = - EncryptedMessageStore::new_unencrypted(StorageOption::Persistent(tmpdb.clone())) - .unwrap(); + EncryptedMessageStore::new(StorageOption::Persistent(tmpdb.clone()), db_key).unwrap(); let client_d = ClientBuilder::new(IdentityStrategy::CachedOnly) - .local_grpc() + .local_client() .await .store(store_d) .build() diff --git a/xmtp_mls/src/client.rs b/xmtp_mls/src/client.rs index 9d4ecb8e6..6f5bc6246 100644 --- a/xmtp_mls/src/client.rs +++ b/xmtp_mls/src/client.rs @@ -1,7 +1,14 @@ -use std::{collections::HashMap, mem::Discriminant, sync::Arc}; +use std::{ + collections::HashMap, + mem::Discriminant, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, +}; use futures::{ - stream::{self, StreamExt}, + stream::{self, FuturesUnordered, StreamExt}, Future, }; use openmls::{ @@ -9,18 +16,20 @@ use openmls::{ framing::{MlsMessageBodyIn, MlsMessageIn}, group::GroupEpoch, messages::Welcome, - prelude::tls_codec::{Deserialize, Error as TlsCodecError, Serialize}, + prelude::tls_codec::{Deserialize, Error as TlsCodecError}, }; use openmls_traits::OpenMlsProvider; use prost::EncodeError; use thiserror::Error; +use tokio::sync::broadcast; use xmtp_cryptography::signature::{sanitize_evm_addresses, AddressValidationError}; use xmtp_id::{ associations::{ builder::{SignatureRequest, SignatureRequestError}, - AssociationError, + AssociationError, AssociationState, SignatureError, }, + scw_verifier::{RpcSmartContractWalletVerifier, SmartContractSignatureVerifier}, InboxId, }; @@ -32,17 +41,23 @@ use xmtp_proto::xmtp::mls::api::v1::{ use crate::{ api::ApiClientWrapper, groups::{ - validated_commit::CommitValidationError, GroupError, GroupMetadataOptions, IntentError, - MlsGroup, PreconfiguredPolicies, + group_permissions::PolicySet, validated_commit::CommitValidationError, GroupError, + GroupMetadataOptions, IntentError, MlsGroup, }, identity::{parse_credential, Identity, IdentityError}, - identity_updates::IdentityUpdateError, + identity_updates::{load_identity_updates, IdentityUpdateError}, + mutex_registry::MutexRegistry, + retry::Retry, + retry_async, retryable, storage::{ + consent_record::{ConsentState, ConsentType, StoredConsentRecord}, db_connection::DbConnection, group::{GroupMembershipState, StoredGroup}, + group_message::StoredGroupMessage, refresh_state::EntityKind, sql_key_store, EncryptedMessageStore, StorageError, }, + subscriptions::LocalEvents, verified_key_package_v2::{KeyPackageVerificationError, VerifiedKeyPackageV2}, xmtp_openmls_provider::XmtpOpenMlsProvider, Fetch, XmtpApi, @@ -83,17 +98,38 @@ pub enum ClientError { StreamInconsistency(String), #[error("Association error: {0}")] Association(#[from] AssociationError), + #[error("signature validation error: {0}")] + SignatureValidation(#[from] SignatureError), #[error(transparent)] IdentityUpdate(#[from] IdentityUpdateError), #[error(transparent)] SignatureRequest(#[from] SignatureRequestError), // the box is to prevent infinite cycle between client and group errors #[error(transparent)] - Group(#[from] Box), + Group(Box), #[error("generic:{0}")] Generic(String), } +impl From for ClientError { + fn from(err: GroupError) -> ClientError { + ClientError::Group(Box::new(err)) + } +} + +impl crate::retry::RetryableError for ClientError { + fn is_retryable(&self) -> bool { + match self { + ClientError::Group(group_error) => retryable!(group_error), + ClientError::Diesel(diesel_error) => retryable!(diesel_error), + ClientError::Api(api_error) => retryable!(api_error), + ClientError::Storage(storage_error) => retryable!(storage_error), + ClientError::Generic(err) => err.contains("database is locked"), + _ => false, + } + } +} + /// An enum of errors that can occur when reading and processing a message off the network #[derive(Debug, Error)] pub enum MessageProcessingError { @@ -111,13 +147,9 @@ pub enum MessageProcessingError { #[error(transparent)] Identity(#[from] IdentityError), #[error("openmls process message error: {0}")] - OpenMlsProcessMessage( - #[from] openmls::prelude::ProcessMessageError, - ), - #[error("merge pending commit: {0}")] - MergePendingCommit(#[from] openmls::group::MergePendingCommitError), + OpenMlsProcessMessage(#[from] openmls::prelude::ProcessMessageError), #[error("merge staged commit: {0}")] - MergeStagedCommit(#[from] openmls::group::MergeCommitError), + MergeStagedCommit(#[from] openmls::group::MergeCommitError), #[error( "no pending commit to merge. group epoch is {group_epoch:?} and got {message_epoch:?}" )] @@ -147,16 +179,28 @@ pub enum MessageProcessingError { WrongCredentialType(#[from] BasicCredentialError), #[error("proto decode error: {0}")] DecodeError(#[from] prost::DecodeError), + #[error("clear pending commit error: {0}")] + ClearPendingCommit(#[from] sql_key_store::SqlKeyStoreError), #[error(transparent)] Group(#[from] Box), + #[error("Serialization/Deserialization Error {0}")] + Serde(#[from] serde_json::Error), #[error("generic:{0}")] Generic(String), + #[error("intent is missing staged_commit field")] + IntentMissingStagedCommit, } impl crate::retry::RetryableError for MessageProcessingError { fn is_retryable(&self) -> bool { match self { - Self::Storage(s) => s.is_retryable(), + Self::Group(group_error) => retryable!(group_error), + Self::Identity(identity_error) => retryable!(identity_error), + Self::OpenMlsProcessMessage(err) => retryable!(err), + Self::MergeStagedCommit(err) => retryable!(err), + Self::Diesel(diesel_error) => retryable!(diesel_error), + Self::Storage(s) => retryable!(s), + Self::Generic(err) => err.contains("database is locked"), _ => false, } } @@ -179,6 +223,9 @@ impl From<&str> for ClientError { pub struct Client { pub(crate) api_client: ApiClientWrapper, pub(crate) context: Arc, + #[cfg(feature = "message-history")] + pub(crate) history_sync_url: Option, + pub(crate) local_events: broadcast::Sender, } /// The local context a XMTP MLS needs to function: @@ -190,6 +237,7 @@ pub struct XmtpMlsLocalContext { pub(crate) identity: Identity, /// XMTP Local Storage pub(crate) store: EncryptedMessageStore, + pub(crate) mutexes: MutexRegistry, } impl XmtpMlsLocalContext { @@ -208,15 +256,16 @@ impl XmtpMlsLocalContext { self.identity.sequence_id(conn) } + /// Pulls a new database connection and creates a new provider + pub fn mls_provider(&self) -> Result { + Ok(self.store.conn()?.into()) + } + /// Integrators should always check the `signature_request` return value of this function before calling [`register_identity`](Self::register_identity). /// If `signature_request` returns `None`, then the wallet signature is not required and [`register_identity`](Self::register_identity) can be called with None as an argument. pub fn signature_request(&self) -> Option { self.identity.signature_request() } - - pub(crate) fn mls_provider(&self, conn: DbConnection) -> XmtpOpenMlsProvider { - XmtpOpenMlsProvider::new(conn) - } } impl Client @@ -230,11 +279,20 @@ where api_client: ApiClientWrapper, identity: Identity, store: EncryptedMessageStore, + #[cfg(feature = "message-history")] history_sync_url: Option, ) -> Self { - let context = XmtpMlsLocalContext { identity, store }; + let context = XmtpMlsLocalContext { + identity, + store, + mutexes: MutexRegistry::new(), + }; + let (tx, _) = broadcast::channel(10); Self { api_client, context: Arc::new(context), + #[cfg(feature = "message-history")] + history_sync_url, + local_events: tx, } } @@ -246,11 +304,124 @@ where self.context.inbox_id() } + /// Pulls a connection and creates a new MLS Provider + pub fn mls_provider(&self) -> Result { + self.context.mls_provider() + } + + pub async fn find_inbox_id_from_address( + &self, + address: String, + ) -> Result, ClientError> { + let results = self + .find_inbox_ids_from_addresses(vec![address.clone()]) + .await?; + if let Some(first_result) = results.into_iter().next() { + Ok(first_result) + } else { + Ok(None) + } + } + + pub async fn find_inbox_ids_from_addresses( + &self, + addresses: Vec, + ) -> Result>, ClientError> { + let sanitized_addresses = sanitize_evm_addresses(addresses.clone())?; + let mut results = self + .api_client + .get_inbox_ids(sanitized_addresses.clone()) + .await?; + let inbox_ids: Vec> = sanitized_addresses + .into_iter() + .map(|address| results.remove(&address)) + .collect(); + + Ok(inbox_ids) + } + /// Get sequence id, may not be consistent with the backend pub fn inbox_sequence_id(&self, conn: &DbConnection) -> Result { self.context.inbox_sequence_id(conn) } + pub async fn inbox_state( + &self, + refresh_from_network: bool, + ) -> Result { + let conn = self.store().conn()?; + let inbox_id = self.inbox_id(); + if refresh_from_network { + load_identity_updates(&self.api_client, &conn, vec![inbox_id.clone()]).await?; + } + let state = self.get_association_state(&conn, inbox_id, None).await?; + Ok(state) + } + + // set the consent record in the database + // if the consent record is an address also set the inboxId + pub async fn set_consent_states( + &self, + mut records: Vec, + ) -> Result<(), ClientError> { + let conn = self.store().conn()?; + + let mut new_records = Vec::new(); + let mut addresses_to_lookup = Vec::new(); + let mut record_indices = Vec::new(); + + for (index, record) in records.iter().enumerate() { + if record.entity_type == ConsentType::Address { + addresses_to_lookup.push(record.entity.clone()); + record_indices.push(index); + } + } + + let inbox_ids = self + .find_inbox_ids_from_addresses(addresses_to_lookup) + .await?; + + for (i, inbox_id_opt) in inbox_ids.into_iter().enumerate() { + if let Some(inbox_id) = inbox_id_opt { + let record = &records[record_indices[i]]; + new_records.push(StoredConsentRecord::new( + ConsentType::InboxId, + record.state, + inbox_id, + )); + } + } + + records.extend(new_records); + conn.insert_or_replace_consent_records(records)?; + + Ok(()) + } + + // get the consent record from the database + // if the consent record is an address also get the inboxId instead + pub async fn get_consent_state( + &self, + entity_type: ConsentType, + entity: String, + ) -> Result { + let conn = self.store().conn()?; + let record = if entity_type == ConsentType::Address { + if let Some(inbox_id) = self.find_inbox_id_from_address(entity.clone()).await? { + conn.get_consent_record(inbox_id, ConsentType::InboxId)? + } else { + conn.get_consent_record(entity, entity_type)? + } + } else { + conn.get_consent_record(entity, entity_type)? + }; + + match record { + Some(rec) => Ok(rec.state), + None => Ok(ConsentState::Unknown), + } + } + pub fn store(&self) -> &EncryptedMessageStore { &self.context.store } @@ -270,37 +441,64 @@ where &self.context.identity } - pub(crate) fn mls_provider(&self, conn: DbConnection) -> XmtpOpenMlsProvider { - XmtpOpenMlsProvider::new(conn) - } - pub fn context(&self) -> &Arc { &self.context } + // TODO:nm Replace with real implementation + pub fn smart_contract_signature_verifier(&self) -> Box { + let scw_verifier = RpcSmartContractWalletVerifier::new("http://www.fake.com".to_string()); + Box::new(scw_verifier) + } + /// Create a new group with the default settings pub fn create_group( &self, - permissions: Option, + permissions_policy_set: Option, opts: GroupMetadataOptions, ) -> Result { - log::info!("creating group"); + tracing::info!("creating group"); let group = MlsGroup::create_and_insert( self.context.clone(), GroupMembershipState::Allowed, - permissions, + permissions_policy_set.unwrap_or_default(), opts, - ) - .map_err(Box::new)?; + )?; + + // notify any streams of the new group + let _ = self.local_events.send(LocalEvents::NewGroup(group.clone())); + + Ok(group) + } + + pub async fn create_group_with_members( + &self, + account_addresses: Vec, + permissions_policy_set: Option, + opts: GroupMetadataOptions, + ) -> Result { + tracing::info!("creating group"); + + let group = MlsGroup::create_and_insert( + self.context.clone(), + GroupMembershipState::Allowed, + permissions_policy_set.unwrap_or_default(), + opts, + )?; + + group.add_members(self, account_addresses).await?; + + // notify any streams of the new group + let _ = self.local_events.send(LocalEvents::NewGroup(group.clone())); Ok(group) } + #[cfg(feature = "message-history")] pub(crate) fn create_sync_group(&self) -> Result { - log::info!("creating sync group"); - let sync_group = - MlsGroup::create_and_insert_sync_group(self.context.clone()).map_err(Box::new)?; + tracing::info!("creating sync group"); + let sync_group = MlsGroup::create_and_insert_sync_group(self.context.clone())?; Ok(sync_group) } @@ -323,6 +521,20 @@ where } } + /// Look up a message by its ID + /// Returns a [`StoredGroupMessage`] if the message exists, or an error if it does not + pub fn message(&self, message_id: Vec) -> Result { + let conn = &mut self.store().conn()?; + let message = conn.get_group_message(&message_id)?; + match message { + Some(message) => Ok(message), + None => Err(ClientError::Storage(StorageError::NotFound(format!( + "message {}", + hex::encode(message_id) + )))), + } + } + /// Query for groups with optional filters /// /// Filters: @@ -352,36 +564,35 @@ where .collect()) } - /// Register the identity with the network - /// Callers should always check the result of [`text_to_sign`](Self::text_to_sign) before invoking this function. - /// - /// If `text_to_sign` returns `None`, then the wallet signature is not required and this function can be called with `None`. - /// - /// If `text_to_sign` returns `Some`, then the caller should sign the text with their wallet and pass the signature to this function. + /// Upload a Key Package to the network and publish the signed identity update + /// from the provided SignatureRequest pub async fn register_identity( &self, signature_request: SignatureRequest, ) -> Result<(), ClientError> { - log::info!("registering identity"); - self.apply_signature_request(signature_request).await?; - let connection = self.store().conn()?; - let provider = self.mls_provider(connection); + tracing::info!("registering identity"); + // Register the identity before applying the signature request + let provider: XmtpOpenMlsProvider = self.store().conn()?.into(); + self.identity() .register(&provider, &self.api_client) .await?; + self.apply_signature_request(signature_request).await?; + Ok(()) } /// Upload a new key package to the network replacing an existing key package /// This is expected to be run any time the client receives new Welcome messages pub async fn rotate_key_package(&self) -> Result<(), ClientError> { - let connection = self.store().conn()?; - let kp = self - .identity() - .new_key_package(&self.mls_provider(connection))?; - let kp_bytes = kp.tls_serialize_detached()?; - self.api_client.upload_key_package(kp_bytes, true).await?; + self.store() + .transaction_async(|provider| async move { + self.identity() + .rotate_key_package(&provider, &self.api_client) + .await + }) + .await?; Ok(()) } @@ -423,8 +634,7 @@ where ) -> Result, ClientError> { let key_package_results = self.api_client.fetch_key_packages(installation_ids).await?; - let conn = self.store().conn()?; - let mls_provider = self.mls_provider(conn); + let mls_provider = self.mls_provider()?; Ok(key_package_results .values() .map(|bytes| VerifiedKeyPackageV2::from_bytes(mls_provider.crypto(), bytes.as_slice())) @@ -446,7 +656,7 @@ where .transaction_async(|provider| async move { let is_updated = provider - .conn() + .conn_ref() .update_cursor(entity_id, entity_kind, cursor as i64)?; if !is_updated { return Err(MessageProcessingError::AlreadyProcessed(cursor)); @@ -460,6 +670,7 @@ where /// Returns any new groups created in the operation pub async fn sync_welcomes(&self) -> Result, ClientError> { let envelopes = self.query_welcome_messages(&self.store().conn()?).await?; + let num_envelopes = envelopes.len(); let id = self.installation_public_key(); let groups: Vec = stream::iter(envelopes.into_iter()) @@ -467,44 +678,105 @@ where let welcome_v1 = match extract_welcome_message(envelope) { Ok(inner) => inner, Err(err) => { - log::error!("failed to extract welcome message: {}", err); + tracing::error!("failed to extract welcome message: {}", err); return None; } }; - - self.process_for_id( - &id, - EntityKind::Welcome, - welcome_v1.id, - |provider| async move { - // TODO: Abort if error is retryable - match MlsGroup::create_from_encrypted_welcome( - self, - &provider, - welcome_v1.hpke_public_key.as_slice(), - welcome_v1.data, - welcome_v1.id as i64, + retry_async!( + Retry::default(), + (async { + let welcome_v1 = welcome_v1.clone(); + self.process_for_id( + &id, + EntityKind::Welcome, + welcome_v1.id, + |provider| async move { + let result = MlsGroup::create_from_encrypted_welcome( + self, + &provider, + welcome_v1.hpke_public_key.as_slice(), + welcome_v1.data, + welcome_v1.id as i64, + ) + .await; + + match result { + Ok(mls_group) => Ok(Some(mls_group)), + Err(err) => { + tracing::error!( + "failed to create group from welcome: {}", + err + ); + Err(MessageProcessingError::WelcomeProcessing( + err.to_string(), + )) + } + } + }, ) .await - { - Ok(mls_group) => Ok(Some(mls_group)), - Err(err) => { - log::error!("failed to create group from welcome: {}", err); - Err(MessageProcessingError::WelcomeProcessing(err.to_string())) - } - } - }, + }) ) - .await .ok() .flatten() }) .collect() .await; + // If any welcomes were found, rotate your key package + if num_envelopes > 0 { + self.rotate_key_package().await?; + } + Ok(groups) } + /// Sync all groups for the current user and return the number of groups that were synced. + /// Only active groups will be synced. + pub async fn sync_all_groups(&self, groups: Vec) -> Result { + // Acquire a single connection to be reused + let provider: XmtpOpenMlsProvider = self.mls_provider()?; + + let active_group_count = Arc::new(AtomicUsize::new(0)); + + let sync_futures = groups + .into_iter() + .map(|group| { + // create new provider ref that gets moved, leaving original + // provider alone. + let provider_ref = &provider; + let active_group_count = Arc::clone(&active_group_count); + async move { + let mls_group = group.load_mls_group(provider_ref)?; + tracing::info!("[{}] syncing group", self.inbox_id()); + tracing::info!( + "current epoch for [{}] in sync_all_groups() is Epoch: [{}]", + self.inbox_id(), + mls_group.epoch() + ); + if mls_group.is_active() { + group + .maybe_update_installations(provider_ref, None, self) + .await?; + + group.sync_with_conn(provider_ref, self).await?; + active_group_count.fetch_add(1, Ordering::SeqCst); + } + + Ok::<(), GroupError>(()) + } + }) + .collect::>(); + + sync_futures + .collect::>>() + .await + .into_iter() + .collect::, _>>()?; + + Ok(active_group_count.load(Ordering::SeqCst)) + } + /** * Validates a credential against the given installation public key * @@ -579,20 +851,56 @@ pub fn deserialize_welcome(welcome_bytes: &Vec) -> Result( + client: &Client, + installation_id: &[u8], + ) -> Vec { + let kps = client + .get_key_packages_for_installation_ids(vec![installation_id.to_vec()]) + .await + .unwrap(); + let kp = kps.first().unwrap(); + + serialize_key_package_hash_ref(&kp.inner, &client.mls_provider().unwrap()).unwrap() + } + + #[tokio::test] + async fn test_key_package_rotation() { + let alix_wallet = generate_local_wallet(); + let bo_wallet = generate_local_wallet(); + let alix = ClientBuilder::new_test_client(&alix_wallet).await; + let bo = ClientBuilder::new_test_client(&bo_wallet).await; + let bo_store = bo.store(); + + let alix_original_init_key = + get_key_package_init_key(&alix, &alix.installation_public_key()).await; + let bo_original_init_key = + get_key_package_init_key(&bo, &bo.installation_public_key()).await; + + // Bo's original key should be deleted + let bo_original_from_db = bo_store + .conn() + .unwrap() + .find_key_package_history_entry_by_hash_ref(bo_original_init_key.clone()); + assert!(bo_original_from_db.is_ok()); + + alix.create_group_with_members( + vec![bo_wallet.get_address()], + None, + GroupMetadataOptions::default(), + ) + .await + .unwrap(); + + bo.sync_welcomes().await.unwrap(); + + let bo_new_key = get_key_package_init_key(&bo, &bo.installation_public_key()).await; + // Bo's key should have changed + assert_ne!(bo_original_init_key, bo_new_key); + + bo.sync_welcomes().await.unwrap(); + let bo_new_key_2 = get_key_package_init_key(&bo, &bo.installation_public_key()).await; + // Bo's key should not have changed syncing the second time. + assert_eq!(bo_new_key, bo_new_key_2); + + alix.sync_welcomes().await.unwrap(); + let alix_key_2 = get_key_package_init_key(&alix, &alix.installation_public_key()).await; + // Alix's key should not have changed at all + assert_eq!(alix_original_init_key, alix_key_2); + + alix.create_group_with_members( + vec![bo_wallet.get_address()], + None, + GroupMetadataOptions::default(), + ) + .await + .unwrap(); + bo.sync_welcomes().await.unwrap(); + + // Bo should have two groups now + let bo_groups = bo.find_groups(None, None, None, None).unwrap(); + assert_eq!(bo_groups.len(), 2); + + // Bo's original key should be deleted + let bo_original_after_delete = bo_store + .conn() + .unwrap() + .find_key_package_history_entry_by_hash_ref(bo_original_init_key); + assert!(bo_original_after_delete.is_err()); + } } diff --git a/xmtp_mls/src/configuration.rs b/xmtp_mls/src/configuration.rs index 70e0a48c2..039299de2 100644 --- a/xmtp_mls/src/configuration.rs +++ b/xmtp_mls/src/configuration.rs @@ -12,18 +12,22 @@ pub const MAX_GROUP_SYNC_RETRIES: usize = 3; pub const MAX_INTENT_PUBLISH_ATTEMPTS: usize = 3; -const NANOSECONDS_IN_HOUR: i64 = 3_600_000_000_000; +const NS_IN_SEC: i64 = 1_000_000_000; -pub const UPDATE_INSTALLATIONS_INTERVAL_NS: i64 = NANOSECONDS_IN_HOUR / 2; // 30 min +const NS_IN_HOUR: i64 = NS_IN_SEC * 60 * 60; + +pub const SYNC_UPDATE_INSTALLATIONS_INTERVAL_NS: i64 = NS_IN_HOUR / 2; // 30 min + +pub const SEND_MESSAGE_UPDATE_INSTALLATIONS_INTERVAL_NS: i64 = 5 * NS_IN_SEC; pub const MAX_GROUP_SIZE: u16 = 400; +pub const MAX_PAST_EPOCHS: usize = 3; + /// the max amount of data that can be sent in one gRPC call /// we leave 5 * 1024 * 1024 as extra buffer room pub const GRPC_DATA_LIMIT: usize = 45 * 1024 * 1024; -pub const DELIMITER: char = '\x01'; - /// MLS Extension Types /// /// Copied from draft-ietf-mls-protocol-16: @@ -44,3 +48,8 @@ pub const GROUP_PERMISSIONS_EXTENSION_ID: u16 = 0xff02; pub const DEFAULT_GROUP_NAME: &str = ""; pub const DEFAULT_GROUP_DESCRIPTION: &str = ""; pub const DEFAULT_GROUP_IMAGE_URL_SQUARE: &str = ""; +pub const DEFAULT_GROUP_PINNED_FRAME_URL: &str = ""; + +// If a metadata field name starts with this character, +// and it does not have a policy set, it is a super admin only field +pub const SUPER_ADMIN_METADATA_PREFIX: &str = "_"; diff --git a/xmtp_mls/src/credential/grant_messaging_access_association.rs b/xmtp_mls/src/credential/grant_messaging_access_association.rs deleted file mode 100644 index 86a6cce00..000000000 --- a/xmtp_mls/src/credential/grant_messaging_access_association.rs +++ /dev/null @@ -1,290 +0,0 @@ -use chrono::DateTime; -use serde::{Deserialize, Serialize}; - -use xmtp_cryptography::signature::{ - ed25519_public_key_to_address, sanitize_evm_addresses, RecoverableSignature, -}; -use xmtp_proto::xmtp::mls::message_contents::{ - GrantMessagingAccessAssociation as GrantMessagingAccessAssociationProto, - RecoverableEcdsaSignature as RecoverableEcdsaSignatureProto, -}; - -use crate::{types::Address, utils::time::NS_IN_SEC, InboxOwner}; - -use super::AssociationError; - -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -pub struct UnsignedGrantMessagingAccessData { - pub(crate) account_address: Address, - pub(crate) installation_public_key: Vec, - pub(crate) created_ns: u64, - iso8601_time: String, -} - -impl UnsignedGrantMessagingAccessData { - pub fn new( - account_address: Address, - installation_public_key: Vec, - created_ns: u64, - ) -> Result { - let account_address = sanitize_evm_addresses(vec![account_address])?[0].clone(); - let created_time = DateTime::from_timestamp( - created_ns as i64 / NS_IN_SEC, - (created_ns as i64 % NS_IN_SEC) as u32, - ) - .ok_or(AssociationError::MalformedAssociation)?; - let iso8601_time = format!("{}", created_time.format("%+")); - - Ok(Self { - account_address, - installation_public_key, - created_ns, - iso8601_time, - }) - } - - pub fn account_address(&self) -> Address { - self.account_address.clone() - } - - pub fn installation_public_key(&self) -> Vec { - self.installation_public_key.clone() - } - - pub fn created_ns(&self) -> u64 { - self.created_ns - } - - fn header_text() -> String { - let label = "Grant Messaging Access".to_string(); - format!("XMTP : {}", label) - } - - fn body_text( - account_address: &Address, - installation_public_key: &[u8], - iso8601_time: &str, - ) -> String { - format!( - "\nCurrent Time: {}\nAccount Address: {}\nInstallation ID: {}", - iso8601_time, - account_address, - ed25519_public_key_to_address(installation_public_key) - ) - } - - fn footer_text() -> String { - "For more info: https://xmtp.org/signatures/".to_string() - } - - pub fn text(&self) -> String { - format!( - "{}\n{}\n\n{}", - Self::header_text(), - Self::body_text( - &self.account_address, - &self.installation_public_key, - &self.iso8601_time - ), - Self::footer_text() - ) - .to_string() - } -} - -/// An Association is link between a blockchain account and an xmtp installation for the purposes of -/// authentication. -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -pub struct GrantMessagingAccessAssociation { - association_data: UnsignedGrantMessagingAccessData, - signature: RecoverableSignature, -} - -impl GrantMessagingAccessAssociation { - pub(crate) fn new_validated( - association_data: UnsignedGrantMessagingAccessData, - signature: RecoverableSignature, - ) -> Result { - let this = Self { - association_data, - signature, - }; - this.is_valid()?; - Ok(this) - } - - pub(crate) fn create( - owner: &impl InboxOwner, - installation_public_key: Vec, - created_ns: u64, - ) -> Result { - let unsigned_data = UnsignedGrantMessagingAccessData::new( - owner.get_address(), - installation_public_key, - created_ns, - )?; - let text = unsigned_data.text(); - let signature = owner.sign(&text)?; - Self::new_validated(unsigned_data, signature) - } - - pub fn from_proto_validated( - proto: GrantMessagingAccessAssociationProto, - expected_installation_public_key: &[u8], - ) -> Result { - let signature = RecoverableSignature::Eip191Signature( - proto - .signature - .ok_or(AssociationError::MalformedAssociation)? - .bytes, - ); - Self::new_validated( - UnsignedGrantMessagingAccessData::new( - proto.account_address, - expected_installation_public_key.to_vec(), - proto.created_ns, - )?, - signature, - ) - } - - fn is_valid(&self) -> Result<(), AssociationError> { - let assumed_addr = self.association_data.account_address(); - - let addr = self - .signature - .recover_address(&self.association_data.text())?; - - let sanitized_addresses = sanitize_evm_addresses(vec![assumed_addr, addr])?; - if sanitized_addresses[0] != sanitized_addresses[1] { - Err(AssociationError::AddressMismatch { - provided_addr: sanitized_addresses[0].clone(), - signing_addr: sanitized_addresses[1].clone(), - }) - } else { - Ok(()) - } - } - - pub fn account_address(&self) -> String { - self.association_data.account_address() - } - - pub fn installation_public_key(&self) -> Vec { - self.association_data.installation_public_key() - } - - pub fn created_ns(&self) -> u64 { - self.association_data.created_ns() - } -} - -impl From for GrantMessagingAccessAssociationProto { - fn from(assoc: GrantMessagingAccessAssociation) -> Self { - let account_address = assoc.account_address(); - let created_ns = assoc.created_ns(); - Self { - account_address, - // Hardcoded version for now - association_text_version: 1, - signature: Some(RecoverableEcdsaSignatureProto { - bytes: assoc.signature.into(), - }), - created_ns, - } - } -} - -#[cfg(test)] -pub mod tests { - use ethers::signers::{LocalWallet, Signer}; - - use xmtp_cryptography::{signature::h160addr_to_string, utils::rng}; - use xmtp_proto::xmtp::mls::message_contents::GrantMessagingAccessAssociation as GrantMessagingAccessAssociationProto; - - use crate::credential::{ - grant_messaging_access_association::GrantMessagingAccessAssociation, - UnsignedGrantMessagingAccessData, - }; - - #[tokio::test] - async fn assoc_gen() { - let key_bytes = vec![22, 33, 44, 55]; - - let wallet = LocalWallet::new(&mut rng()); - let other_wallet = LocalWallet::new(&mut rng()); - let addr = h160addr_to_string(wallet.address()); - let other_addr = h160addr_to_string(other_wallet.address()); - let grant_time = 1609459200000000; - let bad_grant_time = 1609459200000001; - - let data = - UnsignedGrantMessagingAccessData::new(addr.clone(), key_bytes.clone(), grant_time) - .unwrap(); - let sig = wallet.sign_message(data.text()).await.expect("BadSign"); - - let other_data = UnsignedGrantMessagingAccessData::new( - other_addr.clone(), - key_bytes.clone(), - grant_time, - ) - .unwrap(); - let other_sig = wallet - .sign_message(other_data.text()) - .await - .expect("BadSign"); - - let bad_key_bytes = vec![11, 22, 33]; - - assert!(GrantMessagingAccessAssociation::new_validated( - UnsignedGrantMessagingAccessData::new(addr.clone(), key_bytes.clone(), grant_time) - .unwrap(), - sig.into() - ) - .is_ok()); - assert!(GrantMessagingAccessAssociation::new_validated( - UnsignedGrantMessagingAccessData::new(addr.clone(), bad_key_bytes.clone(), grant_time) - .unwrap(), - sig.into() - ) - .is_err()); - assert!(GrantMessagingAccessAssociation::new_validated( - UnsignedGrantMessagingAccessData::new( - other_addr.clone(), - key_bytes.clone(), - grant_time, - ) - .unwrap(), - sig.into() - ) - .is_err()); - assert!(GrantMessagingAccessAssociation::new_validated( - UnsignedGrantMessagingAccessData::new(addr.clone(), key_bytes.clone(), bad_grant_time) - .unwrap(), - sig.into() - ) - .is_err()); - assert!(GrantMessagingAccessAssociation::new_validated( - UnsignedGrantMessagingAccessData::new(addr.clone(), key_bytes.clone(), grant_time) - .unwrap(), - other_sig.into() - ) - .is_err()); - } - - #[tokio::test] - async fn to_proto() { - let key_bytes = vec![22, 33, 44, 55]; - let wallet = LocalWallet::new(&mut rng()); - let addr = h160addr_to_string(wallet.address()); - let created_ns = 1609459200000000; - let data = UnsignedGrantMessagingAccessData::new(addr, key_bytes, created_ns).unwrap(); - let sig = wallet.sign_message(data.text()).await.expect("BadSign"); - - let assoc = GrantMessagingAccessAssociation::new_validated(data, sig.into()).unwrap(); - let proto_signature: GrantMessagingAccessAssociationProto = assoc.into(); - - assert_eq!(proto_signature.association_text_version, 1); - assert_eq!(proto_signature.signature.unwrap().bytes, sig.to_vec()); - } -} diff --git a/xmtp_mls/src/credential/legacy_create_identity_association.rs b/xmtp_mls/src/credential/legacy_create_identity_association.rs deleted file mode 100644 index c974064e0..000000000 --- a/xmtp_mls/src/credential/legacy_create_identity_association.rs +++ /dev/null @@ -1,211 +0,0 @@ -use prost::Message; - -use xmtp_id::associations::signature::ValidatedLegacySignedPublicKey; -use xmtp_proto::xmtp::{ - message_contents::{signed_private_key, SignedPrivateKey as LegacySignedPrivateKeyProto}, - mls::message_contents::{ - LegacyCreateIdentityAssociation as LegacyCreateIdentityAssociationProto, - RecoverableEcdsaSignature as RecoverableEcdsaSignatureProto, - }, -}; -use xmtp_v2::k256_helper; - -use super::AssociationError; - -/// An Association is link between a blockchain account and an xmtp installation for the purposes of -/// authentication. -pub struct LegacyCreateIdentityAssociation { - installation_public_key: Vec, - delegating_signature: Vec, - legacy_signed_public_key: ValidatedLegacySignedPublicKey, -} - -impl LegacyCreateIdentityAssociation { - fn new_validated( - installation_public_key: Vec, - delegating_signature: Vec, - legacy_signed_public_key: ValidatedLegacySignedPublicKey, - ) -> Result { - let this = Self { - installation_public_key, - delegating_signature, - legacy_signed_public_key, - }; - this.is_valid()?; - Ok(this) - } - - pub(crate) fn create( - legacy_signed_private_key: Vec, - installation_public_key: Vec, - ) -> Result { - let legacy_signed_private_key_proto = - LegacySignedPrivateKeyProto::decode(legacy_signed_private_key.as_slice())?; - let signed_private_key::Union::Secp256k1(secp256k1) = legacy_signed_private_key_proto - .union - .ok_or(AssociationError::MalformedLegacyKey( - "Missing secp256k1.union field".to_string(), - ))?; - let legacy_private_key = secp256k1.bytes; - let (mut delegating_signature, recovery_id) = k256_helper::sign_sha256( - &legacy_private_key, // secret_key - &installation_public_key, // message - ) - .map_err(AssociationError::LegacySignature)?; - delegating_signature.push(recovery_id); // TODO: normalize recovery ID if necessary - - let legacy_signed_public_key_proto = legacy_signed_private_key_proto.public_key.ok_or( - AssociationError::MalformedLegacyKey("Missing public_key field".to_string()), - )?; - Self::new_validated( - installation_public_key, - delegating_signature, - legacy_signed_public_key_proto.try_into()?, // ValidatedLegacySignedPublicKey - ) - } - - pub fn from_proto_validated( - proto: LegacyCreateIdentityAssociationProto, - expected_installation_public_key: &[u8], - ) -> Result { - let delegating_signature = proto - .signature - .ok_or(AssociationError::MalformedAssociation)? - .bytes; - let legacy_signed_public_key_proto = proto - .signed_legacy_create_identity_key - .ok_or(AssociationError::MalformedAssociation)?; - - Self::new_validated( - expected_installation_public_key.to_vec(), - delegating_signature, - legacy_signed_public_key_proto.try_into()?, // ValidatedLegacySignedPublicKey - ) - } - - fn is_valid(&self) -> Result<(), AssociationError> { - // Validate legacy key signs installation key - if self.delegating_signature.len() != 65 { - return Err(AssociationError::MalformedAssociation); - } - assert!(k256_helper::verify_sha256( - &self.legacy_signed_public_key.key_bytes(), // signed_by - &self.installation_public_key, // message - &self.delegating_signature[0..64], // signature - self.delegating_signature[64], // recovery_id - ) - .map_err(AssociationError::LegacySignature)?); // always returns true if no error - - // Wallet signature of legacy key is internally validated by ValidatedLegacySignedPublicKey on creation - Ok(()) - } - - pub fn account_address(&self) -> String { - self.legacy_signed_public_key.account_address() - } - - pub fn installation_public_key(&self) -> Vec { - self.installation_public_key.clone() - } - - pub fn created_ns(&self) -> u64 { - self.legacy_signed_public_key.created_ns() - } -} - -impl From for LegacyCreateIdentityAssociationProto { - fn from(assoc: LegacyCreateIdentityAssociation) -> Self { - Self { - signature: Some(RecoverableEcdsaSignatureProto { - bytes: assoc.delegating_signature.clone(), - }), - signed_legacy_create_identity_key: Some(assoc.legacy_signed_public_key.into()), - } - } -} - -#[cfg(test)] -pub mod tests { - use openmls_basic_credential::SignatureKeyPair; - use xmtp_proto::xmtp::mls::message_contents::LegacyCreateIdentityAssociation as LegacyCreateIdentityAssociationProto; - - use crate::{ - assert_err, - configuration::CIPHERSUITE, - credential::{ - legacy_create_identity_association::LegacyCreateIdentityAssociation, AssociationError, - }, - }; - - #[tokio::test] - async fn validate_serialization_round_trip() { - let legacy_address = "0x419cb1fa5635b0c6df47c9dc5765c8f1f4dff78e"; - let legacy_signed_private_key_proto = vec![ - 8, 128, 154, 196, 133, 220, 244, 197, 216, 23, 18, 34, 10, 32, 214, 70, 104, 202, 68, - 204, 25, 202, 197, 141, 239, 159, 145, 249, 55, 242, 147, 126, 3, 124, 159, 207, 96, - 135, 134, 122, 60, 90, 82, 171, 131, 162, 26, 153, 1, 10, 79, 8, 128, 154, 196, 133, - 220, 244, 197, 216, 23, 26, 67, 10, 65, 4, 232, 32, 50, 73, 113, 99, 115, 168, 104, - 229, 206, 24, 217, 132, 223, 217, 91, 63, 137, 136, 50, 89, 82, 186, 179, 150, 7, 127, - 140, 10, 165, 117, 233, 117, 196, 134, 227, 143, 125, 210, 187, 77, 195, 169, 162, 116, - 34, 20, 196, 145, 40, 164, 246, 139, 197, 154, 233, 190, 148, 35, 131, 240, 106, 103, - 18, 70, 18, 68, 10, 64, 90, 24, 36, 99, 130, 246, 134, 57, 60, 34, 142, 165, 221, 123, - 63, 27, 138, 242, 195, 175, 212, 146, 181, 152, 89, 48, 8, 70, 104, 94, 163, 0, 25, - 196, 228, 190, 49, 108, 141, 60, 174, 150, 177, 115, 229, 138, 92, 105, 170, 226, 204, - 249, 206, 12, 37, 145, 3, 35, 226, 15, 49, 20, 102, 60, 16, 1, - ]; - let installation_keys = SignatureKeyPair::new(CIPHERSUITE.signature_algorithm()).unwrap(); - - let assoc = LegacyCreateIdentityAssociation::create( - legacy_signed_private_key_proto, - installation_keys.to_public_vec(), - ) - .unwrap(); - - let proto: LegacyCreateIdentityAssociationProto = assoc.into(); - let assoc = LegacyCreateIdentityAssociation::from_proto_validated( - proto, - &installation_keys.to_public_vec(), - ) - .unwrap(); - assert_eq!(assoc.account_address(), legacy_address); - assert_eq!( - assoc.installation_public_key(), - installation_keys.to_public_vec() - ); - } - - #[tokio::test] - async fn validate_bad_signature() { - // let legacy_address = "0x419Cb1fA5635b0c6Df47c9DC5765c8f1f4DfF78e"; - let legacy_signed_private_key_proto = vec![ - 8, 128, 154, 196, 133, 220, 244, 197, 216, 23, 18, 34, 10, 32, 214, 70, 104, 202, 68, - 204, 25, 202, 197, 141, 239, 159, 145, 249, 55, 242, 147, 126, 3, 124, 159, 207, 96, - 135, 134, 122, 60, 90, 82, 171, 131, 162, 26, 153, 1, 10, 79, 8, 128, 154, 196, 133, - 220, 244, 197, 216, 23, 26, 67, 10, 65, 4, 232, 32, 50, 73, 113, 99, 115, 168, 104, - 229, 206, 24, 217, 132, 223, 217, 91, 63, 137, 136, 50, 89, 82, 186, 179, 150, 7, 127, - 140, 10, 165, 117, 233, 117, 196, 134, 227, 143, 125, 210, 187, 77, 195, 169, 162, 116, - 34, 20, 196, 145, 40, 164, 246, 139, 197, 154, 233, 190, 148, 35, 131, 240, 106, 103, - 18, 70, 18, 68, 10, 64, 90, 24, 36, 99, 130, 246, 134, 57, 60, 34, 142, 165, 221, 123, - 63, 27, 138, 242, 195, 175, 212, 146, 181, 152, 89, 48, 8, 70, 104, 94, 163, 0, 25, - 196, 228, 190, 49, 108, 141, 60, 174, 150, 177, 115, 229, 138, 92, 105, 170, 226, 204, - 249, 206, 12, 37, 145, 3, 35, 226, 15, 49, 20, 102, 60, 16, 1, - ]; - let installation_keys = SignatureKeyPair::new(CIPHERSUITE.signature_algorithm()).unwrap(); - - let mut assoc = LegacyCreateIdentityAssociation::create( - legacy_signed_private_key_proto, - installation_keys.to_public_vec(), - ) - .unwrap(); - assoc.delegating_signature[0] ^= 1; - - let proto: LegacyCreateIdentityAssociationProto = assoc.into(); - assert_err!( - LegacyCreateIdentityAssociation::from_proto_validated( - proto, - &installation_keys.to_public_vec(), - ), - AssociationError::LegacySignature(_) - ); - } -} diff --git a/xmtp_mls/src/credential/mod.rs b/xmtp_mls/src/credential/mod.rs deleted file mode 100644 index 44e137ae3..000000000 --- a/xmtp_mls/src/credential/mod.rs +++ /dev/null @@ -1,185 +0,0 @@ -mod grant_messaging_access_association; -mod legacy_create_identity_association; - -use openmls_basic_credential::SignatureKeyPair; -use prost::{DecodeError, Message}; -use thiserror::Error; - -use xmtp_cryptography::signature::AddressValidationError; -use xmtp_cryptography::signature::{RecoverableSignature, SignatureError}; -use xmtp_proto::xmtp::mls::message_contents::{ - mls_credential::Association as AssociationProto, MlsCredential as MlsCredentialProto, -}; - -use crate::{types::Address, utils::time::now_ns, InboxOwner}; - -pub use self::grant_messaging_access_association::GrantMessagingAccessAssociation; -pub use self::grant_messaging_access_association::UnsignedGrantMessagingAccessData; -pub use self::legacy_create_identity_association::LegacyCreateIdentityAssociation; - -#[derive(Debug, Error)] -pub enum AssociationError { - #[error("bad signature")] - BadSignature(#[from] SignatureError), - #[error("decode error: {0}")] - DecodeError(#[from] DecodeError), - #[error("legacy key: {0}")] - MalformedLegacyKey(String), - #[error("legacy signature: {0}")] - LegacySignature(String), - #[error("Association text mismatch")] - TextMismatch, - #[error("Installation public key mismatch")] - InstallationPublicKeyMismatch, - #[error( - "Address mismatch in Association: Provided:{provided_addr:?} != signed:{signing_addr:?}" - )] - AddressMismatch { - provided_addr: Address, - signing_addr: Address, - }, - #[error(transparent)] - AddressValidationError(#[from] AddressValidationError), - #[error("Malformed association")] - MalformedAssociation, - - #[error(transparent)] - // TODO: remove this AssociationError and use [xmtp_id::associations::AssociationError] - IDAssociationError(#[from] xmtp_id::associations::AssociationError), - #[error(transparent)] - SignatureError(#[from] xmtp_id::associations::SignatureError), -} - -pub enum Credential { - GrantMessagingAccess(GrantMessagingAccessAssociation), - LegacyCreateIdentity(LegacyCreateIdentityAssociation), -} - -impl Credential { - pub fn create( - installation_keys: &SignatureKeyPair, - owner: &impl InboxOwner, - ) -> Result { - let created_ns = now_ns() as u64; - let association = GrantMessagingAccessAssociation::create( - owner, - installation_keys.to_public_vec(), - created_ns, - )?; - Ok(Self::GrantMessagingAccess(association)) - } - - pub fn create_from_external_signer( - association_data: UnsignedGrantMessagingAccessData, - signature: Vec, - ) -> Result { - let association = GrantMessagingAccessAssociation::new_validated( - association_data, - RecoverableSignature::Eip191Signature(signature), - )?; - Ok(Self::GrantMessagingAccess(association)) - } - - pub fn create_from_legacy( - installation_keys: &SignatureKeyPair, - legacy_signed_private_key: Vec, - ) -> Result { - let association = LegacyCreateIdentityAssociation::create( - legacy_signed_private_key, - installation_keys.to_public_vec(), - )?; - Ok(Self::LegacyCreateIdentity(association)) - } - - pub fn from_proto_validated( - proto: MlsCredentialProto, - expected_account_address: Option<&str>, // Must validate when fetching identity updates - expected_installation_public_key: Option<&[u8]>, // Must cross-reference against leaf node when relevant - ) -> Result { - let credential = match proto - .association - .ok_or(AssociationError::MalformedAssociation)? - { - AssociationProto::MessagingAccess(assoc) => { - GrantMessagingAccessAssociation::from_proto_validated( - assoc, - &proto.installation_public_key, - ) - .map(Credential::GrantMessagingAccess) - } - AssociationProto::LegacyCreateIdentity(assoc) => { - LegacyCreateIdentityAssociation::from_proto_validated( - assoc, - &proto.installation_public_key, - ) - .map(Credential::LegacyCreateIdentity) - } - }?; - - if let Some(address) = expected_account_address { - if credential.address() != address { - return Err(AssociationError::AddressMismatch { - provided_addr: address.to_string(), - signing_addr: credential.address(), - }); - } - } - if let Some(public_key) = expected_installation_public_key { - if credential.installation_public_key() != public_key { - return Err(AssociationError::InstallationPublicKeyMismatch); - } - } - Ok(credential) - } - - pub fn address(&self) -> String { - match &self { - Credential::GrantMessagingAccess(assoc) => assoc.account_address(), - Credential::LegacyCreateIdentity(assoc) => assoc.account_address(), - } - } - - pub fn installation_public_key(&self) -> Vec { - match &self { - Credential::GrantMessagingAccess(assoc) => assoc.installation_public_key(), - Credential::LegacyCreateIdentity(assoc) => assoc.installation_public_key(), - } - } - - pub fn created_ns(&self) -> u64 { - match &self { - Credential::GrantMessagingAccess(assoc) => assoc.created_ns(), - Credential::LegacyCreateIdentity(assoc) => assoc.created_ns(), - } - } -} - -impl From for MlsCredentialProto { - fn from(credential: Credential) -> Self { - Self { - installation_public_key: credential.installation_public_key(), - association: match credential { - Credential::GrantMessagingAccess(assoc) => { - Some(AssociationProto::MessagingAccess(assoc.into())) - } - Credential::LegacyCreateIdentity(assoc) => { - Some(AssociationProto::LegacyCreateIdentity(assoc.into())) - } - }, - } - } -} - -pub fn get_validated_account_address( - credential: &[u8], - installation_public_key: &[u8], -) -> Result { - let proto = MlsCredentialProto::decode(credential)?; - let credential = Credential::from_proto_validated( - proto, - None, // expected_account_address - Some(installation_public_key), - )?; - - Ok(credential.address()) -} diff --git a/xmtp_mls/src/groups/group_membership.rs b/xmtp_mls/src/groups/group_membership.rs index e14a5ea23..a614a7ea2 100644 --- a/xmtp_mls/src/groups/group_membership.rs +++ b/xmtp_mls/src/groups/group_membership.rs @@ -105,6 +105,7 @@ impl From<&GroupMembership> for Vec { } } +#[derive(Debug)] pub struct MembershipDiff<'inbox_id> { pub added_inboxes: Vec<&'inbox_id String>, pub removed_inboxes: Vec<&'inbox_id String>, diff --git a/xmtp_mls/src/groups/group_metadata.rs b/xmtp_mls/src/groups/group_metadata.rs index e8db126a5..608294e14 100644 --- a/xmtp_mls/src/groups/group_metadata.rs +++ b/xmtp_mls/src/groups/group_metadata.rs @@ -46,6 +46,7 @@ impl GroupMetadata { conversation_type: conversation_type as i32, creator_inbox_id: self.creator_inbox_id.clone(), creator_account_address: "".to_string(), // TODO: remove from proto + dm_members: None, }) } } diff --git a/xmtp_mls/src/groups/group_mutable_metadata.rs b/xmtp_mls/src/groups/group_mutable_metadata.rs index 48ea93843..6b1eaf2fe 100644 --- a/xmtp_mls/src/groups/group_mutable_metadata.rs +++ b/xmtp_mls/src/groups/group_mutable_metadata.rs @@ -13,7 +13,7 @@ use xmtp_proto::xmtp::mls::message_contents::{ use crate::configuration::{ DEFAULT_GROUP_DESCRIPTION, DEFAULT_GROUP_IMAGE_URL_SQUARE, DEFAULT_GROUP_NAME, - MUTABLE_METADATA_EXTENSION_ID, + DEFAULT_GROUP_PINNED_FRAME_URL, MUTABLE_METADATA_EXTENSION_ID, }; use super::GroupMetadataOptions; @@ -42,14 +42,16 @@ pub enum MetadataField { GroupName, Description, GroupImageUrlSquare, + GroupPinnedFrameUrl, } impl MetadataField { - fn as_str(self) -> &'static str { + pub const fn as_str(&self) -> &'static str { match self { MetadataField::GroupName => "group_name", MetadataField::Description => "description", MetadataField::GroupImageUrlSquare => "group_image_url_square", + MetadataField::GroupPinnedFrameUrl => "group_pinned_frame_url", } } } @@ -89,14 +91,20 @@ impl GroupMutableMetadata { ); attributes.insert( MetadataField::Description.to_string(), - DEFAULT_GROUP_DESCRIPTION.to_string(), + opts.description + .unwrap_or_else(|| DEFAULT_GROUP_DESCRIPTION.to_string()), ); attributes.insert( MetadataField::GroupImageUrlSquare.to_string(), opts.image_url_square .unwrap_or_else(|| DEFAULT_GROUP_IMAGE_URL_SQUARE.to_string()), ); - let admin_list = vec![creator_inbox_id.clone()]; + attributes.insert( + MetadataField::GroupPinnedFrameUrl.to_string(), + opts.pinned_frame_url + .unwrap_or_else(|| DEFAULT_GROUP_PINNED_FRAME_URL.to_string()), + ); + let admin_list = vec![]; let super_admin_list = vec![creator_inbox_id.clone()]; Self { attributes, @@ -111,6 +119,7 @@ impl GroupMutableMetadata { MetadataField::GroupName, MetadataField::Description, MetadataField::GroupImageUrlSquare, + MetadataField::GroupPinnedFrameUrl, ] } diff --git a/xmtp_mls/src/groups/group_permissions.rs b/xmtp_mls/src/groups/group_permissions.rs index 1a03a758b..d0e4ee304 100644 --- a/xmtp_mls/src/groups/group_permissions.rs +++ b/xmtp_mls/src/groups/group_permissions.rs @@ -25,7 +25,7 @@ use xmtp_proto::xmtp::mls::message_contents::{ PermissionsUpdatePolicy as PermissionsPolicyProto, PolicySet as PolicySetProto, }; -use crate::configuration::GROUP_PERMISSIONS_EXTENSION_ID; +use crate::configuration::{GROUP_PERMISSIONS_EXTENSION_ID, SUPER_ADMIN_METADATA_PREFIX}; use super::{ group_mutable_metadata::GroupMutableMetadata, @@ -46,6 +46,8 @@ pub enum GroupMutablePermissionsError { MissingPolicies, #[error("missing extension")] MissingExtension, + #[error("invalid permission policy option")] + InvalidPermissionPolicyOption, } #[derive(Debug, Clone, PartialEq)] @@ -70,7 +72,7 @@ impl GroupMutablePermissions { if proto.policies.is_none() { return Err(GroupMutablePermissionsError::MissingPolicies); } - let policies = proto.policies.unwrap(); + let policies = proto.policies.expect("checked for none"); Ok(Self::new(PolicySet::from_proto(policies)?)) } @@ -128,6 +130,15 @@ impl TryFrom<&Extensions> for GroupMutablePermissions { } } +impl TryFrom<&OpenMlsGroup> for GroupMutablePermissions { + type Error = GroupMutablePermissionsError; + + fn try_from(value: &OpenMlsGroup) -> Result { + let extensions = value.export_group_context().extensions(); + extensions.try_into() + } +} + pub fn extract_group_permissions( group: &OpenMlsGroup, ) -> Result { @@ -233,11 +244,11 @@ impl TryFrom for MetadataPolicies { 2 => Ok(MetadataPolicies::deny()), 3 => Ok(MetadataPolicies::allow_if_actor_admin()), 4 => Ok(MetadataPolicies::allow_if_actor_super_admin()), - _ => Err(PolicyError::InvalidPolicy), + _ => Err(PolicyError::InvalidMetadataPolicy), }, Some(MetadataPolicyKindProto::AndCondition(inner)) => { if inner.policies.is_empty() { - return Err(PolicyError::InvalidPolicy); + return Err(PolicyError::InvalidMetadataPolicy); } let policies = inner .policies @@ -249,7 +260,7 @@ impl TryFrom for MetadataPolicies { } Some(MetadataPolicyKindProto::AnyCondition(inner)) => { if inner.policies.is_empty() { - return Err(PolicyError::InvalidPolicy); + return Err(PolicyError::InvalidMetadataPolicy); } let policies = inner @@ -260,7 +271,7 @@ impl TryFrom for MetadataPolicies { Ok(MetadataPolicies::any(policies)) } - None => Err(PolicyError::InvalidPolicy), + None => Err(PolicyError::InvalidMetadataPolicy), } } } @@ -436,11 +447,11 @@ impl TryFrom for PermissionsPolicies { 1 => Ok(PermissionsPolicies::deny()), 2 => Ok(PermissionsPolicies::allow_if_actor_admin()), 3 => Ok(PermissionsPolicies::allow_if_actor_super_admin()), - _ => Err(PolicyError::InvalidPolicy), + _ => Err(PolicyError::InvalidPermissionsPolicy), }, Some(PermissionsPolicyKindProto::AndCondition(inner)) => { if inner.policies.is_empty() { - return Err(PolicyError::InvalidPolicy); + return Err(PolicyError::InvalidPermissionsPolicy); } let policies = inner .policies @@ -452,7 +463,7 @@ impl TryFrom for PermissionsPolicies { } Some(PermissionsPolicyKindProto::AnyCondition(inner)) => { if inner.policies.is_empty() { - return Err(PolicyError::InvalidPolicy); + return Err(PolicyError::InvalidPermissionsPolicy); } let policies = inner @@ -463,7 +474,7 @@ impl TryFrom for PermissionsPolicies { Ok(PermissionsPolicies::any(policies)) } - None => Err(PolicyError::InvalidPolicy), + None => Err(PolicyError::InvalidPermissionsPolicy), } } } @@ -563,8 +574,28 @@ pub enum PolicyError { Serialization(#[from] prost::EncodeError), #[error("deserialization {0}")] Deserialization(#[from] prost::DecodeError), + #[error("Missing metadata policy field: {name}")] + MissingMetadataPolicyField { name: String }, #[error("invalid policy")] InvalidPolicy, + #[error("unexpected preset policy")] + InvalidPresetPolicy, + #[error("invalid metadata policy")] + InvalidMetadataPolicy, + #[error("invalid membership policy")] + InvalidMembershipPolicy, + #[error("invalid permissions policy")] + InvalidPermissionsPolicy, + #[error("from proto add member invalid policy")] + FromProtoAddMemberInvalidPolicy, + #[error("from proto remove member invalid policy")] + FromProtoRemoveMemberInvalidPolicy, + #[error("from proto add admin invalid policy")] + FromProtoAddAdminInvalidPolicy, + #[error("from proto remove admin invalid policy")] + FromProtoRemoveAdminInvalidPolicy, + #[error("from proto update permissions invalid policy")] + FromProtoUpdatePermissionsInvalidPolicy, } #[derive(Clone, Copy, Debug, PartialEq)] @@ -658,11 +689,11 @@ impl TryFrom for MembershipPolicies { 2 => Ok(MembershipPolicies::deny()), 3 => Ok(MembershipPolicies::allow_if_actor_admin()), 4 => Ok(MembershipPolicies::allow_if_actor_super_admin()), - _ => Err(PolicyError::InvalidPolicy), + _ => Err(PolicyError::InvalidMembershipPolicy), }, Some(PolicyKindProto::AndCondition(inner)) => { if inner.policies.is_empty() { - return Err(PolicyError::InvalidPolicy); + return Err(PolicyError::InvalidMembershipPolicy); } let policies = inner .policies @@ -674,7 +705,7 @@ impl TryFrom for MembershipPolicies { } Some(PolicyKindProto::AnyCondition(inner)) => { if inner.policies.is_empty() { - return Err(PolicyError::InvalidPolicy); + return Err(PolicyError::InvalidMembershipPolicy); } let policies = inner @@ -685,7 +716,7 @@ impl TryFrom for MembershipPolicies { Ok(MembershipPolicies::any(policies)) } - None => Err(PolicyError::InvalidPolicy), + None => Err(PolicyError::InvalidMembershipPolicy), } } } @@ -846,8 +877,8 @@ impl PolicySet { let super_admin_remove_valid = commit.metadata_changes.super_admins_removed.is_empty() || (commit.actor.is_super_admin && commit.metadata_changes.num_super_admins > 0); - // TODO Validate permissions updates are valid - // once we add the user actions for updating permissions + // Permissions can only be changed by the super admin + let permissions_changes_valid = !commit.permissions_changed || commit.actor.is_super_admin; added_inboxes_valid && removed_inboxes_valid @@ -856,6 +887,7 @@ impl PolicySet { && removed_admins_valid && super_admin_add_valid && super_admin_remove_valid + && permissions_changes_valid } fn evaluate_policy<'a, I, P>( @@ -871,7 +903,7 @@ impl PolicySet { changes.all(|change| { let is_ok = policy.evaluate(actor, change); if !is_ok { - log::info!( + tracing::info!( "Policy {:?} failed for actor {:?} and change {:?}", policy, actor, @@ -893,9 +925,8 @@ impl PolicySet { { changes.all(|change| { if let Some(policy) = policies.get(&change.field_name) { - let is_ok = policy.evaluate(actor, change); - if !is_ok { - log::info!( + if !policy.evaluate(actor, change) { + tracing::info!( "Policy for field {} failed for actor {:?} and change {:?}", change.field_name, actor, @@ -903,13 +934,25 @@ impl PolicySet { ); return false; } - return is_ok; + return true; + } + // Policy is not found for metadata change, let's check if the new field contains the super_admin prefix + // and evaluate accordingly + let policy_for_unrecognized_field = + if change.field_name.starts_with(SUPER_ADMIN_METADATA_PREFIX) { + MetadataPolicies::allow_if_actor_super_admin() + } else { + // Otherwise we default to admin only for fields with missing policies + MetadataPolicies::allow_if_actor_admin() + }; + if !policy_for_unrecognized_field.evaluate(actor, change) { + tracing::info!( + "Metadata field update with unknown policy was denied: {}", + change.field_name + ); + return false; } - log::info!( - "Missing policy for changed metadata field: {}", - change.field_name - ); - false + true }) } @@ -937,25 +980,29 @@ impl PolicySet { pub(crate) fn from_proto(proto: PolicySetProto) -> Result { let add_member_policy = MembershipPolicies::try_from( - proto.add_member_policy.ok_or(PolicyError::InvalidPolicy)?, + proto + .add_member_policy + .ok_or(PolicyError::FromProtoAddMemberInvalidPolicy)?, )?; let remove_member_policy = MembershipPolicies::try_from( proto .remove_member_policy - .ok_or(PolicyError::InvalidPolicy)?, + .ok_or(PolicyError::FromProtoRemoveMemberInvalidPolicy)?, )?; let add_admin_policy = PermissionsPolicies::try_from( - proto.add_admin_policy.ok_or(PolicyError::InvalidPolicy)?, + proto + .add_admin_policy + .ok_or(PolicyError::FromProtoAddAdminInvalidPolicy)?, )?; let remove_admin_policy = PermissionsPolicies::try_from( proto .remove_admin_policy - .ok_or(PolicyError::InvalidPolicy)?, + .ok_or(PolicyError::FromProtoRemoveAdminInvalidPolicy)?, )?; let update_permissions_policy = PermissionsPolicies::try_from( proto .update_permissions_policy - .ok_or(PolicyError::InvalidPolicy)?, + .ok_or(PolicyError::FromProtoUpdatePermissionsInvalidPolicy)?, )?; let mut update_metadata_policy = HashMap::new(); @@ -986,6 +1033,52 @@ impl PolicySet { } } +// Depending on if the client is on a newer or older version of libxmtp +// since the group was created, the number of metadata policies might not match +// the default All Members Policy Set. As long as all metadata policies are allow, we will +// match against All Members Preconfigured Policy +pub fn is_policy_all_members(policy: &PolicySet) -> Result { + let mut metadata_policies_equal = true; + for field_name in policy.update_metadata_policy.keys() { + let metadata_policy = policy.update_metadata_policy.get(field_name).ok_or( + PolicyError::MissingMetadataPolicyField { + name: field_name.to_string(), + }, + )?; + metadata_policies_equal = + metadata_policies_equal && metadata_policy.eq(&MetadataPolicies::allow()); + } + Ok(metadata_policies_equal + && policy.add_member_policy == MembershipPolicies::allow() + && policy.remove_member_policy == MembershipPolicies::allow_if_actor_admin() + && policy.add_admin_policy == PermissionsPolicies::allow_if_actor_super_admin() + && policy.remove_admin_policy == PermissionsPolicies::allow_if_actor_super_admin() + && policy.update_permissions_policy == PermissionsPolicies::allow_if_actor_super_admin()) +} + +// Depending on if the client is on a newer or older version of libxmtp +// since the group was created, the number of metadata policies might not match +// the default Admin Only Policy Set. As long as all metadata policies are admin only, we will +// match against Admin Only Preconfigured Policy +pub fn is_policy_admin_only(policy: &PolicySet) -> Result { + let mut metadata_policies_equal = true; + for field_name in policy.update_metadata_policy.keys() { + let metadata_policy = policy.update_metadata_policy.get(field_name).ok_or( + PolicyError::MissingMetadataPolicyField { + name: field_name.to_string(), + }, + )?; + metadata_policies_equal = metadata_policies_equal + && metadata_policy.eq(&MetadataPolicies::allow_if_actor_admin()); + } + Ok(metadata_policies_equal + && policy.add_member_policy == MembershipPolicies::allow_if_actor_admin() + && policy.remove_member_policy == MembershipPolicies::allow_if_actor_admin() + && policy.add_admin_policy == PermissionsPolicies::allow_if_actor_super_admin() + && policy.remove_admin_policy == PermissionsPolicies::allow_if_actor_super_admin() + && policy.update_permissions_policy == PermissionsPolicies::allow_if_actor_super_admin()) +} + /// A policy where any member can add or remove any other member pub(crate) fn policy_all_members() -> PolicySet { let mut metadata_policies_map: HashMap = HashMap::new(); @@ -994,7 +1087,7 @@ pub(crate) fn policy_all_members() -> PolicySet { } PolicySet::new( MembershipPolicies::allow(), - MembershipPolicies::allow(), + MembershipPolicies::allow_if_actor_admin(), metadata_policies_map, PermissionsPolicies::allow_if_actor_super_admin(), PermissionsPolicies::allow_if_actor_super_admin(), @@ -1017,6 +1110,11 @@ pub(crate) fn policy_admin_only() -> PolicySet { PermissionsPolicies::allow_if_actor_super_admin(), ) } +impl Default for PolicySet { + fn default() -> Self { + PreconfiguredPolicies::default().to_policy_set() + } +} #[derive(Debug, Clone, PartialEq, Default)] pub enum PreconfiguredPolicies { @@ -1034,12 +1132,12 @@ impl PreconfiguredPolicies { } pub fn from_policy_set(policy_set: &PolicySet) -> Result { - if policy_set.eq(&policy_all_members()) { + if is_policy_all_members(policy_set)? { Ok(PreconfiguredPolicies::AllMembers) - } else if policy_set.eq(&policy_admin_only()) { + } else if is_policy_admin_only(policy_set)? { Ok(PreconfiguredPolicies::AdminsOnly) } else { - Err(PolicyError::InvalidPolicy) + Err(PolicyError::InvalidPresetPolicy) } } } @@ -1088,14 +1186,16 @@ mod tests { member_added: Option, member_removed: Option, metadata_fields_changed: Option>, + permissions_changed: bool, + actor_is_admin: bool, actor_is_super_admin: bool, ) -> ValidatedCommit { - let actor = build_actor(None, None, actor_is_super_admin, actor_is_super_admin); + let actor = build_actor(None, None, actor_is_admin, actor_is_super_admin); let build_membership_change = |same_address_as_actor| { if same_address_as_actor { vec![build_change( Some(actor.inbox_id.clone()), - actor_is_super_admin, + actor_is_admin, actor_is_super_admin, )] } else { @@ -1121,6 +1221,7 @@ mod tests { metadata_field_changes: field_changes, ..Default::default() }, + permissions_changed, } } @@ -1135,7 +1236,7 @@ mod tests { PermissionsPolicies::allow_if_actor_super_admin(), ); - let commit = build_validated_commit(Some(true), Some(true), None, false); + let commit = build_validated_commit(Some(true), Some(true), None, false, false, false); assert!(permissions.evaluate_commit(&commit)); } @@ -1150,10 +1251,12 @@ mod tests { PermissionsPolicies::allow_if_actor_super_admin(), ); - let member_added_commit = build_validated_commit(Some(false), None, None, false); + let member_added_commit = + build_validated_commit(Some(false), None, None, false, false, false); assert!(!permissions.evaluate_commit(&member_added_commit)); - let member_removed_commit = build_validated_commit(None, Some(false), None, false); + let member_removed_commit = + build_validated_commit(None, Some(false), None, false, false, false); assert!(!permissions.evaluate_commit(&member_removed_commit)); } @@ -1169,13 +1272,16 @@ mod tests { ); // Can not remove the creator if they are the only super admin - let commit_with_creator = build_validated_commit(Some(true), Some(true), None, true); + let commit_with_creator = + build_validated_commit(Some(true), Some(true), None, false, false, true); assert!(!permissions.evaluate_commit(&commit_with_creator)); - let commit_with_creator = build_validated_commit(Some(true), Some(false), None, true); + let commit_with_creator = + build_validated_commit(Some(true), Some(false), None, false, false, true); assert!(permissions.evaluate_commit(&commit_with_creator)); - let commit_without_creator = build_validated_commit(Some(true), Some(true), None, false); + let commit_without_creator = + build_validated_commit(Some(true), Some(true), None, false, false, false); assert!(!permissions.evaluate_commit(&commit_without_creator)); } @@ -1190,10 +1296,12 @@ mod tests { PermissionsPolicies::allow_if_actor_super_admin(), ); - let commit_with_same_member = build_validated_commit(Some(true), None, None, false); + let commit_with_same_member = + build_validated_commit(Some(true), None, None, false, false, false); assert!(permissions.evaluate_commit(&commit_with_same_member)); - let commit_with_different_member = build_validated_commit(Some(false), None, None, false); + let commit_with_different_member = + build_validated_commit(Some(false), None, None, false, false, false); assert!(!permissions.evaluate_commit(&commit_with_different_member)); } @@ -1211,7 +1319,8 @@ mod tests { PermissionsPolicies::allow_if_actor_super_admin(), ); - let member_added_commit = build_validated_commit(Some(true), None, None, false); + let member_added_commit = + build_validated_commit(Some(true), None, None, false, false, false); assert!(!permissions.evaluate_commit(&member_added_commit)); } @@ -1229,7 +1338,8 @@ mod tests { PermissionsPolicies::allow_if_actor_super_admin(), ); - let member_added_commit = build_validated_commit(Some(true), None, None, false); + let member_added_commit = + build_validated_commit(Some(true), None, None, false, false, false); assert!(permissions.evaluate_commit(&member_added_commit)); } @@ -1276,6 +1386,8 @@ mod tests { None, Some(vec![MetadataField::GroupName.to_string()]), false, + false, + false, ); assert!(allow_permissions.evaluate_commit(&member_added_commit)); @@ -1326,4 +1438,129 @@ mod tests { PreconfiguredPolicies::AdminsOnly ); } + + #[test] + fn test_preconfigured_policy_equality_new_metadata() { + let mut metadata_policies_map = MetadataPolicies::default_map(MetadataPolicies::allow()); + metadata_policies_map.insert("new_metadata_field".to_string(), MetadataPolicies::allow()); + let policy_set_new_metadata_permission = PolicySet { + add_member_policy: MembershipPolicies::allow(), + remove_member_policy: MembershipPolicies::allow_if_actor_admin(), + update_metadata_policy: metadata_policies_map, + add_admin_policy: PermissionsPolicies::allow_if_actor_super_admin(), + remove_admin_policy: PermissionsPolicies::allow_if_actor_super_admin(), + update_permissions_policy: PermissionsPolicies::allow_if_actor_super_admin(), + }; + + assert!(is_policy_all_members(&policy_set_new_metadata_permission).unwrap()); + + let mut metadata_policies_map = + MetadataPolicies::default_map(MetadataPolicies::allow_if_actor_admin()); + metadata_policies_map.insert( + "new_metadata_field_2".to_string(), + MetadataPolicies::allow_if_actor_admin(), + ); + let policy_set_new_metadata_permission = PolicySet { + add_member_policy: MembershipPolicies::allow_if_actor_admin(), + remove_member_policy: MembershipPolicies::allow_if_actor_admin(), + update_metadata_policy: metadata_policies_map, + add_admin_policy: PermissionsPolicies::allow_if_actor_super_admin(), + remove_admin_policy: PermissionsPolicies::allow_if_actor_super_admin(), + update_permissions_policy: PermissionsPolicies::allow_if_actor_super_admin(), + }; + + assert!(is_policy_admin_only(&policy_set_new_metadata_permission).unwrap()); + } + + #[test] + fn test_permission_update() { + let permissions = PolicySet::new( + MembershipPolicies::allow(), + MembershipPolicies::allow_if_actor_admin(), + MetadataPolicies::default_map(MetadataPolicies::allow()), + PermissionsPolicies::allow_if_actor_super_admin(), + PermissionsPolicies::allow_if_actor_super_admin(), + PermissionsPolicies::allow_if_actor_super_admin(), + ); + + // Commit should fail because actor is not superadmin + let commit = build_validated_commit(None, None, None, true, false, false); + assert!(!permissions.evaluate_commit(&commit)); + + // Commit should pass because actor is superadmin + let commit = build_validated_commit(None, None, None, true, false, true); + assert!(permissions.evaluate_commit(&commit)); + } + + #[test] + fn test_evaluate_field_with_unknown_policy() { + // Create a group whose default metadata can be updated by any member + let permissions = PolicySet::new( + MembershipPolicies::allow(), + MembershipPolicies::allow(), + MetadataPolicies::default_map(MetadataPolicies::allow()), + PermissionsPolicies::allow_if_actor_super_admin(), + PermissionsPolicies::allow_if_actor_super_admin(), + PermissionsPolicies::allow_if_actor_super_admin(), + ); + + // Non admin, non super admin can update group name + let name_updated_commit = build_validated_commit( + None, + None, + Some(vec![MetadataField::GroupName.to_string()]), + false, + false, + false, + ); + assert!(permissions.evaluate_commit(&name_updated_commit)); + + // Non admin, non super admin can NOT update non existing field + let non_existing_field_updated_commit = build_validated_commit( + None, + None, + Some(vec!["non_existing_field".to_string()]), + false, + false, + false, + ); + assert!(!permissions.evaluate_commit(&non_existing_field_updated_commit)); + + // Admin can update non existing field + let non_existing_field_updated_commit = build_validated_commit( + None, + None, + Some(vec!["non_existing_field".to_string()]), + false, + true, + false, + ); + assert!(permissions.evaluate_commit(&non_existing_field_updated_commit)); + + // Admin can NOT update non existing field that starts with super_admin only prefix + let non_existing_field_updated_commit = build_validated_commit( + None, + None, + Some(vec![ + SUPER_ADMIN_METADATA_PREFIX.to_string() + "non_existing_field", + ]), + false, + true, + false, + ); + assert!(!permissions.evaluate_commit(&non_existing_field_updated_commit)); + + // Super Admin CAN update non existing field that starts with super_admin only prefix + let non_existing_field_updated_commit = build_validated_commit( + None, + None, + Some(vec![ + SUPER_ADMIN_METADATA_PREFIX.to_string() + "non_existing_field", + ]), + false, + false, + true, + ); + assert!(permissions.evaluate_commit(&non_existing_field_updated_commit)); + } } diff --git a/xmtp_mls/src/groups/intents.rs b/xmtp_mls/src/groups/intents.rs index c0074b6a6..f5394fa74 100644 --- a/xmtp_mls/src/groups/intents.rs +++ b/xmtp_mls/src/groups/intents.rs @@ -19,9 +19,10 @@ use xmtp_proto::xmtp::mls::database::{ Version as UpdateGroupMembershipVersion, V1 as UpdateGroupMembershipV1, }, update_metadata_data::{Version as UpdateMetadataVersion, V1 as UpdateMetadataV1}, + update_permission_data::{Version as UpdatePermissionVersion, V1 as UpdatePermissionV1}, AccountAddresses, AddressesOrInstallationIds as AddressesOrInstallationIdsProtoWrapper, InstallationIds, PostCommitAction as PostCommitActionProto, SendMessageData, - UpdateAdminListsData, UpdateGroupMembershipData, UpdateMetadataData, + UpdateAdminListsData, UpdateGroupMembershipData, UpdateMetadataData, UpdatePermissionData, }; use crate::{ @@ -29,7 +30,11 @@ use crate::{ verified_key_package_v2::{KeyPackageVerificationError, VerifiedKeyPackageV2}, }; -use super::{group_membership::GroupMembership, group_mutable_metadata::MetadataField}; +use super::{ + group_membership::GroupMembership, + group_mutable_metadata::MetadataField, + group_permissions::{MembershipPolicies, MetadataPolicies, PermissionsPolicies}, +}; #[derive(Debug, Error)] pub enum IntentError { @@ -54,16 +59,12 @@ impl SendMessageIntentData { } pub(crate) fn to_bytes(&self) -> Vec { - let mut buf = Vec::new(); SendMessageData { version: Some(SendMessageVersion::V1(SendMessageV1 { payload_bytes: self.message.clone(), })), } - .encode(&mut buf) - .unwrap(); - - buf + .encode_to_vec() } pub(crate) fn from_bytes(data: &[u8]) -> Result { @@ -169,6 +170,20 @@ impl UpdateMetadataIntentData { field_value: group_image_url_square, } } + + pub fn new_update_group_description(group_description: String) -> Self { + Self { + field_name: MetadataField::Description.to_string(), + field_value: group_description, + } + } + + pub fn new_update_group_pinned_frame_url(pinned_frame_url: String) -> Self { + Self { + field_name: MetadataField::GroupPinnedFrameUrl.to_string(), + field_value: pinned_frame_url, + } + } } impl From for Vec { @@ -226,7 +241,7 @@ impl UpdateGroupMembershipIntentData { } pub fn apply_to_group_membership(&self, group_membership: &GroupMembership) -> GroupMembership { - log::info!("old group membership: {:?}", group_membership.members); + tracing::info!("old group membership: {:?}", group_membership.members); let mut new_membership = group_membership.clone(); for (inbox_id, sequence_id) in self.membership_updates.iter() { new_membership.add(inbox_id.clone(), *sequence_id); @@ -235,7 +250,7 @@ impl UpdateGroupMembershipIntentData { for inbox_id in self.removed_members.iter() { new_membership.remove(inbox_id) } - log::info!("updated group membership: {:?}", new_membership.members); + tracing::info!("updated group membership: {:?}", new_membership.members); new_membership } } @@ -373,6 +388,174 @@ impl TryFrom> for UpdateAdminListIntentData { } } +#[repr(i32)] +#[derive(Debug, Clone, PartialEq)] +pub enum PermissionUpdateType { + AddMember = 1, // Matches ADD_MEMBER in Protobuf + RemoveMember = 2, // Matches REMOVE_MEMBER in Protobuf + AddAdmin = 3, // Matches ADD_ADMIN in Protobuf + RemoveAdmin = 4, // Matches REMOVE_ADMIN in Protobuf + UpdateMetadata = 5, // Matches UPDATE_METADATA in Protobuf +} + +impl TryFrom for PermissionUpdateType { + type Error = &'static str; + + fn try_from(value: i32) -> Result { + match value { + 1 => Ok(PermissionUpdateType::AddMember), + 2 => Ok(PermissionUpdateType::RemoveMember), + 3 => Ok(PermissionUpdateType::AddAdmin), + 4 => Ok(PermissionUpdateType::RemoveAdmin), + 5 => Ok(PermissionUpdateType::UpdateMetadata), + _ => Err("Unknown value for PermissionUpdateType"), + } + } +} + +#[repr(i32)] +#[derive(Debug, Clone, PartialEq)] +pub enum PermissionPolicyOption { + Allow = 1, // Matches ADD_MEMBER in Protobuf + Deny = 2, // Matches REMOVE_MEMBER in Protobuf + AdminOnly = 3, // Matches ADD_ADMIN in Protobuf + SuperAdminOnly = 4, // Matches REMOVE_ADMIN in Protobuf +} + +impl TryFrom for PermissionPolicyOption { + type Error = &'static str; + + fn try_from(value: i32) -> Result { + match value { + 1 => Ok(PermissionPolicyOption::Allow), + 2 => Ok(PermissionPolicyOption::Deny), + 3 => Ok(PermissionPolicyOption::AdminOnly), + 4 => Ok(PermissionPolicyOption::SuperAdminOnly), + _ => Err("Unknown value for PermissionPolicyOption"), + } + } +} + +impl From for MembershipPolicies { + fn from(value: PermissionPolicyOption) -> Self { + match value { + PermissionPolicyOption::Allow => MembershipPolicies::allow(), + PermissionPolicyOption::Deny => MembershipPolicies::deny(), + PermissionPolicyOption::AdminOnly => MembershipPolicies::allow_if_actor_admin(), + PermissionPolicyOption::SuperAdminOnly => { + MembershipPolicies::allow_if_actor_super_admin() + } + } + } +} + +impl From for MetadataPolicies { + fn from(value: PermissionPolicyOption) -> Self { + match value { + PermissionPolicyOption::Allow => MetadataPolicies::allow(), + PermissionPolicyOption::Deny => MetadataPolicies::deny(), + PermissionPolicyOption::AdminOnly => MetadataPolicies::allow_if_actor_admin(), + PermissionPolicyOption::SuperAdminOnly => { + MetadataPolicies::allow_if_actor_super_admin() + } + } + } +} + +impl From for PermissionsPolicies { + fn from(value: PermissionPolicyOption) -> Self { + match value { + PermissionPolicyOption::Allow => { + tracing::error!("PermissionPolicyOption::Allow is not allowed for PermissionsPolicies, set to super_admin only instead"); + PermissionsPolicies::allow_if_actor_super_admin() + } + PermissionPolicyOption::Deny => PermissionsPolicies::deny(), + PermissionPolicyOption::AdminOnly => PermissionsPolicies::allow_if_actor_admin(), + PermissionPolicyOption::SuperAdminOnly => { + PermissionsPolicies::allow_if_actor_super_admin() + } + } + } +} + +#[derive(Debug, Clone)] +pub struct UpdatePermissionIntentData { + pub update_type: PermissionUpdateType, + pub policy_option: PermissionPolicyOption, + pub metadata_field_name: Option, +} + +impl UpdatePermissionIntentData { + pub fn new( + update_type: PermissionUpdateType, + policy_option: PermissionPolicyOption, + metadata_field_name: Option, + ) -> Self { + Self { + update_type, + policy_option, + metadata_field_name, + } + } +} + +impl From for Vec { + fn from(intent: UpdatePermissionIntentData) -> Self { + let mut buf = Vec::new(); + let update_type = intent.update_type as i32; + let policy_option = intent.policy_option as i32; + + UpdatePermissionData { + version: Some(UpdatePermissionVersion::V1(UpdatePermissionV1 { + permission_update_type: update_type, + permission_policy_option: policy_option, + metadata_field_name: intent.metadata_field_name, + })), + } + .encode(&mut buf) + .expect("encode error"); + + buf + } +} + +impl TryFrom> for UpdatePermissionIntentData { + type Error = IntentError; + + fn try_from(data: Vec) -> Result { + let msg = UpdatePermissionData::decode(Bytes::from(data))?; + + let update_type: PermissionUpdateType = match msg.version { + Some(UpdatePermissionVersion::V1(ref v1)) => { + PermissionUpdateType::try_from(v1.permission_update_type) + .map_err(|e| IntentError::Generic(e.to_string()))? + } + None => { + return Err(IntentError::Generic( + "missing update permission version".to_string(), + )) + } + }; + let policy_option: PermissionPolicyOption = match msg.version { + Some(UpdatePermissionVersion::V1(ref v1)) => { + PermissionPolicyOption::try_from(v1.permission_policy_option) + .map_err(|e| IntentError::Generic(e.to_string()))? + } + None => { + return Err(IntentError::Generic( + "missing update permission version".to_string(), + )) + } + }; + let metadata_field_name = match msg.version { + Some(UpdatePermissionVersion::V1(ref v1)) => v1.metadata_field_name.clone(), + None => None, + }; + + Ok(Self::new(update_type, policy_option, metadata_field_name)) + } +} + #[derive(Debug, Clone)] pub enum PostCommitAction { SendWelcomes(SendWelcomesAction), @@ -426,7 +609,6 @@ impl SendWelcomesAction { } pub(crate) fn to_bytes(&self) -> Vec { - let mut buf = Vec::new(); PostCommitActionProto { kind: Some(PostCommitActionKind::SendWelcomes(SendWelcomesProto { installations: self @@ -438,10 +620,7 @@ impl SendWelcomesAction { welcome_message: self.welcome_message.clone(), })), } - .encode(&mut buf) - .unwrap(); - - buf + .encode_to_vec() } } @@ -480,9 +659,11 @@ impl PostCommitAction { } } -impl From> for PostCommitAction { - fn from(data: Vec) -> Self { - PostCommitAction::from_bytes(data.as_slice()).unwrap() +impl TryFrom> for PostCommitAction { + type Error = IntentError; + + fn try_from(data: Vec) -> Result { + PostCommitAction::from_bytes(data.as_slice()) } } diff --git a/xmtp_mls/src/groups/members.rs b/xmtp_mls/src/groups/members.rs index 4e9b073db..91ee8d90d 100644 --- a/xmtp_mls/src/groups/members.rs +++ b/xmtp_mls/src/groups/members.rs @@ -3,7 +3,12 @@ use xmtp_id::InboxId; use super::{validated_commit::extract_group_membership, GroupError, MlsGroup}; use crate::{ - storage::association_state::StoredAssociationState, xmtp_openmls_provider::XmtpOpenMlsProvider, + storage::{ + association_state::StoredAssociationState, + consent_record::{ConsentState, ConsentType}, + }, + xmtp_openmls_provider::XmtpOpenMlsProvider, + Client, XmtpApi, }; #[derive(Debug, Clone)] @@ -12,6 +17,7 @@ pub struct GroupMember { pub account_addresses: Vec, pub installation_ids: Vec>, pub permission_level: PermissionLevel, + pub consent_state: ConsentState, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -23,14 +29,17 @@ pub enum PermissionLevel { impl MlsGroup { // Load the member list for the group from the DB, merging together multiple installations into a single entry - pub fn members(&self) -> Result, GroupError> { - let conn = self.context.store.conn()?; - let provider = self.context.mls_provider(conn); - self.members_with_provider(&provider) + pub async fn members( + &self, + client: &Client, + ) -> Result, GroupError> { + let provider = self.mls_provider()?; + self.members_with_provider(client, &provider).await } - pub fn members_with_provider( + pub async fn members_with_provider( &self, + client: &Client, provider: &XmtpOpenMlsProvider, ) -> Result, GroupError> { let openmls_group = self.load_mls_group(provider)?; @@ -40,14 +49,47 @@ impl MlsGroup { .members .into_iter() .map(|(inbox_id, sequence_id)| (inbox_id, sequence_id as i64)) + .filter(|(_, sequence_id)| *sequence_id != 0) // Skip the initial state .collect::>(); let conn = provider.conn_ref(); - let association_state_map = StoredAssociationState::batch_read_from_cache(conn, requests)?; - let mutable_metadata = self.mutable_metadata()?; - // TODO: Figure out what to do with missing members from the local DB. Do we go to the network? Load from identity updates? - // Right now I am just omitting them - let members = association_state_map + let mut association_states = + StoredAssociationState::batch_read_from_cache(conn, requests.clone())?; + let mutable_metadata = self.mutable_metadata(provider)?; + if association_states.len() != requests.len() { + // Attempt to rebuild the cache. + let missing_requests: Vec<_> = requests + .iter() + .filter_map(|(id, sequence)| { + // Filter out association states we already have to avoid unnecessary requests. + if association_states + .iter() + .any(|state| state.inbox_id() == id) + { + return None; + } + Some((id.clone(), Some(*sequence))) + }) + .collect(); + + let mut new_states = client + .batch_get_association_state(conn, &missing_requests) + .await?; + association_states.append(&mut new_states); + + if association_states.len() != requests.len() { + // Cache miss - not expected to happen because: + // 1. We don't allow updates to the group metadata unless we have already validated the association state + // 2. When validating the association state, we must have written it to the cache + tracing::error!( + "Failed to load all members for group - metadata: {:?}, computed members: {:?}", + requests, + association_states + ); + return Err(GroupError::InvalidGroupMembership); + } + } + let members = association_states .into_iter() .map(|association_state| { let inbox_id_str = association_state.inbox_id().to_string(); @@ -61,11 +103,15 @@ impl MlsGroup { PermissionLevel::Member }; + let consent = + conn.get_consent_record(inbox_id_str.clone(), ConsentType::InboxId)?; + Ok(GroupMember { - inbox_id: inbox_id_str, + inbox_id: inbox_id_str.clone(), account_addresses: association_state.account_addresses(), installation_ids: association_state.installation_ids(), permission_level, + consent_state: consent.map_or(ConsentState::Unknown, |c| c.state), }) }) .collect::, GroupError>>()?; diff --git a/xmtp_mls/src/groups/message_history.rs b/xmtp_mls/src/groups/message_history.rs index 4cbed99bb..3b08eb9a8 100644 --- a/xmtp_mls/src/groups/message_history.rs +++ b/xmtp_mls/src/groups/message_history.rs @@ -7,13 +7,11 @@ use aes_gcm::{ aead::{Aead, KeyInit}, Aes256Gcm, }; -use prost::Message; use rand::{ distributions::{Alphanumeric, DistString}, Rng, RngCore, }; -use ring::hmac; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use thiserror::Error; use xmtp_cryptography::utils as crypto_utils; @@ -27,24 +25,33 @@ use xmtp_proto::{ }, }; -use super::GroupError; +use super::{GroupError, MlsGroup}; use crate::XmtpApi; use crate::{ client::ClientError, - configuration::DELIMITER, - groups::{intents::SendMessageIntentData, GroupMessageKind, StoredGroupMessage}, - storage::{ - group::StoredGroup, - group_intent::{IntentKind, NewGroupIntent}, - StorageError, - }, + groups::{GroupMessageKind, StoredGroupMessage}, + storage::{group::StoredGroup, StorageError}, Client, Store, }; const ENC_KEY_SIZE: usize = 32; // 256-bit key const NONCE_SIZE: usize = 12; // 96-bit nonce +pub struct MessageHistoryUrls; + +impl MessageHistoryUrls { + pub const LOCAL_ADDRESS: &'static str = "http://0.0.0.0:5558"; + pub const DEV_ADDRESS: &'static str = "https://message-history.dev.ephemera.network/"; + pub const PRODUCTION_ADDRESS: &'static str = "https://message-history.ephemera.network/"; +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum MessageHistoryContent { + Request(MessageHistoryRequest), + Reply(MessageHistoryReply), +} + #[derive(Debug, Error)] pub enum MessageHistoryError { #[error("pin not found")] @@ -53,8 +60,8 @@ pub enum MessageHistoryError { PinMismatch, #[error("IO error: {0}")] IO(#[from] std::io::Error), - #[error("JSON serialization error: {0}")] - Serialization(#[from] serde_json::Error), + #[error("Serialization/Deserialization Error {0}")] + Serde(#[from] serde_json::Error), #[error("AES-GCM encryption error")] AesGcm(#[from] aes_gcm::Error), #[error("reqwest error: {0}")] @@ -69,6 +76,22 @@ pub enum MessageHistoryError { Client(#[from] ClientError), #[error("group error: {0}")] Group(#[from] GroupError), + #[error("request ID of reply does not match request")] + ReplyRequestIdMismatch, + #[error("reply already processed")] + ReplyAlreadyProcessed, + #[error("no pending request to reply to")] + NoPendingRequest, + #[error("no reply to process")] + NoReplyToProcess, + #[error("generic: {0}")] + Generic(String), + #[error("missing history sync url")] + MissingHistorySyncUrl, + #[error("invalid history message payload")] + InvalidPayload, + #[error("invalid history bundle url")] + InvalidBundleUrl, } #[derive(Debug, Deserialize)] @@ -82,9 +105,31 @@ impl Client where ApiClient: XmtpApi, { - pub async fn allow_history_sync(&self) -> Result<(), GroupError> { - let history_sync_group = self.create_sync_group()?; - history_sync_group.sync(self).await?; + pub fn get_sync_group(&self) -> Result { + let conn = self.store().conn()?; + let sync_group_id = conn + .find_sync_groups()? + .pop() + .ok_or(GroupError::GroupNotFound)? + .id; + let sync_group = self.group(sync_group_id.clone())?; + + Ok(sync_group) + } + + pub async fn enable_history_sync(&self) -> Result<(), GroupError> { + // look for the sync group, create if not found + let sync_group = match self.get_sync_group() { + Ok(group) => group, + Err(_) => { + // create the sync group + self.create_sync_group()? + } + }; + + // sync the group + sync_group.sync(self).await?; + Ok(()) } @@ -99,113 +144,289 @@ where Ok(()) } - pub(crate) async fn send_history_request(&self) -> Result { + // returns (request_id, pin_code) + pub async fn send_history_request(&self) -> Result<(String, String), MessageHistoryError> { // find the sync group let conn = self.store().conn()?; - let sync_group_id = conn - .find_sync_groups()? - .pop() - .ok_or(GroupError::GroupNotFound)? - .id; - let sync_group = self.group(sync_group_id.clone())?; + let sync_group = self.get_sync_group()?; + + // sync the group + sync_group.sync(self).await?; + + let messages = sync_group.find_messages( + Some(GroupMessageKind::Application), + None, + None, + None, + None, + )?; + + let last_message = messages.last(); + if let Some(msg) = last_message { + let message_history_content = + serde_json::from_slice::(&msg.decrypted_message_bytes)?; + + if let MessageHistoryContent::Request(request) = message_history_content { + return Ok((request.request_id, request.pin_code)); + } + }; // build the request let history_request = HistoryRequest::new(); let pin_code = history_request.pin_code.clone(); - let idempotency_key = new_request_id(); - let envelope = PlaintextEnvelope { - content: Some(Content::V2(V2 { - message_type: Some(Request(history_request.into())), - idempotency_key, - })), - }; + let request_id = history_request.request_id.clone(); - // build the intent - let mut encoded_envelope = vec![]; - envelope - .encode(&mut encoded_envelope) - .map_err(GroupError::EncodeError)?; - let intent_data: Vec = SendMessageIntentData::new(encoded_envelope).into(); - let intent = NewGroupIntent::new(IntentKind::SendMessage, sync_group_id, intent_data); - intent.store(&conn)?; + let content = MessageHistoryContent::Request(MessageHistoryRequest { + request_id: request_id.clone(), + pin_code: pin_code.clone(), + }); + let content_bytes = serde_json::to_vec(&content)?; + + let _message_id = + sync_group.prepare_message(content_bytes.as_slice(), &conn, move |_time_ns| { + PlaintextEnvelope { + content: Some(Content::V2(V2 { + message_type: Some(Request(history_request.into())), + idempotency_key: new_request_id(), + })), + } + })?; // publish the intent - if let Err(err) = sync_group.publish_intents(conn, self).await { - log::error!("error publishing sync group intents: {:?}", err); + if let Err(err) = sync_group.publish_intents(&conn.into(), self).await { + tracing::error!("error publishing sync group intents: {:?}", err); } - Ok(pin_code) + Ok((request_id, pin_code)) } pub(crate) async fn send_history_reply( &self, contents: MessageHistoryReply, - ) -> Result<(), GroupError> { + ) -> Result<(), MessageHistoryError> { // find the sync group let conn = self.store().conn()?; - let sync_group_id = conn - .find_sync_groups()? - .pop() - .ok_or(GroupError::GroupNotFound)? - .id; + let sync_group = self.get_sync_group()?; + + // sync the group + Box::pin(sync_group.sync(self)).await?; + + let messages = sync_group.find_messages( + Some(GroupMessageKind::Application), + None, + None, + None, + None, + )?; - // build the reply - let envelope = PlaintextEnvelope { - content: Some(Content::V2(V2 { - idempotency_key: new_request_id(), - message_type: Some(Reply(contents)), - })), + let last_message = match messages.last() { + Some(msg) => { + let message_history_content = + serde_json::from_slice::(&msg.decrypted_message_bytes)?; + match message_history_content { + MessageHistoryContent::Request(request) => { + // check that the request ID matches + if !request.request_id.eq(&contents.request_id) { + return Err(MessageHistoryError::ReplyRequestIdMismatch); + } + Some(msg) + } + MessageHistoryContent::Reply(_) => { + // if last message is a reply, it's already been processed + return Err(MessageHistoryError::ReplyAlreadyProcessed); + } + } + } + None => { + return Err(MessageHistoryError::NoPendingRequest); + } }; - // build the intent - let mut encoded_envelope = vec![]; - envelope - .encode(&mut encoded_envelope) - .map_err(GroupError::EncodeError)?; - let intent_data: Vec = SendMessageIntentData::new(encoded_envelope).into(); - let intent = - NewGroupIntent::new(IntentKind::SendMessage, sync_group_id.clone(), intent_data); - intent.store(&conn)?; + tracing::info!("{:?}", last_message); + + if let Some(msg) = last_message { + // ensure the requester is a member of all the groups + self.ensure_member_of_all_groups(msg.sender_inbox_id.clone()) + .await?; + } + + // the reply message + let content = MessageHistoryContent::Reply(contents.clone()); + let content_bytes = serde_json::to_vec(&content)?; + + let _message_id = + sync_group.prepare_message(content_bytes.as_slice(), &conn, move |_time_ns| { + PlaintextEnvelope { + content: Some(Content::V2(V2 { + idempotency_key: new_request_id(), + message_type: Some(Reply(contents)), + })), + } + })?; // publish the intent - let sync_group = self.group(sync_group_id)?; - if let Err(err) = sync_group.publish_intents(conn, self).await { - log::error!("error publishing sync group intents: {:?}", err); + if let Err(err) = sync_group.publish_intents(&conn.into(), self).await { + tracing::error!("error publishing sync group intents: {:?}", err); } Ok(()) } - pub(crate) fn provide_pin(&self, pin_challenge: &str) -> Result<(), GroupError> { - let conn = self.store().conn()?; - let sync_group_id = conn - .find_sync_groups()? - .pop() - .ok_or(GroupError::GroupNotFound)? - .id; + pub async fn get_pending_history_request( + &self, + ) -> Result, MessageHistoryError> { + let sync_group = self.get_sync_group()?; - let requests = conn.get_group_messages( - sync_group_id, + // sync the group + sync_group.sync(self).await?; + + let messages = sync_group.find_messages( + Some(GroupMessageKind::Application), + None, None, None, + None, + )?; + let last_message = messages.last(); + + let history_request: Option<(String, String)> = if let Some(msg) = last_message { + let message_history_content = + serde_json::from_slice::(&msg.decrypted_message_bytes)?; + match message_history_content { + // if the last message is a request, return its request ID and pin code + MessageHistoryContent::Request(request) => { + Some((request.request_id, request.pin_code)) + } + _ => None, + } + } else { + None + }; + + Ok(history_request) + } + + pub async fn reply_to_history_request( + &self, + ) -> Result { + let pending_request = self.get_pending_history_request().await?; + + if let Some((request_id, _)) = pending_request { + let reply = self.prepare_history_reply(&request_id).await?; + self.send_history_reply(reply.clone().into()).await?; + return Ok(reply.into()); + } + + Err(MessageHistoryError::NoPendingRequest) + } + + pub async fn get_latest_history_reply( + &self, + ) -> Result, MessageHistoryError> { + let sync_group = self.get_sync_group()?; + + // sync the group + sync_group.sync(self).await?; + + let messages = sync_group.find_messages( Some(GroupMessageKind::Application), None, None, + None, + None, + )?; + + let last_message = messages.last(); + + let reply: Option = match last_message { + Some(msg) => { + // if the message was sent by this installation, ignore it + if msg + .sender_installation_id + .eq(&self.installation_public_key()) + { + None + } else { + let message_history_content = serde_json::from_slice::( + &msg.decrypted_message_bytes, + )?; + match message_history_content { + // if the last message is a reply, return it + MessageHistoryContent::Reply(reply) => Some(reply), + _ => None, + } + } + } + None => None, + }; + + Ok(reply) + } + + pub async fn process_history_reply(&self) -> Result<(), MessageHistoryError> { + let reply = self.get_latest_history_reply().await?; + + if let Some(reply) = reply { + let Some(encryption_key) = reply.encryption_key.clone() else { + return Err(MessageHistoryError::InvalidPayload); + }; + + let history_bundle = download_history_bundle(&reply.url).await?; + let messages_path = std::env::temp_dir().join("messages.jsonl"); + + decrypt_history_file(&history_bundle, &messages_path, encryption_key)?; + + self.insert_history_bundle(&messages_path)?; + + // clean up temporary files associated with the bundle + std::fs::remove_file(history_bundle.as_path())?; + std::fs::remove_file(messages_path.as_path())?; + + self.sync_welcomes().await?; + + let conn = self.store().conn()?; + let groups = conn.find_groups(None, None, None, None)?; + for crate::storage::group::StoredGroup { id, .. } in groups.into_iter() { + let group = self.group(id)?; + Box::pin(group.sync(self)).await?; + } + + return Ok(()); + } + + Err(MessageHistoryError::NoReplyToProcess) + } + + pub(crate) fn verify_pin( + &self, + request_id: &str, + pin_code: &str, + ) -> Result<(), MessageHistoryError> { + let sync_group = self.get_sync_group()?; + let requests = sync_group.find_messages( + Some(GroupMessageKind::Application), + None, + None, + None, + None, )?; let request = requests.into_iter().find(|msg| { - let msg_bytes = &msg.decrypted_message_bytes; - match msg_bytes.iter().position(|&idx| idx == DELIMITER as u8) { - Some(index) => { - let (_id_part, pin_part) = msg_bytes.split_at(index); - let pin = String::from_utf8_lossy(&pin_part[1..]); - verify_pin(&pin, pin_challenge) + let message_history_content = + serde_json::from_slice::(&msg.decrypted_message_bytes); + + match message_history_content { + Ok(MessageHistoryContent::Request(request)) => { + request.request_id.eq(request_id) && request.pin_code.eq(pin_code) + } + Err(e) => { + tracing::debug!("serde_json error: {:?}", e); + false } - None => false, + _ => false, } }); + if request.is_none() { - return Err(GroupError::MessageHistory(Box::new( - MessageHistoryError::PinNotFound, - ))); + return Err(MessageHistoryError::PinNotFound); } Ok(()) @@ -241,22 +462,21 @@ where pub(crate) async fn prepare_history_reply( &self, request_id: &str, - url: &str, ) -> Result { let (history_file, enc_key) = self.write_history_bundle().await?; + let url = match &self.history_sync_url { + Some(url) => url.as_str(), + None => return Err(MessageHistoryError::MissingHistorySyncUrl), + }; + let upload_url = format!("{}{}", url, "upload"); + tracing::info!("using upload url {:?}", upload_url); - let signing_key = HistoryKeyType::new_chacha20_poly1305_key(); - upload_history_bundle(url, history_file.clone(), signing_key.as_bytes()).await?; + let bundle_file = upload_history_bundle(&upload_url, history_file.clone()).await?; + let bundle_url = format!("{}files/{}", url, bundle_file); - let history_reply = HistoryReply::new( - request_id, - url, - signing_key.as_bytes().to_vec(), - signing_key, - enc_key, - ); + tracing::info!("history bundle uploaded to {:?}", bundle_url); - Ok(history_reply) + Ok(HistoryReply::new(request_id, &bundle_url, enc_key)) } async fn write_history_bundle(&self) -> Result<(PathBuf, HistoryKeyType), MessageHistoryError> { @@ -282,14 +502,7 @@ where async fn prepare_groups_to_sync(&self) -> Result, MessageHistoryError> { let conn = self.store().conn()?; - let groups = conn.find_groups(None, None, None, None)?; - let mut all_groups: Vec = vec![]; - - for group in groups.into_iter() { - all_groups.push(group); - } - - Ok(all_groups) + Ok(conn.find_groups(None, None, None, None)?) } async fn prepare_messages_to_sync( @@ -384,56 +597,60 @@ pub(crate) fn decrypt_history_file( async fn upload_history_bundle( url: &str, file_path: PathBuf, - signing_key: &[u8], -) -> Result<(), MessageHistoryError> { +) -> Result { let mut file = File::open(file_path)?; let mut content = Vec::new(); file.read_to_end(&mut content)?; - let key = hmac::Key::new(hmac::HMAC_SHA256, signing_key); - let tag = hmac::sign(&key, &content); - let hmac_hex = hex::encode(tag.as_ref()); - let client = reqwest::Client::new(); - let _response = client - .post(url) - .header("X-HMAC", hmac_hex) - .body(content) - .send() - .await?; + let response = client.post(url).body(content).send().await?; - Ok(()) + if response.status().is_success() { + Ok(response.text().await?) + } else { + tracing::error!( + "Failed to upload file. Status code: {} Response: {:?}", + response.status(), + response + ); + Err(MessageHistoryError::Reqwest( + response + .error_for_status() + .expect_err("Checked for success"), + )) + } } -pub(crate) async fn download_history_bundle( - url: &str, - hmac_value: Vec, - signing_key: MessageHistoryKeyType, -) -> Result { - let sign_key: HistoryKeyType = signing_key.try_into()?; - let sign_key_bytes = sign_key.as_bytes().to_vec(); +pub(crate) async fn download_history_bundle(url: &str) -> Result { let client = reqwest::Client::new(); - let response = client - .get(url) - .header("X-HMAC", hmac_value) - .header("X-SIGNING-KEY", hex::encode(sign_key_bytes)) - .send() - .await?; + + tracing::info!("downloading history bundle from {:?}", url); + + let bundle_name = url + .split('/') + .last() + .ok_or(MessageHistoryError::InvalidBundleUrl)?; + + let response = client.get(url).send().await?; if response.status().is_success() { - let file_path = std::env::temp_dir().join("downloaded_bundle.jsonl.enc"); + let file_name = format!("{}.jsonl.enc", bundle_name); + let file_path = std::env::temp_dir().join(file_name); let mut file = File::create(&file_path)?; let bytes = response.bytes().await?; file.write_all(&bytes)?; + tracing::info!("downloaded history bundle to {:?}", file_path); Ok(file_path) } else { - eprintln!( + tracing::error!( "Failed to download file. Status code: {} Response: {:?}", response.status(), response ); Err(MessageHistoryError::Reqwest( - response.error_for_status().unwrap_err(), + response + .error_for_status() + .expect_err("Checked for success"), )) } } @@ -462,33 +679,21 @@ impl From for MessageHistoryRequest { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct HistoryReply { /// Unique ID for each client Message History Request request_id: String, /// URL to download the backup bundle url: String, - /// HMAC value of the backup bundle - bundle_hash: Vec, - /// HMAC Signing key for the backup bundle - signing_key: HistoryKeyType, /// Encryption key for the backup bundle encryption_key: HistoryKeyType, } impl HistoryReply { - pub(crate) fn new( - id: &str, - url: &str, - bundle_hash: Vec, - signing_key: HistoryKeyType, - encryption_key: HistoryKeyType, - ) -> Self { + pub(crate) fn new(id: &str, url: &str, encryption_key: HistoryKeyType) -> Self { Self { request_id: id.into(), url: url.into(), - bundle_hash, - signing_key, encryption_key, } } @@ -499,8 +704,6 @@ impl From for MessageHistoryReply { MessageHistoryReply { request_id: reply.request_id, url: reply.url, - bundle_hash: reply.bundle_hash, - signing_key: Some(reply.signing_key.into()), encryption_key: Some(reply.encryption_key.into()), } } @@ -576,13 +779,6 @@ fn new_pin() -> String { format!("{:04}", pin) } -// Yes, this is a just a simple string comparison. -// If we need to add more complex logic, we can do so here. -// For example if we want to add a time limit or enforce a certain number of attempts. -fn verify_pin(expected: &str, actual: &str) -> bool { - expected.eq(actual) -} - #[cfg(test)] mod tests { @@ -594,14 +790,15 @@ mod tests { use std::io::{BufRead, BufReader}; use tempfile::NamedTempFile; use xmtp_cryptography::utils::generate_local_wallet; + use xmtp_id::InboxOwner; use crate::{assert_ok, builder::ClientBuilder, groups::GroupMetadataOptions}; #[tokio::test(flavor = "multi_thread", worker_threads = 1)] - async fn test_allow_history_sync() { + async fn test_enable_history_sync() { let wallet = generate_local_wallet(); let client = ClientBuilder::new_test_client(&wallet).await; - assert_ok!(client.allow_history_sync().await); + assert_ok!(client.enable_history_sync().await); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] @@ -610,7 +807,7 @@ mod tests { let amal_a = ClientBuilder::new_test_client(&wallet).await; let amal_b = ClientBuilder::new_test_client(&wallet).await; let amal_c = ClientBuilder::new_test_client(&wallet).await; - assert_ok!(amal_c.allow_history_sync().await); + assert_ok!(amal_c.enable_history_sync().await); amal_a.sync_welcomes().await.expect("sync_welcomes"); amal_b.sync_welcomes().await.expect("sync_welcomes"); @@ -636,30 +833,76 @@ mod tests { async fn test_send_history_request() { let wallet = generate_local_wallet(); let client = ClientBuilder::new_test_client(&wallet).await; - assert_ok!(client.allow_history_sync().await); + assert_ok!(client.enable_history_sync().await); // test that the request is sent, and that the pin code is returned - let pin_code = client + let (request_id, pin_code) = client .send_history_request() .await .expect("history request"); + assert_eq!(request_id.len(), 32); assert_eq!(pin_code.len(), 4); + + // test that another request will return the same request_id and + // pin_code because it hasn't been replied to yet + let (request_id2, pin_code2) = client + .send_history_request() + .await + .expect("history request"); + assert_eq!(request_id, request_id2); + assert_eq!(pin_code, pin_code2); + + // make sure there's only 1 message in the sync group + let sync_group = client.get_sync_group().unwrap(); + let messages = sync_group + .find_messages(Some(GroupMessageKind::Application), None, None, None, None) + .unwrap(); + assert_eq!(messages.len(), 1); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn test_send_history_reply() { let wallet = generate_local_wallet(); let client = ClientBuilder::new_test_client(&wallet).await; - assert_ok!(client.allow_history_sync().await); + assert_ok!(client.enable_history_sync().await); let request_id = new_request_id(); let url = "https://test.com/abc-123"; - let backup_hash = b"ABC123".into(); - let signing_key = HistoryKeyType::new_chacha20_poly1305_key(); let encryption_key = HistoryKeyType::new_chacha20_poly1305_key(); - let reply = HistoryReply::new(&request_id, url, backup_hash, signing_key, encryption_key); + let reply = HistoryReply::new(&request_id, url, encryption_key); let result = client.send_history_reply(reply.into()).await; + + // the reply should fail because there's no pending request to reply to + assert!(result.is_err()); + + let (request_id, _) = client + .send_history_request() + .await + .expect("history request"); + + let request_id2 = new_request_id(); + let url = "https://test.com/abc-123"; + let encryption_key = HistoryKeyType::new_chacha20_poly1305_key(); + let reply = HistoryReply::new(&request_id2, url, encryption_key); + let result = client.send_history_reply(reply.into()).await; + + // the reply should fail because there's a mismatched request ID + assert!(result.is_err()); + + let url = "https://test.com/abc-123"; + let encryption_key = HistoryKeyType::new_chacha20_poly1305_key(); + let reply = HistoryReply::new(&request_id, url, encryption_key); + let result = client.send_history_reply(reply.into()).await; + + // the reply should succeed with a valid request ID assert_ok!(result); + + // make sure there's 2 messages in the sync group + let sync_group = client.get_sync_group().unwrap(); + let messages = sync_group + .find_messages(Some(GroupMessageKind::Application), None, None, None, None) + .unwrap(); + assert_eq!(messages.len(), 2); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] @@ -667,11 +910,11 @@ mod tests { let wallet = generate_local_wallet(); let amal_a = ClientBuilder::new_test_client(&wallet).await; let amal_b = ClientBuilder::new_test_client(&wallet).await; - assert_ok!(amal_b.allow_history_sync().await); + assert_ok!(amal_b.enable_history_sync().await); amal_a.sync_welcomes().await.expect("sync_welcomes"); - let _sent = amal_b + let (_group_id, _pin_code) = amal_b .send_history_request() .await .expect("history request"); @@ -696,15 +939,15 @@ mod tests { #[tokio::test(flavor = "multi_thread", worker_threads = 1)] #[ignore] // this test is only relevant if we are enforcing the PIN challenge - async fn test_provide_pin_challenge() { + async fn test_verify_pin() { let wallet = generate_local_wallet(); let amal_a = ClientBuilder::new_test_client(&wallet).await; let amal_b = ClientBuilder::new_test_client(&wallet).await; - assert_ok!(amal_b.allow_history_sync().await); + assert_ok!(amal_b.enable_history_sync().await); amal_a.sync_welcomes().await.expect("sync_welcomes"); - let pin_code = amal_b + let (request_id, pin_code) = amal_b .send_history_request() .await .expect("history request"); @@ -714,25 +957,72 @@ mod tests { // get the first sync group let amal_a_sync_group = amal_a.group(amal_a_sync_groups[0].id.clone()).unwrap(); amal_a_sync_group.sync(&amal_a).await.expect("sync"); - let pin_challenge_result = amal_a.provide_pin(&pin_code); + let pin_challenge_result = amal_a.verify_pin(&request_id, &pin_code); assert_ok!(pin_challenge_result); - let pin_challenge_result_2 = amal_a.provide_pin("000"); + let pin_challenge_result_2 = amal_a.verify_pin("000", "000"); assert!(pin_challenge_result_2.is_err()); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] #[ignore] async fn test_request_reply_roundtrip() { + let options = mockito::ServerOpts { + host: HISTORY_SERVER_HOST, + port: HISTORY_SERVER_PORT + 1, + ..Default::default() + }; + let mut server = mockito::Server::new_with_opts_async(options).await; + + let _m = server + .mock("POST", "/upload") + .with_status(201) + .with_body("File uploaded") + .create(); + + let history_sync_url = format!( + "http://{}:{}/upload", + HISTORY_SERVER_HOST, + HISTORY_SERVER_PORT + 1 + ); + let wallet = generate_local_wallet(); let amal_a = ClientBuilder::new_test_client(&wallet).await; + let _group_a = amal_a + .create_group(None, GroupMetadataOptions::default()) + .expect("create group"); + + let groups = amal_a.prepare_groups_to_sync().await.unwrap(); + + let input_file = NamedTempFile::new().unwrap(); + let input_path = input_file.path(); + write_to_file(input_path, groups).unwrap(); + + let output_file = NamedTempFile::new().unwrap(); + let output_path = output_file.path(); + let encryption_key = HistoryKeyType::new_chacha20_poly1305_key(); + encrypt_history_file(input_path, output_path, encryption_key.as_bytes()).unwrap(); + + let mut file = File::open(output_path).unwrap(); + let mut content = Vec::new(); + file.read_to_end(&mut content).unwrap(); + + let _m = server + .mock("GET", "/upload") + .with_status(201) + .with_body(content) + .create(); + + let wallet = generate_local_wallet(); + let mut amal_a = ClientBuilder::new_test_client(&wallet).await; + amal_a.history_sync_url = Some(history_sync_url.clone()); let amal_b = ClientBuilder::new_test_client(&wallet).await; - assert_ok!(amal_b.allow_history_sync().await); + assert_ok!(amal_b.enable_history_sync().await); amal_a.sync_welcomes().await.expect("sync_welcomes"); // amal_b sends a message history request to sync group messages - let _pin_code = amal_b + let (_group_id, _pin_code) = amal_b .send_history_request() .await .expect("history request"); @@ -744,13 +1034,7 @@ mod tests { amal_a_sync_group.sync(&amal_a).await.expect("sync"); // amal_a builds and sends a message history reply back - let history_reply = HistoryReply::new( - "test", - "https://test.com/abc-123", - b"ABC123".into(), - HistoryKeyType::new_chacha20_poly1305_key(), - HistoryKeyType::new_chacha20_poly1305_key(), - ); + let history_reply = HistoryReply::new(&new_request_id(), &history_sync_url, encryption_key); amal_a .send_history_reply(history_reply.into()) .await @@ -798,22 +1082,10 @@ mod tests { .create_group(None, GroupMetadataOptions::default()) .expect("create group"); - group_a - .send_message(b"hi", &amal_a) - .await - .expect("send message"); - group_a - .send_message(b"hi x2", &amal_a) - .await - .expect("send message"); - group_b - .send_message(b"hi", &amal_a) - .await - .expect("send message"); - group_b - .send_message(b"hi x2", &amal_a) - .await - .expect("send message"); + group_a.send_message(b"hi", &amal_a).await.expect("send"); + group_a.send_message(b"hi x2", &amal_a).await.expect("send"); + group_b.send_message(b"hi", &amal_a).await.expect("send"); + group_b.send_message(b"hi x2", &amal_a).await.expect("send"); let messages_result = amal_a.prepare_messages_to_sync().await.unwrap(); assert_eq!(messages_result.len(), 4); @@ -830,22 +1102,10 @@ mod tests { .create_group(None, GroupMetadataOptions::default()) .expect("create group"); - group_a - .send_message(b"hi", &amal_a) - .await - .expect("send message"); - group_a - .send_message(b"hi", &amal_a) - .await - .expect("send message"); - group_b - .send_message(b"hi", &amal_a) - .await - .expect("send message"); - group_b - .send_message(b"hi", &amal_a) - .await - .expect("send message"); + group_a.send_message(b"hi", &amal_a).await.expect("send"); + group_a.send_message(b"hi", &amal_a).await.expect("send"); + group_b.send_message(b"hi", &amal_a).await.expect("send"); + group_b.send_message(b"hi", &amal_a).await.expect("send"); let groups = amal_a.prepare_groups_to_sync().await.unwrap(); let messages = amal_a.prepare_messages_to_sync().await.unwrap(); @@ -909,12 +1169,11 @@ mod tests { let _m = server .mock("POST", "/upload") - .with_status(200) + .with_status(201) .with_body("File uploaded") .create(); - let signing_key = b"test_signing_key"; - let file_content = b"test file content"; + let file_content = b"'{\"test\": \"data\"}\n{\"test\": \"data2\"}\n'"; let mut file = NamedTempFile::new().unwrap(); file.write_all(file_content).unwrap(); @@ -925,7 +1184,7 @@ mod tests { HISTORY_SERVER_HOST, HISTORY_SERVER_PORT + 1 ); - let result = upload_history_bundle(&url, file_path.into(), signing_key).await; + let result = upload_history_bundle(&url, file_path.into()).await; assert!(result.is_ok()); _m.assert_async().await; @@ -935,8 +1194,6 @@ mod tests { #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn test_download_history_bundle() { let bundle_id = "test_bundle_id"; - let hmac_value = "test_hmac_value"; - let signing_key = HistoryKeyType::new_chacha20_poly1305_key(); let options = mockito::ServerOpts { host: HISTORY_SERVER_HOST, port: HISTORY_SERVER_PORT, @@ -954,7 +1211,7 @@ mod tests { "http://{}:{}/files/{bundle_id}", HISTORY_SERVER_HOST, HISTORY_SERVER_PORT ); - let output_path = download_history_bundle(&url, hmac_value.into(), signing_key.into()) + let output_path = download_history_bundle(&url) .await .expect("could not download history bundle"); @@ -966,9 +1223,9 @@ mod tests { #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn test_prepare_history_reply() { let wallet = generate_local_wallet(); - let amal_a = ClientBuilder::new_test_client(&wallet).await; + let mut amal_a = ClientBuilder::new_test_client(&wallet).await; let amal_b = ClientBuilder::new_test_client(&wallet).await; - assert_ok!(amal_b.allow_history_sync().await); + assert_ok!(amal_b.enable_history_sync().await); amal_a.sync_welcomes().await.expect("sync_welcomes"); @@ -982,19 +1239,158 @@ mod tests { }; let mut server = mockito::Server::new_with_opts_async(options).await; - let url = format!("http://{HISTORY_SERVER_HOST}:{port}/upload"); + let url = format!("http://{HISTORY_SERVER_HOST}:{port}/"); let _m = server .mock("POST", "/upload") .with_status(201) .with_body("encrypted_content") .create(); - let reply = amal_a.prepare_history_reply(&request_id, &url).await; + amal_a.history_sync_url = Some(url); + let reply = amal_a.prepare_history_reply(&request_id).await; assert!(reply.is_ok()); _m.assert_async().await; server.reset(); } + #[tokio::test] + async fn test_get_pending_history_request() { + let wallet = generate_local_wallet(); + let amal_a = ClientBuilder::new_test_client(&wallet).await; + + // enable history sync for the client + assert_ok!(amal_a.enable_history_sync().await); + + // ensure there's no pending request initially + let initial_request = amal_a.get_pending_history_request().await; + assert!(initial_request.is_ok()); + assert!(initial_request.unwrap().is_none()); + + // create a history request + let request = amal_a + .send_history_request() + .await + .expect("history request"); + + // check for the pending request + let pending_request = amal_a.get_pending_history_request().await; + assert!(pending_request.is_ok()); + let pending = pending_request.unwrap(); + assert!(pending.is_some()); + + let (request_id, pin_code) = pending.unwrap(); + assert_eq!(request_id, request.0); + assert_eq!(pin_code, request.1); + } + + #[tokio::test] + async fn test_get_latest_history_reply() { + let wallet = generate_local_wallet(); + let amal_a = ClientBuilder::new_test_client(&wallet).await; + let amal_b = ClientBuilder::new_test_client(&wallet).await; + + // enable history sync for both clients + assert_ok!(amal_a.enable_history_sync().await); + assert_ok!(amal_b.enable_history_sync().await); + + // ensure there's no reply initially + let initial_reply = amal_b.get_latest_history_reply().await; + assert!(initial_reply.is_ok()); + assert!(initial_reply.unwrap().is_none()); + + // amal_b sends a history request + let (request_id, _pin_code) = amal_b + .send_history_request() + .await + .expect("history request"); + + // sync amal_a + amal_a.sync_welcomes().await.expect("sync_welcomes"); + + // amal_a sends a reply + amal_a + .send_history_reply(MessageHistoryReply { + request_id: request_id.clone(), + url: "http://foo/bar".to_string(), + encryption_key: None, + }) + .await + .expect("send reply"); + + // check latest reply for amal_b + let latest_reply = amal_b.get_latest_history_reply().await; + assert!(latest_reply.is_ok()); + let received_reply = latest_reply.unwrap(); + assert!(received_reply.is_some()); + + let received_reply = received_reply.unwrap(); + assert_eq!(received_reply.request_id, request_id); + } + + #[tokio::test] + async fn test_reply_to_history_request() { + let wallet = generate_local_wallet(); + let mut amal_a = ClientBuilder::new_test_client(&wallet).await; + let amal_b = ClientBuilder::new_test_client(&wallet).await; + + // enable history sync for both clients + assert_ok!(amal_a.enable_history_sync().await); + assert_ok!(amal_b.enable_history_sync().await); + + // amal_b sends a history request + let (request_id, _pin_code) = amal_b + .send_history_request() + .await + .expect("history request"); + + // sync amal_a + amal_a.sync_welcomes().await.expect("sync_welcomes"); + + // start mock server + let options = mockito::ServerOpts { + host: HISTORY_SERVER_HOST, + port: HISTORY_SERVER_PORT + 3, + ..Default::default() + }; + let mut server = mockito::Server::new_with_opts_async(options).await; + + let _m = server + .mock("POST", "/upload") + .with_status(201) + .with_body("File uploaded") + .create(); + + let url = format!( + "http://{}:{}/", + HISTORY_SERVER_HOST, + HISTORY_SERVER_PORT + 3 + ); + amal_a.history_sync_url = Some(url); + + // amal_a replies to the history request + let reply = amal_a.reply_to_history_request().await; + assert!(reply.is_ok()); + let reply = reply.unwrap(); + + // verify the reply + assert_eq!(reply.request_id, request_id); + assert!(!reply.url.is_empty()); + assert!(reply.encryption_key.is_some()); + + // check if amal_b received the reply + let received_reply = amal_b.get_latest_history_reply().await; + assert!(received_reply.is_ok()); + let received_reply = received_reply.unwrap(); + assert!(received_reply.is_some()); + let received_reply = received_reply.unwrap(); + assert_eq!(received_reply.request_id, request_id); + assert_eq!(received_reply.url, reply.url); + assert_eq!(received_reply.encryption_key, reply.encryption_key); + + _m.assert_async().await; + server.reset(); + } + #[tokio::test] async fn test_insert_history_bundle() { let wallet = generate_local_wallet(); @@ -1023,6 +1419,38 @@ mod tests { assert!(inserted.is_ok()); } + #[tokio::test] + async fn test_externals_cant_join_sync_group() { + let wallet = generate_local_wallet(); + let amal = ClientBuilder::new_test_client(&wallet).await; + assert_ok!(amal.enable_history_sync().await); + amal.sync_welcomes().await.expect("sync welcomes"); + + let external_wallet = generate_local_wallet(); + let external_client = ClientBuilder::new_test_client(&external_wallet).await; + assert_ok!(external_client.enable_history_sync().await); + external_client + .sync_welcomes() + .await + .expect("sync welcomes"); + + let amal_sync_groups = amal + .store() + .conn() + .unwrap() + .find_sync_groups() + .expect("find sync groups"); + assert_eq!(amal_sync_groups.len(), 1); + + // try to join amal's sync group + let sync_group_id = amal_sync_groups[0].id.clone(); + let group = amal.group(sync_group_id).expect("get group"); + let result = group + .add_members(&external_client, vec![external_wallet.get_address()]) + .await; + assert!(result.is_err()); + } + #[test] fn test_new_pin() { let pin = new_pin(); @@ -1030,6 +1458,12 @@ mod tests { assert_eq!(pin.len(), 4); } + #[test] + fn test_new_request_id() { + let request_id = new_request_id(); + assert_eq!(request_id.len(), ENC_KEY_SIZE); + } + #[test] fn test_new_key() { let sig_key = HistoryKeyType::new_chacha20_poly1305_key(); diff --git a/xmtp_mls/src/groups/mod.rs b/xmtp_mls/src/groups/mod.rs index 895b4dec8..23b3fe21b 100644 --- a/xmtp_mls/src/groups/mod.rs +++ b/xmtp_mls/src/groups/mod.rs @@ -2,10 +2,11 @@ pub mod group_membership; pub mod group_metadata; pub mod group_mutable_metadata; pub mod group_permissions; -mod intents; +pub mod intents; pub mod members; #[allow(dead_code)] -mod message_history; +#[cfg(feature = "message-history")] +pub mod message_history; mod subscriptions; mod sync; pub mod validated_commit; @@ -31,9 +32,12 @@ use openmls::{ use openmls_traits::OpenMlsProvider; use prost::Message; use thiserror::Error; +use tokio::sync::Mutex; pub use self::group_permissions::PreconfiguredPolicies; pub use self::intents::{AddressesOrInstallationIds, IntentError}; +#[cfg(feature = "message-history")] +use self::message_history::MessageHistoryError; use self::{ group_membership::GroupMembership, group_metadata::extract_group_metadata, @@ -41,13 +45,15 @@ use self::{ group_permissions::{ extract_group_permissions, GroupMutablePermissions, GroupMutablePermissionsError, }, - intents::{AdminListActionType, UpdateAdminListIntentData, UpdateMetadataIntentData}, + intents::{ + AdminListActionType, PermissionPolicyOption, PermissionUpdateType, + UpdateAdminListIntentData, UpdateMetadataIntentData, UpdatePermissionIntentData, + }, validated_commit::extract_group_membership, }; use self::{ group_metadata::{ConversationType, GroupMetadata, GroupMetadataError}, group_permissions::PolicySet, - message_history::MessageHistoryError, validated_commit::CommitValidationError, }; use std::{collections::HashSet, sync::Arc}; @@ -69,14 +75,15 @@ use crate::{ client::{deserialize_welcome, ClientError, MessageProcessingError, XmtpMlsLocalContext}, configuration::{ CIPHERSUITE, GROUP_MEMBERSHIP_EXTENSION_ID, GROUP_PERMISSIONS_EXTENSION_ID, MAX_GROUP_SIZE, - MUTABLE_METADATA_EXTENSION_ID, + MAX_PAST_EPOCHS, MUTABLE_METADATA_EXTENSION_ID, + SEND_MESSAGE_UPDATE_INSTALLATIONS_INTERVAL_NS, }, hpke::{decrypt_welcome, HpkeError}, identity::{parse_credential, Identity, IdentityError}, identity_updates::{load_identity_updates, InstallationDiffError}, retry::RetryableError, - retryable, storage::{ + consent_record::{ConsentState, ConsentType, StoredConsentRecord}, db_connection::DbConnection, group::{GroupMembershipState, Purpose, StoredGroup}, group_intent::{IntentKind, NewGroupIntent}, @@ -105,23 +112,23 @@ pub enum GroupError { #[error("intent error: {0}")] Intent(#[from] IntentError), #[error("create message: {0}")] - CreateMessage(#[from] openmls::prelude::CreateMessageError), + CreateMessage(#[from] openmls::prelude::CreateMessageError), #[error("TLS Codec error: {0}")] TlsError(#[from] TlsCodecError), - #[error("No changes found in commit")] - NoChanges, + #[error("SequenceId not found in local db")] + MissingSequenceId, #[error("Addresses not found {0:?}")] AddressNotFound(Vec), #[error("add members: {0}")] UpdateGroupMembership( - #[from] openmls::prelude::UpdateGroupMembershipError, + #[from] openmls::prelude::UpdateGroupMembershipError, ), #[error("group create: {0}")] - GroupCreate(#[from] openmls::group::NewGroupError), + GroupCreate(#[from] openmls::group::NewGroupError), #[error("self update: {0}")] - SelfUpdate(#[from] openmls::group::SelfUpdateError), + SelfUpdate(#[from] openmls::group::SelfUpdateError), #[error("welcome error: {0}")] - WelcomeError(#[from] openmls::prelude::WelcomeError), + WelcomeError(#[from] openmls::prelude::WelcomeError), #[error("Invalid extension {0}")] InvalidExtension(#[from] openmls::prelude::InvalidExtensionError), #[error("Invalid signature: {0}")] @@ -158,30 +165,53 @@ pub enum GroupError { EncodeError(#[from] prost::EncodeError), #[error("create group context proposal error: {0}")] CreateGroupContextExtProposalError( - #[from] CreateGroupContextExtProposalError, + #[from] CreateGroupContextExtProposalError, ), #[error("Credential error")] CredentialError(#[from] BasicCredentialError), #[error("LeafNode error")] LeafNodeError(#[from] LibraryError), + #[cfg(feature = "message-history")] #[error("Message History error: {0}")] MessageHistory(#[from] Box), #[error("Installation diff error: {0}")] InstallationDiff(#[from] InstallationDiffError), #[error("PSKs are not support")] NoPSKSupport, + #[error("Metadata update must specify a metadata field")] + InvalidPermissionUpdate, + #[error("The intent publishing task was cancelled")] + PublishCancelled, + #[error("the publish failed to complete due to panic")] + PublishPanicked, + #[error("Missing metadata field {name}")] + MissingMetadataField { name: String }, + #[error("Message was processed but is missing")] + MissingMessage, + #[error("sql key store error: {0}")] + SqlKeyStore(#[from] sql_key_store::SqlKeyStoreError), + #[error("No pending commit found")] + MissingPendingCommit, + #[error("Sync failed to wait for intent")] + SyncFailedToWait, } impl RetryableError for GroupError { fn is_retryable(&self) -> bool { match self { - Self::Diesel(diesel) => retryable!(diesel), - Self::Storage(storage) => retryable!(storage), - Self::ReceiveError(msg) => retryable!(msg), - Self::UpdateGroupMembership(update) => retryable!(update), - Self::GroupCreate(group) => retryable!(group), - Self::SelfUpdate(update) => retryable!(update), - Self::WelcomeError(welcome) => retryable!(welcome), + Self::Api(api_error) => api_error.is_retryable(), + Self::Client(client_error) => client_error.is_retryable(), + Self::Diesel(diesel) => diesel.is_retryable(), + Self::Storage(storage) => storage.is_retryable(), + Self::ReceiveError(msg) => msg.is_retryable(), + Self::Hpke(hpke) => hpke.is_retryable(), + Self::Identity(identity) => identity.is_retryable(), + Self::UpdateGroupMembership(update) => update.is_retryable(), + Self::GroupCreate(group) => group.is_retryable(), + Self::SelfUpdate(update) => update.is_retryable(), + Self::WelcomeError(welcome) => welcome.is_retryable(), + Self::InstallationDiff(diff) => diff.is_retryable(), + Self::CreateGroupContextExtProposalError(create) => create.is_retryable(), _ => false, } } @@ -192,12 +222,15 @@ pub struct MlsGroup { pub group_id: Vec, pub created_at_ns: i64, context: Arc, + mutex: Arc>, } #[derive(Default)] pub struct GroupMetadataOptions { pub name: Option, pub image_url_square: Option, + pub description: Option, + pub pinned_frame_url: Option, } impl Clone for MlsGroup { @@ -206,6 +239,7 @@ impl Clone for MlsGroup { context: self.context.clone(), group_id: self.group_id.clone(), created_at_ns: self.created_at_ns, + mutex: self.mutex.clone(), } } } @@ -221,16 +255,27 @@ pub enum UpdateAdminListType { impl MlsGroup { // Creates a new group instance. Does not validate that the group exists in the DB pub fn new(context: Arc, group_id: Vec, created_at_ns: i64) -> Self { + let mut mutexes = context.mutexes.clone(); Self { context, - group_id, + group_id: group_id.clone(), created_at_ns, + mutex: mutexes.get_mutex(group_id), } } + /// Instantiate a new [`XmtpOpenMlsProvider`] pulling a connection from the database. + /// prefer to use an already-instantiated mls provider if possible. + pub fn mls_provider(&self) -> Result { + Ok(self.context.store.conn()?.into()) + } + // Load the stored MLS group from the OpenMLS provider's keystore #[tracing::instrument(level = "trace", skip_all)] - fn load_mls_group(&self, provider: impl OpenMlsProvider) -> Result { + pub(crate) fn load_mls_group( + &self, + provider: impl OpenMlsProvider, + ) -> Result { let mls_group = OpenMlsGroup::load(provider.storage(), &GroupId::from_slice(&self.group_id)) .map_err(|_| GroupError::GroupNotFound)? @@ -243,7 +288,7 @@ impl MlsGroup { pub fn create_and_insert( context: Arc, membership_state: GroupMembershipState, - permissions: Option, + permissions_policy_set: PolicySet, opts: GroupMetadataOptions, ) -> Result { let conn = context.store.conn()?; @@ -252,8 +297,7 @@ impl MlsGroup { build_protected_metadata_extension(&context.identity, Purpose::Conversation)?; let mutable_metadata = build_mutable_metadata_extension_default(&context.identity, opts)?; let group_membership = build_starting_group_membership_extension(context.inbox_id(), 0); - let mutable_permissions = - build_mutable_permissions_extension(permissions.unwrap_or_default().to_policy_set())?; + let mutable_permissions = build_mutable_permissions_extension(permissions_policy_set)?; let group_config = build_group_config( protected_metadata, mutable_metadata, @@ -280,11 +324,11 @@ impl MlsGroup { ); stored_group.store(provider.conn_ref())?; - Ok(Self::new( - context.clone(), - group_id, - stored_group.created_at_ns, - )) + let new_group = Self::new(context.clone(), group_id, stored_group.created_at_ns); + + // Consent state defaults to allowed when the user creates the group + new_group.update_consent_state(ConsentState::Allowed)?; + Ok(new_group) } // Create a group from a decrypted and decoded welcome message @@ -296,6 +340,7 @@ impl MlsGroup { added_by_inbox: String, welcome_id: i64, ) -> Result { + tracing::info!("Creating from welcome"); let mls_welcome = StagedWelcome::new_from_welcome(provider, &build_group_join_config(), welcome, None)?; @@ -325,7 +370,7 @@ impl MlsGroup { validate_initial_group_membership(client, provider.conn_ref(), &mls_group).await?; - let stored_group = provider.conn().insert_or_replace_group(to_store)?; + let stored_group = provider.conn_ref().insert_or_replace_group(to_store)?; Ok(Self::new( client.context.clone(), @@ -342,6 +387,7 @@ impl MlsGroup { encrypted_welcome_bytes: Vec, welcome_id: i64, ) -> Result { + tracing::info!("Trying to decrypt welcome"); let welcome_bytes = decrypt_welcome(provider, hpke_public_key, &encrypted_welcome_bytes)?; let welcome = deserialize_welcome(&welcome_bytes)?; @@ -352,6 +398,7 @@ impl MlsGroup { ProcessedWelcome::new_from_welcome(provider, &join_config, welcome.clone())?; let psks = processed_welcome.psks(); if !psks.is_empty() { + tracing::error!("No PSK support for welcome"); return Err(GroupError::NoPSKSupport); } let staged_welcome = processed_welcome.into_staged_welcome(provider, None)?; @@ -364,6 +411,7 @@ impl MlsGroup { Self::create_from_welcome(client, provider, welcome, inbox_id, welcome_id).await } + #[cfg(feature = "message-history")] pub(crate) fn create_and_insert_sync_group( context: Arc, ) -> Result { @@ -408,6 +456,7 @@ impl MlsGroup { )) } + /// Send a message on this users XMTP [`Client`]. pub async fn send_message( &self, message: &[u8], @@ -416,12 +465,87 @@ impl MlsGroup { where ApiClient: XmtpApi, { + let update_interval_ns = Some(SEND_MESSAGE_UPDATE_INSTALLATIONS_INTERVAL_NS); let conn = self.context.store.conn()?; self.pre_intent_hook(client).await?; + let provider = XmtpOpenMlsProvider::from(conn); + let message_id = self.prepare_message(message, provider.conn_ref(), |now| { + Self::into_envelope(message, now) + }); + + self.sync_until_last_intent_resolved(&provider, client) + .await?; + + // implicitly set group consent state to allowed + self.update_consent_state(ConsentState::Allowed)?; + + message_id + } + + /// Publish all unpublished messages + pub async fn publish_messages( + &self, + client: &Client, + ) -> Result<(), GroupError> + where + ApiClient: XmtpApi, + { + let conn = self.context.store.conn()?; + let provider = XmtpOpenMlsProvider::from(conn); + let update_interval_ns = Some(SEND_MESSAGE_UPDATE_INSTALLATIONS_INTERVAL_NS); + self.maybe_update_installations(&provider, update_interval_ns, client) + .await?; + self.sync_until_last_intent_resolved(&provider, client) + .await?; + + // implicitly set group consent state to allowed + self.update_consent_state(ConsentState::Allowed)?; + + Ok(()) + } + + /// Update group installations + pub async fn update_installations( + &self, + client: &Client, + ) -> Result<(), GroupError> + where + ApiClient: XmtpApi, + { + let conn = self.context.store.conn()?; + let provider = XmtpOpenMlsProvider::from(conn); + self.maybe_update_installations(&provider, Some(0), client) + .await?; + Ok(()) + } + + /// Send a message, optimistically returning the ID of the message before the result of a message publish. + pub fn send_message_optimistic(&self, message: &[u8]) -> Result, GroupError> { + let conn = self.context.store.conn()?; + let message_id = + self.prepare_message(message, &conn, |now| Self::into_envelope(message, now))?; + Ok(message_id) + } + /// Prepare a [`IntentKind::SendMessage`] intent, and [`StoredGroupMessage`] on this users XMTP [`Client`]. + /// + /// # Arguments + /// * message: UTF-8 or encoded message bytes + /// * conn: Connection to SQLite database + /// * envelope: closure that returns context-specific [`PlaintextEnvelope`]. Closure accepts + /// timestamp attached to intent & stored message. + fn prepare_message( + &self, + message: &[u8], + conn: &DbConnection, + envelope: F, + ) -> Result, GroupError> + where + F: FnOnce(i64) -> PlaintextEnvelope, + { let now = now_ns(); - let plain_envelope = Self::into_envelope(message, &now.to_string()); + let plain_envelope = envelope(now); let mut encoded_envelope = vec![]; plain_envelope .encode(&mut encoded_envelope) @@ -431,7 +555,7 @@ impl MlsGroup { let intent = NewGroupIntent::new(IntentKind::SendMessage, self.group_id.clone(), intent_data); - intent.store(&conn)?; + intent.store(conn)?; // store this unpublished message locally before sending let message_id = calculate_message_id(&self.group_id, message, &now.to_string()); @@ -445,20 +569,16 @@ impl MlsGroup { sender_inbox_id: self.context.inbox_id(), delivery_status: DeliveryStatus::Unpublished, }; - group_message.store(&conn)?; + group_message.store(conn)?; - // Skipping a full sync here and instead just firing and forgetting - if let Err(err) = self.publish_intents(conn, client).await { - log::error!("Send: error publishing intents: {:?}", err); - } Ok(message_id) } - fn into_envelope(encoded_msg: &[u8], idempotency_key: &str) -> PlaintextEnvelope { + fn into_envelope(encoded_msg: &[u8], idempotency_key: i64) -> PlaintextEnvelope { PlaintextEnvelope { content: Some(Content::V1(V1 { content: encoded_msg.to_vec(), - idempotency_key: idempotency_key.into(), + idempotency_key: idempotency_key.to_string(), })), } } @@ -507,7 +627,7 @@ impl MlsGroup { .get_inbox_ids(account_addresses.clone()) .await?; // get current number of users in group - let member_count = self.members()?.len(); + let member_count = self.members(client).await?.len(); if member_count + inbox_id_map.len() > MAX_GROUP_SIZE as usize { return Err(GroupError::UserLimitExceeded); } @@ -520,11 +640,11 @@ impl MlsGroup { missing_addresses.into_iter().cloned().cloned().collect(), )); } + self.add_members_by_inbox_id(client, inbox_id_map.into_values().collect()) .await } - // Before calling this function, please verify pre_intent_hook has been called. #[tracing::instrument(level = "trace", skip_all)] pub async fn add_members_by_inbox_id( @@ -532,10 +652,8 @@ impl MlsGroup { client: &Client, inbox_ids: Vec, ) -> Result<(), GroupError> { - let conn = client.store().conn()?; - let provider = client.mls_provider(conn); - self.pre_intent_hook(client).await?; + let provider = client.mls_provider()?; let intent_data = self .get_membership_update_intent(client, &provider, inbox_ids, vec![]) .await?; @@ -544,16 +662,19 @@ impl MlsGroup { // If some existing group member has an update, this will return an intent with changes // when we really should return an error if intent_data.is_empty() { - return Err(GroupError::NoChanges); + tracing::warn!("Member already added"); + return Ok(()); } - let intent = provider.conn().insert_group_intent(NewGroupIntent::new( - IntentKind::UpdateGroupMembership, - self.group_id.clone(), - intent_data.into(), - ))?; + let intent = provider + .conn_ref() + .insert_group_intent(NewGroupIntent::new( + IntentKind::UpdateGroupMembership, + self.group_id.clone(), + intent_data.into(), + ))?; - self.sync_until_intent_resolved(provider.conn(), intent.id, client) + self.sync_until_intent_resolved(&provider, intent.id, client) .await } @@ -574,9 +695,6 @@ impl MlsGroup { client: &Client, inbox_ids: Vec, ) -> Result<(), GroupError> { - let conn = client.store().conn()?; - let provider = client.mls_provider(conn); - self.pre_intent_hook(client).await?; let intent_data = self .get_membership_update_intent(client, &provider, vec![], inbox_ids) @@ -590,7 +708,7 @@ impl MlsGroup { intent_data.into(), ))?; - self.sync_until_intent_resolved(provider.conn(), intent.id, client) + self.sync_until_intent_resolved(&provider, intent.id, client) .await } @@ -614,12 +732,44 @@ impl MlsGroup { intent_data, ))?; - self.sync_until_intent_resolved(conn, intent.id, client) + self.sync_until_intent_resolved(&conn.into(), intent.id, client) .await } - pub fn group_name(&self) -> Result { - let mutable_metadata = self.mutable_metadata()?; + pub async fn update_permission_policy( + &self, + client: &Client, + permission_update_type: PermissionUpdateType, + permission_policy: PermissionPolicyOption, + metadata_field: Option, + ) -> Result<(), GroupError> { + let conn = client.store().conn()?; + + if permission_update_type == PermissionUpdateType::UpdateMetadata + && metadata_field.is_none() + { + return Err(GroupError::InvalidPermissionUpdate); + } + + let intent_data: Vec = UpdatePermissionIntentData::new( + permission_update_type, + permission_policy, + metadata_field.as_ref().map(|field| field.to_string()), + ) + .into(); + + let intent = conn.insert_group_intent(NewGroupIntent::new( + IntentKind::UpdatePermission, + self.group_id.clone(), + intent_data, + ))?; + + self.sync_until_intent_resolved(&conn.into(), intent.id, client) + .await + } + + pub fn group_name(&self, provider: impl OpenMlsProvider) -> Result { + let mutable_metadata = self.mutable_metadata(provider)?; match mutable_metadata .attributes .get(&MetadataField::GroupName.to_string()) @@ -631,6 +781,40 @@ impl MlsGroup { } } + pub async fn update_group_description( + &self, + client: &Client, + group_description: String, + ) -> Result<(), GroupError> + where + ApiClient: XmtpApi, + { + let conn = self.context.store.conn()?; + let intent_data: Vec = + UpdateMetadataIntentData::new_update_group_description(group_description).into(); + let intent = conn.insert_group_intent(NewGroupIntent::new( + IntentKind::MetadataUpdate, + self.group_id.clone(), + intent_data, + ))?; + + self.sync_until_intent_resolved(&conn.into(), intent.id, client) + .await + } + + pub fn group_description(&self, provider: impl OpenMlsProvider) -> Result { + let mutable_metadata = self.mutable_metadata(provider)?; + match mutable_metadata + .attributes + .get(&MetadataField::Description.to_string()) + { + Some(group_description) => Ok(group_description.clone()), + None => Err(GroupError::GroupMutableMetadata( + GroupMutableMetadataError::MissingExtension, + )), + } + } + pub async fn update_group_image_url_square( &self, client: &Client, @@ -640,7 +824,7 @@ impl MlsGroup { ApiClient: XmtpApi, { let conn = self.context.store.conn()?; - + self.pre_intent_hook(client).await?; let intent_data: Vec = UpdateMetadataIntentData::new_update_group_image_url_square(group_image_url_square) @@ -651,12 +835,15 @@ impl MlsGroup { intent_data, ))?; - self.sync_until_intent_resolved(conn, intent.id, client) + self.sync_until_intent_resolved(&conn.into(), intent.id, client) .await } - pub fn group_image_url_square(&self) -> Result { - let mutable_metadata = self.mutable_metadata()?; + pub fn group_image_url_square( + &self, + provider: impl OpenMlsProvider, + ) -> Result { + let mutable_metadata = self.mutable_metadata(provider)?; match mutable_metadata .attributes .get(&MetadataField::GroupImageUrlSquare.to_string()) @@ -668,23 +855,71 @@ impl MlsGroup { } } - pub fn admin_list(&self) -> Result, GroupError> { - let mutable_metadata = self.mutable_metadata()?; + pub async fn update_group_pinned_frame_url( + &self, + client: &Client, + pinned_frame_url: String, + ) -> Result<(), GroupError> + where + ApiClient: XmtpApi, + { + let conn = self.context.store.conn()?; + let intent_data: Vec = + UpdateMetadataIntentData::new_update_group_pinned_frame_url(pinned_frame_url).into(); + let intent = conn.insert_group_intent(NewGroupIntent::new( + IntentKind::MetadataUpdate, + self.group_id.clone(), + intent_data, + ))?; + + self.sync_until_intent_resolved(&conn.into(), intent.id, client) + .await + } + + pub fn group_pinned_frame_url( + &self, + provider: impl OpenMlsProvider, + ) -> Result { + let mutable_metadata = self.mutable_metadata(provider)?; + match mutable_metadata + .attributes + .get(&MetadataField::GroupPinnedFrameUrl.to_string()) + { + Some(pinned_frame_url) => Ok(pinned_frame_url.clone()), + None => Err(GroupError::GroupMutableMetadata( + GroupMutableMetadataError::MissingExtension, + )), + } + } + + pub fn admin_list(&self, provider: impl OpenMlsProvider) -> Result, GroupError> { + let mutable_metadata = self.mutable_metadata(provider)?; Ok(mutable_metadata.admin_list) } - pub fn super_admin_list(&self) -> Result, GroupError> { - let mutable_metadata = self.mutable_metadata()?; + pub fn super_admin_list( + &self, + provider: impl OpenMlsProvider, + ) -> Result, GroupError> { + let mutable_metadata = self.mutable_metadata(provider)?; Ok(mutable_metadata.super_admin_list) } - pub fn is_admin(&self, inbox_id: String) -> Result { - let mutable_metadata = self.mutable_metadata()?; + pub fn is_admin( + &self, + inbox_id: String, + provider: impl OpenMlsProvider, + ) -> Result { + let mutable_metadata = self.mutable_metadata(provider)?; Ok(mutable_metadata.admin_list.contains(&inbox_id)) } - pub fn is_super_admin(&self, inbox_id: String) -> Result { - let mutable_metadata = self.mutable_metadata()?; + pub fn is_super_admin( + &self, + inbox_id: String, + provider: impl OpenMlsProvider, + ) -> Result { + let mutable_metadata = self.mutable_metadata(provider)?; Ok(mutable_metadata.super_admin_list.contains(&inbox_id)) } @@ -713,7 +948,7 @@ impl MlsGroup { intent_data, ))?; - self.sync_until_intent_resolved(conn, intent.id, client) + self.sync_until_intent_resolved(&conn.into(), intent.id, client) .await } @@ -729,17 +964,43 @@ impl MlsGroup { }) } + /// Find the `consent_state` of the group + pub fn consent_state(&self) -> Result { + let conn = self.context.store.conn()?; + let record = + conn.get_consent_record(hex::encode(self.group_id.clone()), ConsentType::GroupId)?; + + match record { + Some(rec) => Ok(rec.state), + None => Ok(ConsentState::Unknown), + } + } + + pub fn update_consent_state(&self, state: ConsentState) -> Result<(), GroupError> { + let conn = self.context.store.conn()?; + conn.insert_or_replace_consent_records(vec![StoredConsentRecord::new( + ConsentType::GroupId, + state, + hex::encode(self.group_id.clone()), + )])?; + + Ok(()) + } + // Update this installation's leaf key in the group by creating a key update commit pub async fn key_update(&self, client: &Client) -> Result<(), GroupError> where ApiClient: XmtpApi, { let conn = self.context.store.conn()?; + let intent = conn.insert_group_intent(NewGroupIntent::new( + IntentKind::KeyUpdate, + self.group_id.clone(), + vec![], + ))?; - let intent = NewGroupIntent::new(IntentKind::KeyUpdate, self.group_id.clone(), vec![]); - intent.store(&conn)?; - - self.sync_with_conn(conn, client).await + self.sync_until_intent_resolved(&conn.into(), intent.id, client) + .await } /// Checking the last key rotation time before rotating the key. @@ -757,32 +1018,30 @@ impl MlsGroup { self.key_update(client).await?; conn.update_rotated_time_checked(self.group_id.clone())?; } - let update_interval = Some(5_000_000); - self.maybe_update_installations(conn.clone(), update_interval, client) + let provider = client.mls_provider(conn.clone()); + let update_interval_ns = Some(SEND_MESSAGE_UPDATE_INSTALLATIONS_INTERVAL_NS); + self.maybe_update_installations(&provider, update_interval_ns, client) .await?; + // TODO(rich) Use correct sync method self.sync_with_conn(conn, client).await } - pub fn is_active(&self) -> Result { - let conn = self.context.store.conn()?; - let provider = XmtpOpenMlsProvider::new(conn); - let mls_group = self.load_mls_group(&provider)?; + pub fn is_active(&self, provider: impl OpenMlsProvider) -> Result { + let mls_group = self.load_mls_group(provider)?; Ok(mls_group.is_active()) } - pub fn metadata(&self) -> Result { - let conn = self.context.store.conn()?; - let provider = XmtpOpenMlsProvider::new(conn); - let mls_group = self.load_mls_group(&provider)?; - + pub fn metadata(&self, provider: impl OpenMlsProvider) -> Result { + let mls_group = self.load_mls_group(provider)?; Ok(extract_group_metadata(&mls_group)?) } - pub fn mutable_metadata(&self) -> Result { - let conn = self.context.store.conn()?; - let provider = XmtpOpenMlsProvider::new(conn); - let mls_group = &self.load_mls_group(&provider)?; + pub fn mutable_metadata( + &self, + provider: impl OpenMlsProvider, + ) -> Result { + let mls_group = &self.load_mls_group(provider)?; Ok(mls_group.try_into()?) } @@ -849,7 +1108,7 @@ pub fn build_mutable_metadata_extension_default( } #[tracing::instrument(level = "trace", skip_all)] -pub fn build_mutable_metadata_extensions_for_metadata_update( +pub fn build_extensions_for_metadata_update( group: &OpenMlsGroup, field_name: String, field_value: String, @@ -871,7 +1130,75 @@ pub fn build_mutable_metadata_extensions_for_metadata_update( } #[tracing::instrument(level = "trace", skip_all)] -pub fn build_mutable_metadata_extensions_for_admin_lists_update( +pub fn build_extensions_for_permissions_update( + group: &OpenMlsGroup, + update_permissions_intent: UpdatePermissionIntentData, +) -> Result { + let existing_permissions: GroupMutablePermissions = group.try_into()?; + let existing_policy_set = existing_permissions.policies.clone(); + let new_policy_set = match update_permissions_intent.update_type { + PermissionUpdateType::AddMember => PolicySet::new( + update_permissions_intent.policy_option.into(), + existing_policy_set.remove_member_policy, + existing_policy_set.update_metadata_policy, + existing_policy_set.add_admin_policy, + existing_policy_set.remove_admin_policy, + existing_policy_set.update_permissions_policy, + ), + PermissionUpdateType::RemoveMember => PolicySet::new( + existing_policy_set.add_member_policy, + update_permissions_intent.policy_option.into(), + existing_policy_set.update_metadata_policy, + existing_policy_set.add_admin_policy, + existing_policy_set.remove_admin_policy, + existing_policy_set.update_permissions_policy, + ), + PermissionUpdateType::AddAdmin => PolicySet::new( + existing_policy_set.add_member_policy, + existing_policy_set.remove_member_policy, + existing_policy_set.update_metadata_policy, + update_permissions_intent.policy_option.into(), + existing_policy_set.remove_admin_policy, + existing_policy_set.update_permissions_policy, + ), + PermissionUpdateType::RemoveAdmin => PolicySet::new( + existing_policy_set.add_member_policy, + existing_policy_set.remove_member_policy, + existing_policy_set.update_metadata_policy, + existing_policy_set.add_admin_policy, + update_permissions_intent.policy_option.into(), + existing_policy_set.update_permissions_policy, + ), + PermissionUpdateType::UpdateMetadata => { + let mut metadata_policy = existing_policy_set.update_metadata_policy.clone(); + metadata_policy.insert( + update_permissions_intent.metadata_field_name.ok_or( + GroupError::MissingMetadataField { + name: "metadata_field_name".into(), + }, + )?, + update_permissions_intent.policy_option.into(), + ); + PolicySet::new( + existing_policy_set.add_member_policy, + existing_policy_set.remove_member_policy, + metadata_policy, + existing_policy_set.add_admin_policy, + existing_policy_set.remove_admin_policy, + existing_policy_set.update_permissions_policy, + ) + } + }; + let new_group_permissions: Vec = GroupMutablePermissions::new(new_policy_set).try_into()?; + let unknown_gc_extension = UnknownExtension(new_group_permissions); + let extension = Extension::Unknown(GROUP_PERMISSIONS_EXTENSION_ID, unknown_gc_extension); + let mut extensions = group.extensions().clone(); + extensions.add_or_replace(extension); + Ok(extensions) +} + +#[tracing::instrument(level = "trace", skip_all)] +pub fn build_extensions_for_admin_lists_update( group: &OpenMlsGroup, admin_lists_update: UpdateAdminListIntentData, ) -> Result { @@ -962,7 +1289,7 @@ fn build_group_config( .capabilities(capabilities) .ciphersuite(CIPHERSUITE) .wire_format_policy(WireFormatPolicy::default()) - .max_past_epochs(3) // Trying with 3 max past epochs for now + .max_past_epochs(MAX_PAST_EPOCHS) .use_ratchet_tree_extension(true) .build()) } @@ -972,6 +1299,7 @@ async fn validate_initial_group_membership( conn: &DbConnection, mls_group: &OpenMlsGroup, ) -> Result<(), GroupError> { + tracing::info!("Validating initial group membership"); let membership = extract_group_membership(mls_group.extensions())?; let needs_update = client.filter_inbox_ids_needing_updates(conn, membership.to_filters())?; if !needs_update.is_empty() { @@ -1003,45 +1331,51 @@ async fn validate_initial_group_membership( return Err(GroupError::InvalidGroupMembership); } + tracing::info!("Group membership validated"); Ok(()) } fn build_group_join_config() -> MlsGroupJoinConfig { MlsGroupJoinConfig::builder() .wire_format_policy(WireFormatPolicy::default()) - .max_past_epochs(3) // Trying with 3 max past epochs for now + .max_past_epochs(MAX_PAST_EPOCHS) .use_ratchet_tree_extension(true) .build() } #[cfg(test)] mod tests { - use openmls::prelude::{tls_codec::Serialize, Member, MlsGroup as OpenMlsGroup}; + use crate::groups::GroupMessageVersion; + use diesel::connection::SimpleConnection; + use futures::future::join_all; use openmls::prelude::tls_codec::Deserialize; - use openmls::prelude::MlsMessageIn; use openmls::prelude::MlsMessageBodyIn; + use openmls::prelude::MlsMessageIn; use openmls::prelude::ProcessedMessageContent; - use crate::groups::GroupMessageVersion; + use openmls::prelude::{tls_codec::Serialize, Member, MlsGroup as OpenMlsGroup}; use prost::Message; - use tracing_test::traced_test; + use std::sync::Arc; use xmtp_cryptography::utils::generate_local_wallet; - use xmtp_proto::xmtp::mls::message_contents::EncodedContent; use xmtp_proto::xmtp::mls::api::v1::GroupMessage; + use xmtp_proto::xmtp::mls::message_contents::EncodedContent; use crate::{ - assert_logged, + assert_err, assert_logged, builder::ClientBuilder, + client::MessageProcessingError, codecs::{group_updated::GroupUpdatedCodec, ContentCodec}, groups::{ build_group_membership_extension, group_membership::GroupMembership, group_metadata::{ConversationType, GroupMetadata}, group_mutable_metadata::MetadataField, + intents::{PermissionPolicyOption, PermissionUpdateType}, members::{GroupMember, PermissionLevel}, - GroupMetadataOptions, PreconfiguredPolicies, UpdateAdminListType, + DeliveryStatus, GroupMetadataOptions, PreconfiguredPolicies, UpdateAdminListType, }, storage::{ - group_intent::IntentState, + consent_record::ConsentState, + group_intent::{IntentKind, IntentState, NewGroupIntent}, group_message::{GroupMessageKind, StoredGroupMessage}, }, xmtp_openmls_provider::XmtpOpenMlsProvider, @@ -1050,7 +1384,7 @@ mod tests { use super::{ intents::{Installation, SendWelcomesAction}, - MlsGroup, + GroupError, MlsGroup, }; async fn receive_group_invite(client: &Client) -> MlsGroup @@ -1072,7 +1406,6 @@ mod tests { { group.sync(client).await.unwrap(); let mut messages = group.find_messages(None, None, None, None, None).unwrap(); - messages.pop().unwrap() } @@ -1085,8 +1418,7 @@ mod tests { sender_mls_group: &mut OpenMlsGroup, sender_provider: &XmtpOpenMlsProvider, ) { - let new_member_provider = - new_member_client.mls_provider(new_member_client.store().conn().unwrap()); + let new_member_provider = new_member_client.mls_provider().unwrap(); let key_package = new_member_client .identity() @@ -1154,7 +1486,7 @@ mod tests { .expect("send message"); group - .receive(&client.store().conn().unwrap(), &client) + .receive(&client.store().conn().unwrap().into(), &client) .await .unwrap(); // Check for messages @@ -1217,12 +1549,14 @@ mod tests { bola_group.sync(&bola).await.unwrap(); // Verify bola can see the group name - let bola_group_name = bola_group.group_name().unwrap(); + let bola_group_name = bola_group + .group_name(bola_group.mls_provider().unwrap()) + .unwrap(); assert_eq!(bola_group_name, ""); // Check if both clients can see the members correctly - let amal_members: Vec = amal_group.members().unwrap(); - let bola_members: Vec = bola_group.members().unwrap(); + let amal_members: Vec = amal_group.members(&amal).await.unwrap(); + let bola_members: Vec = bola_group.members(&bola).await.unwrap(); assert_eq!(amal_members.len(), 2); assert_eq!(bola_members.len(), 2); @@ -1264,93 +1598,116 @@ mod tests { // Get bola's version of the same group let bola_groups = bola.sync_welcomes().await.unwrap(); let bola_group = bola_groups.first().unwrap(); + bola_group.sync(&bola).await.unwrap(); - log::info!("Adding charlie from amal"); + tracing::info!("Adding charlie from amal"); // Have amal and bola both invite charlie. amal_group .add_members_by_inbox_id(&amal, vec![charlie.inbox_id()]) .await .expect("failed to add charlie"); - log::info!("Adding charlie from bola"); + tracing::info!("Adding charlie from bola"); bola_group .add_members_by_inbox_id(&bola, vec![charlie.inbox_id()]) .await - .expect_err("expected err"); + .expect("bola's add should succeed in a no-op"); amal_group - .receive(&amal.store().conn().unwrap(), &amal) + .receive(&amal.store().conn().unwrap().into(), &amal) .await .expect_err("expected error"); // Check Amal's MLS group state. - let amal_db = amal.context.store.conn().unwrap(); - let amal_mls_group = amal_group - .load_mls_group(&amal.mls_provider(amal_db.clone())) - .unwrap(); + let amal_db = XmtpOpenMlsProvider::from(amal.context.store.conn().unwrap()); + let amal_mls_group = amal_group.load_mls_group(&amal_db).unwrap(); let amal_members: Vec = amal_mls_group.members().collect(); assert_eq!(amal_members.len(), 3); // Check Bola's MLS group state. - let bola_db = bola.context.store.conn().unwrap(); - let bola_mls_group = bola_group - .load_mls_group(&bola.mls_provider(bola_db.clone())) - .unwrap(); + let bola_db = XmtpOpenMlsProvider::from(bola.context.store.conn().unwrap()); + let bola_mls_group = bola_group.load_mls_group(&bola_db).unwrap(); let bola_members: Vec = bola_mls_group.members().collect(); assert_eq!(bola_members.len(), 3); let amal_uncommitted_intents = amal_db + .conn_ref() .find_group_intents( amal_group.group_id.clone(), - Some(vec![IntentState::ToPublish, IntentState::Published]), + Some(vec![ + IntentState::ToPublish, + IntentState::Published, + IntentState::Error, + ]), None, ) .unwrap(); assert_eq!(amal_uncommitted_intents.len(), 0); - let bola_uncommitted_intents = bola_db + let bola_failed_intents = bola_db + .conn_ref() .find_group_intents( bola_group.group_id.clone(), - Some(vec![IntentState::ToPublish, IntentState::Published]), + Some(vec![IntentState::Error]), None, ) .unwrap(); - // Bola should have one uncommitted intent for the failed attempt at adding Charlie, who is already in the group - assert_eq!(bola_uncommitted_intents.len(), 1); - } - - #[tokio::test(flavor = "multi_thread", worker_threads = 1)] - #[traced_test] - async fn test_create_from_welcome_validation() { - let alix = ClientBuilder::new_test_client(&generate_local_wallet()).await; - let bo = ClientBuilder::new_test_client(&generate_local_wallet()).await; + // Bola's attempted add should be deleted, since it will have been a no-op on the second try + assert_eq!(bola_failed_intents.len(), 0); - let alix_group: MlsGroup = alix - .create_group(None, GroupMetadataOptions::default()) + // Make sure sending and receiving both worked + amal_group + .send_message("hello from amal".as_bytes(), &amal) + .await .unwrap(); - let provider = alix.mls_provider(alix.store().conn().unwrap()); - // Doctor the group membership - let mut mls_group = alix_group.load_mls_group(&provider).unwrap(); - let mut existing_extensions = mls_group.extensions().clone(); - let mut group_membership = GroupMembership::new(); - group_membership.add("foo".to_string(), 1); - existing_extensions.add_or_replace(build_group_membership_extension(&group_membership)); - mls_group - .update_group_context_extensions( - &provider, - existing_extensions.clone(), - &alix.identity().installation_keys, - ) + bola_group + .send_message("hello from bola".as_bytes(), &bola) + .await .unwrap(); - mls_group.merge_pending_commit(&provider).unwrap(); - - // Now add bo to the group - force_add_member(&alix, &bo, &alix_group, &mut mls_group, &provider).await; - // Bo should not be able to actually read this group - bo.sync_welcomes().await.unwrap(); - let groups = bo.find_groups(None, None, None, None).unwrap(); - assert_eq!(groups.len(), 0); - assert_logged!("failed to create group from welcome", 1); + let bola_messages = bola_group + .find_messages(None, None, None, None, None) + .unwrap(); + let matching_message = bola_messages + .iter() + .find(|m| m.decrypted_message_bytes == "hello from amal".as_bytes()); + tracing::info!("found message: {:?}", bola_messages); + assert!(matching_message.is_some()); + } + + #[test] + fn test_create_from_welcome_validation() { + crate::traced_test(|| async { + let alix = ClientBuilder::new_test_client(&generate_local_wallet()).await; + let bo = ClientBuilder::new_test_client(&generate_local_wallet()).await; + + let alix_group: MlsGroup = alix + .create_group(None, GroupMetadataOptions::default()) + .unwrap(); + let provider = alix.mls_provider().unwrap(); + // Doctor the group membership + let mut mls_group = alix_group.load_mls_group(&provider).unwrap(); + let mut existing_extensions = mls_group.extensions().clone(); + let mut group_membership = GroupMembership::new(); + group_membership.add("foo".to_string(), 1); + existing_extensions.add_or_replace(build_group_membership_extension(&group_membership)); + mls_group + .update_group_context_extensions( + &provider, + existing_extensions.clone(), + &alix.identity().installation_keys, + ) + .unwrap(); + mls_group.merge_pending_commit(&provider).unwrap(); + + // Now add bo to the group + force_add_member(&alix, &bo, &alix_group, &mut mls_group, &provider).await; + + // Bo should not be able to actually read this group + bo.sync_welcomes().await.unwrap(); + let groups = bo.find_groups(None, None, None, None).unwrap(); + assert_eq!(groups.len(), 0); + assert_logged!("failed to create group from welcome", 1); + }); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] @@ -1465,8 +1822,7 @@ mod tests { .unwrap(); assert_eq!(messages.len(), 4); - let conn = &client.context.store.conn().unwrap(); - let provider = super::XmtpOpenMlsProvider::new(conn.clone()); + let provider: XmtpOpenMlsProvider = client.context.store.conn().unwrap().into(); let mls_group = group.load_mls_group(&provider).unwrap(); let pending_commit = mls_group.pending_commit(); assert!(pending_commit.is_none()); @@ -1492,11 +1848,16 @@ mod tests { let client_b = ClientBuilder::new_test_client(&generate_local_wallet()).await; // client A makes a group with client B. - let group = client_a.create_group(None, GroupMetadataOptions::default()).expect("create group"); - let mut messages = client_a.api_client.query_group_messages(group.group_id.clone(), None).await.unwrap(); + let group = client_a + .create_group(None, GroupMetadataOptions::default()) + .expect("create group"); + let mut messages = client_a + .api_client + .query_group_messages(group.group_id.clone(), None) + .await + .unwrap(); assert_eq!(messages.len(), 0); - group .add_members_by_inbox_id(&client_a, vec![client_b.inbox_id()]) .await @@ -1507,57 +1868,73 @@ mod tests { client_b_group.sync(&client_b).await.unwrap(); // verify no new payloads on client A. - messages = client_a.api_client.query_group_messages(group.group_id.clone(), None).await.unwrap(); + messages = client_a + .api_client + .query_group_messages(group.group_id.clone(), None) + .await + .unwrap(); assert_eq!(messages.len(), 3); - // call pre_intent_hook on client B. + // call pre_intent_hook on client B. client_b_group.pre_intent_hook(&client_b).await.unwrap(); - + // Verify client A receives a key rotation payload - messages = client_b.api_client.query_group_messages(group.group_id.clone(), None).await.unwrap(); - assert_eq!(messages.len(),4); - - // steps to get the leaf node of the updated path. - let first_message = &messages[messages.len()-1]; + messages = client_b + .api_client + .query_group_messages(group.group_id.clone(), None) + .await + .unwrap(); + assert_eq!(messages.len(), 4); + + // steps to get the leaf node of the updated path. + let first_message = &messages[messages.len() - 1]; let msgv1 = match &first_message.version { Some(GroupMessageVersion::V1(value)) => value, _ => panic!("error msgv1"), }; - + let mls_message_in = MlsMessageIn::tls_deserialize_exact(&msgv1.data).unwrap(); let mls_message = match mls_message_in.extract() { - MlsMessageBodyIn::PrivateMessage(mls_message) => mls_message, - _ => panic!("error mls_message"), + MlsMessageBodyIn::PrivateMessage(mls_message) => mls_message, + _ => panic!("error mls_message"), }; let conn = &client_a.context.store.conn().unwrap(); let provider = client_a.mls_provider(conn.clone()); let mut openmls_group = group.load_mls_group(&provider).unwrap(); - let decrypted_message = openmls_group.process_message(&provider, mls_message).unwrap(); + let decrypted_message = openmls_group + .process_message(&provider, mls_message) + .unwrap(); - let staged_commit = match decrypted_message.into_content(){ + let staged_commit = match decrypted_message.into_content() { ProcessedMessageContent::StagedCommitMessage(staged_commit) => *staged_commit, _ => panic!("error staged_commit"), }; - // check there is indeed some updated leaf node, which means the key update works. + // check there is indeed some updated leaf node, which means the key update works. let path_update_leaf_node = staged_commit.update_path_leaf_node(); assert!(path_update_leaf_node.is_some()); - // call pre_intent_hook on client B again, client A receives nothing new. + // call pre_intent_hook on client B again, client A receives nothing new. client_b_group.pre_intent_hook(&client_b).await.unwrap(); - messages = client_b.api_client.query_group_messages(group.group_id.clone(), None).await.unwrap(); - assert_eq!(messages.len(),4); + messages = client_b + .api_client + .query_group_messages(group.group_id.clone(), None) + .await + .unwrap(); + assert_eq!(messages.len(), 4); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] - async fn test_send_message_with_pre_intent_hook(){ + async fn test_send_message_with_pre_intent_hook() { let client_a = ClientBuilder::new_test_client(&generate_local_wallet()).await; let client_b = ClientBuilder::new_test_client(&generate_local_wallet()).await; // client A makes a group with client B. - let group = client_a.create_group(None, GroupMetadataOptions::default()).expect("create group"); + let group = client_a + .create_group(None, GroupMetadataOptions::default()) + .expect("create group"); group .add_members_by_inbox_id(&client_a, vec![client_b.inbox_id()]) .await @@ -1566,21 +1943,32 @@ mod tests { // client B creates it from welcome let client_b_group = receive_group_invite(&client_b).await; client_b_group.sync(&client_b).await.unwrap(); - + // Verify no new payloads on client A - let mut messages = client_a.api_client.query_group_messages(group.group_id.clone(), None).await.unwrap(); + let mut messages = client_a + .api_client + .query_group_messages(group.group_id.clone(), None) + .await + .unwrap(); assert_eq!(messages.len(), 3); // Client B sends a message to Client A let b_message = b"hello from client b"; - client_b_group.send_message(b_message, &client_b).await.expect("send message"); - - // Verify client A receives a key rotation. - messages = client_b.api_client.query_group_messages(group.group_id.clone(), None).await.unwrap(); + client_b_group + .send_message(b_message, &client_b) + .await + .expect("send message"); + + // Verify client A receives a key rotation. + messages = client_b + .api_client + .query_group_messages(group.group_id.clone(), None) + .await + .unwrap(); assert_eq!(messages.len(), 5); - // Steps to get the leaf node of the updated path. - let queried_message = &messages[messages.len()-2]; + // Steps to get the leaf node of the updated path. + let queried_message = &messages[messages.len() - 2]; let msgv1 = match &queried_message.version { Some(GroupMessageVersion::V1(value)) => value, @@ -1596,9 +1984,11 @@ mod tests { let conn = &client_a.context.store.conn().unwrap(); let provider = client_a.mls_provider(conn.clone()); let mut openmls_group = group.load_mls_group(&provider).unwrap(); - let decrypted_message = openmls_group.process_message(&provider, mls_message).unwrap(); + let decrypted_message = openmls_group + .process_message(&provider, mls_message) + .unwrap(); - let staged_commit = match decrypted_message.into_content(){ + let staged_commit = match decrypted_message.into_content() { ProcessedMessageContent::StagedCommitMessage(staged_commit) => *staged_commit, _ => panic!("error staged_commit"), }; @@ -1607,10 +1997,9 @@ mod tests { let path_update_leaf_node = staged_commit.update_path_leaf_node(); assert!(path_update_leaf_node.is_some()); - // Verify client A receives the message. + // Verify client A receives the message. let message = get_latest_message(&group, &client_a).await; assert_eq!(message.decrypted_message_bytes, b_message); - } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] @@ -1654,8 +2043,8 @@ mod tests { ) .await .unwrap(); - log::info!("created the group with 2 additional members"); - assert_eq!(group.members().unwrap().len(), 3); + tracing::info!("created the group with 2 additional members"); + assert_eq!(group.members(&bola).await.unwrap().len(), 3); let messages = group.find_messages(None, None, None, None, None).unwrap(); assert_eq!(messages.len(), 1); assert_eq!(messages[0].kind, GroupMessageKind::MembershipChange); @@ -1669,8 +2058,8 @@ mod tests { .remove_members(&amal, vec![bola_wallet.get_address()]) .await .unwrap(); - assert_eq!(group.members().unwrap().len(), 2); - log::info!("removed bola"); + assert_eq!(group.members(&bola).await.unwrap().len(), 2); + tracing::info!("removed bola"); let messages = group.find_messages(None, None, None, None, None).unwrap(); assert_eq!(messages.len(), 2); assert_eq!(messages[1].kind, GroupMessageKind::MembershipChange); @@ -1682,13 +2071,77 @@ mod tests { let bola_group = receive_group_invite(&bola).await; bola_group.sync(&bola).await.unwrap(); - assert!(!bola_group.is_active().unwrap()) + assert!(!bola_group + .is_active(bola_group.mls_provider().unwrap()) + .unwrap()) } - // TODO:nm add more tests for filling in missing installations - #[tokio::test(flavor = "multi_thread", worker_threads = 1)] - async fn test_add_missing_installations() { + async fn test_removed_members_cannot_send_message_to_others() { + let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; + let bola_wallet = &generate_local_wallet(); + let bola = ClientBuilder::new_test_client(bola_wallet).await; + let charlie_wallet = &generate_local_wallet(); + let charlie = ClientBuilder::new_test_client(charlie_wallet).await; + + let group = amal + .create_group(None, GroupMetadataOptions::default()) + .unwrap(); + group + .add_members( + &amal, + vec![bola_wallet.get_address(), charlie_wallet.get_address()], + ) + .await + .unwrap(); + assert_eq!(group.members(&bola).await.unwrap().len(), 3); + + group + .remove_members(&amal, vec![bola_wallet.get_address()]) + .await + .unwrap(); + assert_eq!(group.members(&bola).await.unwrap().len(), 2); + assert!(group + .members(&bola) + .await + .unwrap() + .iter() + .all(|m| m.inbox_id != bola.inbox_id())); + assert!(group + .members(&bola) + .await + .unwrap() + .iter() + .any(|m| m.inbox_id == charlie.inbox_id())); + + group.sync(&amal).await.expect("sync failed"); + + let message_text = b"hello"; + group + .send_message(message_text, &bola) + .await + .expect_err("expected send_message to fail"); + + group.sync(&bola).await.expect("sync failed"); + group.sync(&amal).await.expect("sync failed"); + + let amal_messages = group + .find_messages(Some(GroupMessageKind::Application), None, None, None, None) + .unwrap() + .into_iter() + .collect::>(); + + let message = amal_messages.first().unwrap(); + + // FIXME:st this is passing ONLY because the message IS being sent to the group + assert_eq!(message_text, &message.decrypted_message_bytes[..]); + assert_eq!(amal_messages.len(), 1); + } + + // TODO:nm add more tests for filling in missing installations + + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn test_add_missing_installations() { // Setup for test let amal_wallet = generate_local_wallet(); let amal = ClientBuilder::new_test_client(&amal_wallet).await; @@ -1702,10 +2155,9 @@ mod tests { .await .unwrap(); - assert_eq!(group.members().unwrap().len(), 2); + assert_eq!(group.members(&amal).await.unwrap().len(), 2); - let conn = &amal.context.store.conn().unwrap(); - let provider = super::XmtpOpenMlsProvider::new(conn.clone()); + let provider: XmtpOpenMlsProvider = amal.context.store.conn().unwrap().into(); // Finished with setup // add a second installation for amal using the same wallet @@ -1779,7 +2231,7 @@ mod tests { let amal_group = amal .create_group( - Some(PreconfiguredPolicies::AdminsOnly), + Some(PreconfiguredPolicies::AdminsOnly.to_policy_set()), GroupMetadataOptions::default(), ) .unwrap(); @@ -1807,11 +2259,15 @@ mod tests { GroupMetadataOptions { name: Some("Group Name".to_string()), image_url_square: Some("url".to_string()), + description: Some("group description".to_string()), + pinned_frame_url: Some("pinned frame".to_string()), }, ) .unwrap(); - let binding = amal_group.mutable_metadata().expect("msg"); + let binding = amal_group + .mutable_metadata(amal_group.mls_provider().unwrap()) + .expect("msg"); let amal_group_name: &String = binding .attributes .get(&MetadataField::GroupName.to_string()) @@ -1820,9 +2276,19 @@ mod tests { .attributes .get(&MetadataField::GroupImageUrlSquare.to_string()) .unwrap(); + let amal_group_description: &String = binding + .attributes + .get(&MetadataField::Description.to_string()) + .unwrap(); + let amal_group_pinned_frame_url: &String = binding + .attributes + .get(&MetadataField::GroupPinnedFrameUrl.to_string()) + .unwrap(); assert_eq!(amal_group_name, "Group Name"); assert_eq!(amal_group_image_url, "url"); + assert_eq!(amal_group_description, "group description"); + assert_eq!(amal_group_pinned_frame_url, "pinned frame"); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] @@ -1833,7 +2299,7 @@ mod tests { let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; let amal_group = amal .create_group( - Some(PreconfiguredPolicies::AdminsOnly), + Some(PreconfiguredPolicies::AdminsOnly.to_policy_set()), GroupMetadataOptions::default(), ) .unwrap(); @@ -1858,14 +2324,16 @@ mod tests { let bola = ClientBuilder::new_test_client(&generate_local_wallet()).await; // Create a group and verify it has the default group name - let policies = Some(PreconfiguredPolicies::AdminsOnly); + let policy_set = Some(PreconfiguredPolicies::AdminsOnly.to_policy_set()); let amal_group: MlsGroup = amal - .create_group(policies, GroupMetadataOptions::default()) + .create_group(policy_set, GroupMetadataOptions::default()) .unwrap(); amal_group.sync(&amal).await.unwrap(); - let group_mutable_metadata = amal_group.mutable_metadata().unwrap(); - assert!(group_mutable_metadata.attributes.len().eq(&3)); + let group_mutable_metadata = amal_group + .mutable_metadata(amal_group.mls_provider().unwrap()) + .unwrap(); + assert!(group_mutable_metadata.attributes.len().eq(&4)); assert!(group_mutable_metadata .attributes .get(&MetadataField::GroupName.to_string()) @@ -1882,7 +2350,9 @@ mod tests { assert_eq!(bola_groups.len(), 1); let bola_group = bola_groups.first().unwrap(); bola_group.sync(&bola).await.unwrap(); - let group_mutable_metadata = bola_group.mutable_metadata().unwrap(); + let group_mutable_metadata = bola_group + .mutable_metadata(bola_group.mls_provider().unwrap()) + .unwrap(); assert!(group_mutable_metadata .attributes .get(&MetadataField::GroupName.to_string()) @@ -1902,7 +2372,9 @@ mod tests { // Verify amal group sees update amal_group.sync(&amal).await.unwrap(); - let binding = amal_group.mutable_metadata().expect("msg"); + let binding = amal_group + .mutable_metadata(amal_group.mls_provider().unwrap()) + .expect("msg"); let amal_group_name: &String = binding .attributes .get(&MetadataField::GroupName.to_string()) @@ -1911,7 +2383,9 @@ mod tests { // Verify bola group sees update bola_group.sync(&bola).await.unwrap(); - let binding = bola_group.mutable_metadata().expect("msg"); + let binding = bola_group + .mutable_metadata(bola_group.mls_provider().unwrap()) + .expect("msg"); let bola_group_name: &String = binding .attributes .get(&MetadataField::GroupName.to_string()) @@ -1926,7 +2400,9 @@ mod tests { // Verify bola group does not see an update bola_group.sync(&bola).await.unwrap(); - let binding = bola_group.mutable_metadata().expect("msg"); + let binding = bola_group + .mutable_metadata(bola_group.mls_provider().unwrap()) + .expect("msg"); let bola_group_name: &String = binding .attributes .get(&MetadataField::GroupName.to_string()) @@ -1939,13 +2415,15 @@ mod tests { let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; // Create a group and verify it has the default group name - let policies = Some(PreconfiguredPolicies::AdminsOnly); + let policy_set = Some(PreconfiguredPolicies::AdminsOnly.to_policy_set()); let amal_group: MlsGroup = amal - .create_group(policies, GroupMetadataOptions::default()) + .create_group(policy_set, GroupMetadataOptions::default()) .unwrap(); amal_group.sync(&amal).await.unwrap(); - let group_mutable_metadata = amal_group.mutable_metadata().unwrap(); + let group_mutable_metadata = amal_group + .mutable_metadata(amal_group.mls_provider().unwrap()) + .unwrap(); assert!(group_mutable_metadata .attributes .get(&MetadataField::GroupImageUrlSquare.to_string()) @@ -1960,7 +2438,9 @@ mod tests { // Verify amal group sees update amal_group.sync(&amal).await.unwrap(); - let binding = amal_group.mutable_metadata().expect("msg"); + let binding = amal_group + .mutable_metadata(amal_group.mls_provider().unwrap()) + .expect("msg"); let amal_group_image_url: &String = binding .attributes .get(&MetadataField::GroupImageUrlSquare.to_string()) @@ -1968,6 +2448,44 @@ mod tests { assert_eq!(amal_group_image_url, "a url"); } + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] + async fn test_update_group_pinned_frame_url() { + let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; + + // Create a group and verify it has the default group name + let policy_set = Some(PreconfiguredPolicies::AdminsOnly.to_policy_set()); + let amal_group: MlsGroup = amal + .create_group(policy_set, GroupMetadataOptions::default()) + .unwrap(); + amal_group.sync(&amal).await.unwrap(); + + let group_mutable_metadata = amal_group + .mutable_metadata(amal_group.mls_provider().unwrap()) + .unwrap(); + assert!(group_mutable_metadata + .attributes + .get(&MetadataField::GroupPinnedFrameUrl.to_string()) + .unwrap() + .eq("")); + + // Update group name + amal_group + .update_group_pinned_frame_url(&amal, "a frame url".to_string()) + .await + .unwrap(); + + // Verify amal group sees update + amal_group.sync(&amal).await.unwrap(); + let binding = amal_group + .mutable_metadata(amal_group.mls_provider().unwrap()) + .expect("msg"); + let amal_group_pinned_frame_url: &String = binding + .attributes + .get(&MetadataField::GroupPinnedFrameUrl.to_string()) + .unwrap(); + assert_eq!(amal_group_pinned_frame_url, "a frame url"); + } + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn test_group_mutable_data_group_permissions() { let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; @@ -1975,13 +2493,15 @@ mod tests { let bola = ClientBuilder::new_test_client(&bola_wallet).await; // Create a group and verify it has the default group name - let policies = Some(PreconfiguredPolicies::AllMembers); + let policy_set = Some(PreconfiguredPolicies::AllMembers.to_policy_set()); let amal_group: MlsGroup = amal - .create_group(policies, GroupMetadataOptions::default()) + .create_group(policy_set, GroupMetadataOptions::default()) .unwrap(); amal_group.sync(&amal).await.unwrap(); - let group_mutable_metadata = amal_group.mutable_metadata().unwrap(); + let group_mutable_metadata = amal_group + .mutable_metadata(amal_group.mls_provider().unwrap()) + .unwrap(); assert!(group_mutable_metadata .attributes .get(&MetadataField::GroupName.to_string()) @@ -1998,7 +2518,9 @@ mod tests { assert_eq!(bola_groups.len(), 1); let bola_group = bola_groups.first().unwrap(); bola_group.sync(&bola).await.unwrap(); - let group_mutable_metadata = bola_group.mutable_metadata().unwrap(); + let group_mutable_metadata = bola_group + .mutable_metadata(bola_group.mls_provider().unwrap()) + .unwrap(); assert!(group_mutable_metadata .attributes .get(&MetadataField::GroupName.to_string()) @@ -2013,7 +2535,9 @@ mod tests { // Verify amal group sees update amal_group.sync(&amal).await.unwrap(); - let binding = amal_group.mutable_metadata().unwrap(); + let binding = amal_group + .mutable_metadata(amal_group.mls_provider().unwrap()) + .unwrap(); let amal_group_name: &String = binding .attributes .get(&MetadataField::GroupName.to_string()) @@ -2022,7 +2546,9 @@ mod tests { // Verify bola group sees update bola_group.sync(&bola).await.unwrap(); - let binding = bola_group.mutable_metadata().expect("msg"); + let binding = bola_group + .mutable_metadata(bola_group.mls_provider().unwrap()) + .expect("msg"); let bola_group_name: &String = binding .attributes .get(&MetadataField::GroupName.to_string()) @@ -2037,7 +2563,9 @@ mod tests { // Verify amal group sees an update amal_group.sync(&amal).await.unwrap(); - let binding = amal_group.mutable_metadata().expect("msg"); + let binding = amal_group + .mutable_metadata(amal_group.mls_provider().unwrap()) + .expect("msg"); let amal_group_name: &String = binding .attributes .get(&MetadataField::GroupName.to_string()) @@ -2053,9 +2581,9 @@ mod tests { let caro = ClientBuilder::new_test_client(&generate_local_wallet()).await; let charlie = ClientBuilder::new_test_client(&generate_local_wallet()).await; - let policies = Some(PreconfiguredPolicies::AdminsOnly); + let policy_set = Some(PreconfiguredPolicies::AdminsOnly.to_policy_set()); let amal_group = amal - .create_group(policies, GroupMetadataOptions::default()) + .create_group(policy_set, GroupMetadataOptions::default()) .unwrap(); amal_group.sync(&amal).await.unwrap(); @@ -2071,10 +2599,11 @@ mod tests { bola_group.sync(&bola).await.unwrap(); // Verify Amal is the only admin and super admin - let admin_list = amal_group.admin_list().unwrap(); - let super_admin_list = amal_group.super_admin_list().unwrap(); - assert_eq!(admin_list.len(), 1); - assert!(admin_list.contains(&amal.inbox_id())); + let provider = amal_group.mls_provider().unwrap(); + let admin_list = amal_group.admin_list(&provider).unwrap(); + let super_admin_list = amal_group.super_admin_list(&provider).unwrap(); + drop(provider); // allow connection to be cleaned + assert_eq!(admin_list.len(), 0); assert_eq!(super_admin_list.len(), 1); assert!(super_admin_list.contains(&amal.inbox_id())); @@ -2096,8 +2625,17 @@ mod tests { .unwrap(); amal_group.sync(&amal).await.unwrap(); bola_group.sync(&bola).await.unwrap(); - assert_eq!(bola_group.admin_list().unwrap().len(), 2); - assert!(bola_group.admin_list().unwrap().contains(&bola.inbox_id())); + assert_eq!( + bola_group + .admin_list(bola_group.mls_provider().unwrap()) + .unwrap() + .len(), + 1 + ); + assert!(bola_group + .admin_list(bola_group.mls_provider().unwrap()) + .unwrap() + .contains(&bola.inbox_id())); // Verify that bola can now add caro because they are an admin bola_group @@ -2105,10 +2643,12 @@ mod tests { .await .unwrap(); - // Verify that bola can not remove amal as an admin, because + bola_group.sync(&bola).await.unwrap(); + + // Verify that bola can not remove amal as a super admin, because // Remove admin is super admin only permissions bola_group - .update_admin_list(&bola, UpdateAdminListType::Remove, amal.inbox_id()) + .update_admin_list(&bola, UpdateAdminListType::RemoveSuper, amal.inbox_id()) .await .expect_err("expected err"); @@ -2119,8 +2659,17 @@ mod tests { .unwrap(); amal_group.sync(&amal).await.unwrap(); bola_group.sync(&bola).await.unwrap(); - assert_eq!(bola_group.admin_list().unwrap().len(), 1); - assert!(!bola_group.admin_list().unwrap().contains(&bola.inbox_id())); + assert_eq!( + bola_group + .admin_list(bola_group.mls_provider().unwrap()) + .unwrap() + .len(), + 0 + ); + assert!(!bola_group + .admin_list(bola_group.mls_provider().unwrap()) + .unwrap() + .contains(&bola.inbox_id())); // Verify that bola can not add charlie because they are not an admin bola.sync_welcomes().await.unwrap(); @@ -2140,9 +2689,9 @@ mod tests { let bola = ClientBuilder::new_test_client(&generate_local_wallet()).await; let caro = ClientBuilder::new_test_client(&generate_local_wallet()).await; - let policies = Some(PreconfiguredPolicies::AdminsOnly); + let policy_set = Some(PreconfiguredPolicies::AdminsOnly.to_policy_set()); let amal_group = amal - .create_group(policies, GroupMetadataOptions::default()) + .create_group(policy_set, GroupMetadataOptions::default()) .unwrap(); amal_group.sync(&amal).await.unwrap(); @@ -2157,11 +2706,12 @@ mod tests { let bola_group = bola_groups.first().unwrap(); bola_group.sync(&bola).await.unwrap(); - // Verify Amal is the only admin and super admin - let admin_list = amal_group.admin_list().unwrap(); - let super_admin_list = amal_group.super_admin_list().unwrap(); - assert_eq!(admin_list.len(), 1); - assert!(admin_list.contains(&amal.inbox_id())); + // Verify Amal is the only super admin + let provider = amal_group.mls_provider().unwrap(); + let admin_list = amal_group.admin_list(&provider).unwrap(); + let super_admin_list = amal_group.super_admin_list(&provider).unwrap(); + drop(provider); // allow connection to be re-added to pool + assert_eq!(admin_list.len(), 0); assert_eq!(super_admin_list.len(), 1); assert!(super_admin_list.contains(&amal.inbox_id())); @@ -2183,11 +2733,13 @@ mod tests { .unwrap(); amal_group.sync(&amal).await.unwrap(); bola_group.sync(&bola).await.unwrap(); - assert_eq!(bola_group.super_admin_list().unwrap().len(), 2); + let provider = bola_group.mls_provider().unwrap(); + assert_eq!(bola_group.super_admin_list(&provider).unwrap().len(), 2); assert!(bola_group - .super_admin_list() + .super_admin_list(&provider) .unwrap() .contains(&bola.inbox_id())); + drop(provider); // allow connection to be re-added to pool // Verify that bola can now add caro as an admin bola_group @@ -2195,8 +2747,13 @@ mod tests { .await .unwrap(); bola_group.sync(&bola).await.unwrap(); - assert_eq!(bola_group.admin_list().unwrap().len(), 2); - assert!(bola_group.admin_list().unwrap().contains(&caro.inbox_id())); + let provider = bola_group.mls_provider().unwrap(); + assert_eq!(bola_group.admin_list(&provider).unwrap().len(), 1); + assert!(bola_group + .admin_list(&provider) + .unwrap() + .contains(&caro.inbox_id())); + drop(provider); // allow connection to be re-added to pool // Verify that no one can remove a super admin from a group amal_group @@ -2210,11 +2767,13 @@ mod tests { .await .unwrap(); bola_group.sync(&bola).await.unwrap(); - assert_eq!(bola_group.super_admin_list().unwrap().len(), 1); + let provider = bola_group.mls_provider().unwrap(); + assert_eq!(bola_group.super_admin_list(&provider).unwrap().len(), 1); assert!(!bola_group - .super_admin_list() + .super_admin_list(&provider) .unwrap() .contains(&bola.inbox_id())); + drop(provider); // allow connection to be re-added to pool // Verify that amal can NOT remove themself as a super admin because they are the only remaining amal_group @@ -2229,9 +2788,9 @@ mod tests { let bola = ClientBuilder::new_test_client(&generate_local_wallet()).await; let caro = ClientBuilder::new_test_client(&generate_local_wallet()).await; - let policies = Some(PreconfiguredPolicies::AdminsOnly); + let policy_set = Some(PreconfiguredPolicies::AdminsOnly.to_policy_set()); let amal_group = amal - .create_group(policies, GroupMetadataOptions::default()) + .create_group(policy_set, GroupMetadataOptions::default()) .unwrap(); amal_group.sync(&amal).await.unwrap(); @@ -2243,7 +2802,7 @@ mod tests { amal_group.sync(&amal).await.unwrap(); // Initial checks for group members - let initial_members = amal_group.members().unwrap(); + let initial_members = amal_group.members(&amal).await.unwrap(); let mut count_member = 0; let mut count_admin = 0; let mut count_super_admin = 0; @@ -2271,7 +2830,7 @@ mod tests { amal_group.sync(&amal).await.unwrap(); // Check after adding Bola as an admin - let members = amal_group.members().unwrap(); + let members = amal_group.members(&amal).await.unwrap(); let mut count_member = 0; let mut count_admin = 0; let mut count_super_admin = 0; @@ -2299,7 +2858,7 @@ mod tests { amal_group.sync(&amal).await.unwrap(); // Check after adding Caro as a super admin - let members = amal_group.members().unwrap(); + let members = amal_group.members(&amal).await.unwrap(); let mut count_member = 0; let mut count_admin = 0; let mut count_super_admin = 0; @@ -2363,17 +2922,21 @@ mod tests { #[tokio::test] async fn test_can_read_group_creator_inbox_id() { let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; - let policies = Some(PreconfiguredPolicies::AllMembers); + let policy_set = Some(PreconfiguredPolicies::AllMembers.to_policy_set()); let amal_group = amal - .create_group(policies, GroupMetadataOptions::default()) + .create_group(policy_set, GroupMetadataOptions::default()) .unwrap(); amal_group.sync(&amal).await.unwrap(); - let mutable_metadata = amal_group.mutable_metadata().unwrap(); - assert_eq!(mutable_metadata.admin_list.len(), 1); - assert_eq!(mutable_metadata.admin_list[0], amal.inbox_id()); + let mutable_metadata = amal_group + .mutable_metadata(amal_group.mls_provider().unwrap()) + .unwrap(); + assert_eq!(mutable_metadata.super_admin_list.len(), 1); + assert_eq!(mutable_metadata.super_admin_list[0], amal.inbox_id()); - let protected_metadata: GroupMetadata = amal_group.metadata().unwrap(); + let protected_metadata: GroupMetadata = amal_group + .metadata(amal_group.mls_provider().unwrap()) + .unwrap(); assert_eq!( protected_metadata.conversation_type, ConversationType::Group @@ -2386,9 +2949,9 @@ mod tests { async fn test_can_update_gce_after_failed_commit() { // Step 1: Amal creates a group let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; - let policies = Some(PreconfiguredPolicies::AllMembers); + let policy_set = Some(PreconfiguredPolicies::AllMembers.to_policy_set()); let amal_group = amal - .create_group(policies, GroupMetadataOptions::default()) + .create_group(policy_set, GroupMetadataOptions::default()) .unwrap(); amal_group.sync(&amal).await.unwrap(); @@ -2409,7 +2972,9 @@ mod tests { .await .unwrap(); amal_group.sync(&amal).await.unwrap(); - let name = amal_group.group_name().unwrap(); + let name = amal_group + .group_name(amal_group.mls_provider().unwrap()) + .unwrap(); assert_eq!(name, "Name Update 1"); // Step 4: Bola attempts an action that they do not have permissions for like add admin, fails as expected @@ -2428,17 +2993,524 @@ mod tests { // Step 6: Verify that both clients can sync without error and that the group name has been updated amal_group.sync(&amal).await.unwrap(); bola_group.sync(&bola).await.unwrap(); - let binding = amal_group.mutable_metadata().expect("msg"); + let binding = amal_group + .mutable_metadata(amal_group.mls_provider().unwrap()) + .expect("msg"); let amal_group_name: &String = binding .attributes .get(&MetadataField::GroupName.to_string()) .unwrap(); assert_eq!(amal_group_name, "Name Update 2"); - let binding = bola_group.mutable_metadata().expect("msg"); + let binding = bola_group + .mutable_metadata(bola_group.mls_provider().unwrap()) + .expect("msg"); let bola_group_name: &String = binding .attributes .get(&MetadataField::GroupName.to_string()) .unwrap(); assert_eq!(bola_group_name, "Name Update 2"); } + + #[tokio::test] + async fn test_can_update_permissions_after_group_creation() { + let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; + let policy_set = Some(PreconfiguredPolicies::AdminsOnly.to_policy_set()); + let amal_group: MlsGroup = amal + .create_group(policy_set, GroupMetadataOptions::default()) + .unwrap(); + + // Step 2: Amal adds Bola to the group + let bola = ClientBuilder::new_test_client(&generate_local_wallet()).await; + amal_group + .add_members_by_inbox_id(&amal, vec![bola.inbox_id()]) + .await + .unwrap(); + + // Step 3: Bola attemps to add Caro, but fails because group is admin only + let caro = ClientBuilder::new_test_client(&generate_local_wallet()).await; + bola.sync_welcomes().await.unwrap(); + let bola_groups = bola.find_groups(None, None, None, None).unwrap(); + let bola_group: &MlsGroup = bola_groups.first().unwrap(); + bola_group.sync(&bola).await.unwrap(); + let result = bola_group + .add_members_by_inbox_id(&bola, vec![caro.inbox_id()]) + .await; + if let Err(e) = &result { + eprintln!("Error adding member: {:?}", e); + } else { + panic!("Expected error adding member"); + } + + // Step 4: Bola attempts to update permissions but fails because they are not a super admin + let result = bola_group + .update_permission_policy( + &bola, + PermissionUpdateType::AddMember, + PermissionPolicyOption::Allow, + None, + ) + .await; + if let Err(e) = &result { + eprintln!("Error updating permissions: {:?}", e); + } else { + panic!("Expected error updating permissions"); + } + + // Step 5: Amal updates group permissions so that all members can add + amal_group + .update_permission_policy( + &amal, + PermissionUpdateType::AddMember, + PermissionPolicyOption::Allow, + None, + ) + .await + .unwrap(); + + // Step 6: Bola can now add Caro to the group + bola_group + .add_members_by_inbox_id(&bola, vec![caro.inbox_id()]) + .await + .unwrap(); + bola_group.sync(&bola).await.unwrap(); + let members = bola_group.members(&bola).await.unwrap(); + assert_eq!(members.len(), 3); + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_optimistic_send() { + let amal = Arc::new(ClientBuilder::new_test_client(&generate_local_wallet()).await); + let bola_wallet = generate_local_wallet(); + let bola = Arc::new(ClientBuilder::new_test_client(&bola_wallet).await); + let amal_group = amal + .create_group(None, GroupMetadataOptions::default()) + .unwrap(); + amal_group.sync(&amal).await.unwrap(); + // Add bola to the group + amal_group + .add_members(&amal, vec![bola_wallet.get_address()]) + .await + .unwrap(); + let bola_group = receive_group_invite(&bola).await; + + let ids = vec![ + amal_group.send_message_optimistic(b"test one").unwrap(), + amal_group.send_message_optimistic(b"test two").unwrap(), + amal_group.send_message_optimistic(b"test three").unwrap(), + amal_group.send_message_optimistic(b"test four").unwrap(), + ]; + + let messages = amal_group + .find_messages(Some(GroupMessageKind::Application), None, None, None, None) + .unwrap() + .into_iter() + .collect::>(); + + let text = messages + .iter() + .cloned() + .map(|m| String::from_utf8_lossy(&m.decrypted_message_bytes).to_string()) + .collect::>(); + assert_eq!( + ids, + messages + .iter() + .cloned() + .map(|m| m.id) + .collect::>>() + ); + assert_eq!( + text, + vec![ + "test one".to_string(), + "test two".to_string(), + "test three".to_string(), + "test four".to_string(), + ] + ); + + let delivery = messages + .iter() + .cloned() + .map(|m| m.delivery_status) + .collect::>(); + assert_eq!( + delivery, + vec![ + DeliveryStatus::Unpublished, + DeliveryStatus::Unpublished, + DeliveryStatus::Unpublished, + DeliveryStatus::Unpublished, + ] + ); + + amal_group.publish_messages(&amal).await.unwrap(); + bola_group.sync(&bola).await.unwrap(); + + let messages = bola_group + .find_messages(None, None, None, None, None) + .unwrap(); + let delivery = messages + .iter() + .cloned() + .map(|m| m.delivery_status) + .collect::>(); + assert_eq!( + delivery, + vec![ + DeliveryStatus::Published, + DeliveryStatus::Published, + DeliveryStatus::Published, + DeliveryStatus::Published, + ] + ); + } + + #[tokio::test(flavor = "multi_thread")] + async fn process_messages_abort_on_retryable_error() { + let alix = ClientBuilder::new_test_client(&generate_local_wallet()).await; + let bo = ClientBuilder::new_test_client(&generate_local_wallet()).await; + + let alix_group = alix + .create_group(None, GroupMetadataOptions::default()) + .unwrap(); + + alix_group + .add_members_by_inbox_id(&alix, vec![bo.inbox_id()]) + .await + .unwrap(); + + // Create two commits + alix_group + .update_group_name(&alix, "foo".to_string()) + .await + .unwrap(); + alix_group + .update_group_name(&alix, "bar".to_string()) + .await + .unwrap(); + + let bo_group = receive_group_invite(&bo).await; + // Get the group messages before we lock the DB, simulating an error that happens + // in the middle of a sync instead of the beginning + let bo_messages = bo + .query_group_messages(&bo_group.group_id, &bo.store().conn().unwrap()) + .await + .unwrap(); + + let conn_1: XmtpOpenMlsProvider = bo.store().conn().unwrap().into(); + let mut conn_2 = bo.store().raw_conn().unwrap(); + + // Begin an exclusive transaction on a second connection to lock the database + conn_2.batch_execute("BEGIN EXCLUSIVE").unwrap(); + let process_result = bo_group.process_messages(bo_messages, &conn_1, &bo).await; + if let Some(GroupError::ReceiveErrors(errors)) = process_result.err() { + assert_eq!(errors.len(), 1); + assert!(errors + .first() + .unwrap() + .to_string() + .contains("database is locked")); + } else { + panic!("Expected error") + } + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_paralell_syncs() { + let wallet = generate_local_wallet(); + let alix1 = Arc::new(ClientBuilder::new_test_client(&wallet).await); + let alix1_group = alix1 + .create_group(None, GroupMetadataOptions::default()) + .unwrap(); + + let alix2 = ClientBuilder::new_test_client(&wallet).await; + + let sync_tasks: Vec<_> = (0..10) + .map(|_| { + let group_clone = alix1_group.clone(); + let client_clone = alix1.clone(); + // Each of these syncs is going to trigger the client to invite alix2 to the group + // because of the race + tokio::spawn(async move { group_clone.sync(&client_clone).await }) + }) + .collect(); + + let results = join_all(sync_tasks).await; + + // Check if any of the syncs failed + for result in results.into_iter() { + assert!(result.is_ok(), "Sync error {:?}", result.err()); + } + + // Make sure that only one welcome was sent + let alix2_welcomes = alix1 + .api_client + .query_welcome_messages(alix2.installation_public_key(), None) + .await + .unwrap(); + assert_eq!(alix2_welcomes.len(), 1); + + // Make sure that only one group message was sent + let group_messages = alix1 + .api_client + .query_group_messages(alix1_group.group_id.clone(), None) + .await + .unwrap(); + assert_eq!(group_messages.len(), 1); + + let alix2_group = receive_group_invite(&alix2).await; + + // Send a message from alix1 + alix1_group + .send_message("hi from alix1".as_bytes(), &alix1) + .await + .unwrap(); + // Send a message from alix2 + alix2_group + .send_message("hi from alix2".as_bytes(), &alix2) + .await + .unwrap(); + + // Sync both clients + alix1_group.sync(&alix1).await.unwrap(); + alix2_group.sync(&alix2).await.unwrap(); + + let alix1_messages = alix1_group + .find_messages(None, None, None, None, None) + .unwrap(); + let alix2_messages = alix2_group + .find_messages(None, None, None, None, None) + .unwrap(); + assert_eq!(alix1_messages.len(), alix2_messages.len()); + + assert!(alix1_messages + .iter() + .any(|m| m.decrypted_message_bytes == "hi from alix2".as_bytes())); + assert!(alix2_messages + .iter() + .any(|m| m.decrypted_message_bytes == "hi from alix1".as_bytes())); + } + + // Create a membership update intent, but don't sync it yet + async fn create_membership_update_no_sync( + group: &MlsGroup, + provider: &XmtpOpenMlsProvider, + client: &Client, + ) where + ApiClient: XmtpApi, + { + let intent_data = group + .get_membership_update_intent(client, provider, vec![], vec![]) + .await + .unwrap(); + + // If there is nothing to do, stop here + if intent_data.is_empty() { + return; + } + + let conn = provider.conn_ref(); + conn.insert_group_intent(NewGroupIntent::new( + IntentKind::UpdateGroupMembership, + group.group_id.clone(), + intent_data.into(), + )) + .unwrap(); + } + + /** + * This test case simulates situations where adding missing + * installations gets interrupted before the sync part happens + * + * We need to be safe even in situations where there are multiple + * intents that do the same thing, leading to conflicts + */ + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn add_missing_installs_reentrancy() { + let wallet = generate_local_wallet(); + let alix1 = ClientBuilder::new_test_client(&wallet).await; + let alix1_group = alix1 + .create_group(None, GroupMetadataOptions::default()) + .unwrap(); + + let alix1_provider = alix1.mls_provider().unwrap(); + + let alix2 = ClientBuilder::new_test_client(&wallet).await; + + // We are going to run add_missing_installations TWICE + // which will create two intents to add the installations + create_membership_update_no_sync(&alix1_group, &alix1_provider, &alix1).await; + create_membership_update_no_sync(&alix1_group, &alix1_provider, &alix1).await; + + // Now I am going to run publish intents multiple times + alix1_group + .publish_intents(&alix1_provider, &alix1) + .await + .expect("Expect publish to be OK"); + alix1_group + .publish_intents(&alix1_provider, &alix1) + .await + .expect("Expected publish to be OK"); + + // Now I am going to sync twice + alix1_group + .sync_with_conn(&alix1_provider, &alix1) + .await + .unwrap(); + alix1_group + .sync_with_conn(&alix1_provider, &alix1) + .await + .unwrap(); + + // Make sure that only one welcome was sent + let alix2_welcomes = alix1 + .api_client + .query_welcome_messages(alix2.installation_public_key(), None) + .await + .unwrap(); + assert_eq!(alix2_welcomes.len(), 1); + + // We expect two group messages to have been sent, + // but only the first is valid + let group_messages = alix1 + .api_client + .query_group_messages(alix1_group.group_id.clone(), None) + .await + .unwrap(); + assert_eq!(group_messages.len(), 2); + + let alix2_group = receive_group_invite(&alix2).await; + + // Send a message from alix1 + alix1_group + .send_message("hi from alix1".as_bytes(), &alix1) + .await + .unwrap(); + // Send a message from alix2 + alix2_group + .send_message("hi from alix2".as_bytes(), &alix2) + .await + .unwrap(); + + // Sync both clients + alix1_group.sync(&alix1).await.unwrap(); + alix2_group.sync(&alix2).await.unwrap(); + + let alix1_messages = alix1_group + .find_messages(None, None, None, None, None) + .unwrap(); + let alix2_messages = alix2_group + .find_messages(None, None, None, None, None) + .unwrap(); + assert_eq!(alix1_messages.len(), alix2_messages.len()); + + assert!(alix1_messages + .iter() + .any(|m| m.decrypted_message_bytes == "hi from alix2".as_bytes())); + assert!(alix2_messages + .iter() + .any(|m| m.decrypted_message_bytes == "hi from alix1".as_bytes())); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn respect_allow_epoch_increment() { + let wallet = generate_local_wallet(); + let client = ClientBuilder::new_test_client(&wallet).await; + + let group = client + .create_group(None, GroupMetadataOptions::default()) + .unwrap(); + + let _client_2 = ClientBuilder::new_test_client(&wallet).await; + + // Sync the group to get the message adding client_2 published to the network + group.sync(&client).await.unwrap(); + + // Retrieve the envelope for the commit from the network + let messages = client + .api_client + .query_group_messages(group.group_id.clone(), None) + .await + .unwrap(); + + let first_envelope = messages.first().unwrap(); + + let Some(xmtp_proto::xmtp::mls::api::v1::group_message::Version::V1(first_message)) = + first_envelope.clone().version + else { + panic!("wrong message format") + }; + let provider = client.mls_provider().unwrap(); + let mut openmls_group = group.load_mls_group(&provider).unwrap(); + let process_result = group + .process_message( + &client, + &mut openmls_group, + &provider, + &first_message, + false, + ) + .await; + + assert_err!( + process_result, + MessageProcessingError::EpochIncrementNotAllowed + ); + } + + #[tokio::test] + async fn test_get_and_set_consent() { + let alix = ClientBuilder::new_test_client(&generate_local_wallet()).await; + let bola = ClientBuilder::new_test_client(&generate_local_wallet()).await; + let caro = ClientBuilder::new_test_client(&generate_local_wallet()).await; + let alix_group = alix + .create_group(None, GroupMetadataOptions::default()) + .unwrap(); + + // group consent state should be allowed if user created it + assert_eq!(alix_group.consent_state().unwrap(), ConsentState::Allowed); + + alix_group + .update_consent_state(ConsentState::Denied) + .unwrap(); + assert_eq!(alix_group.consent_state().unwrap(), ConsentState::Denied); + + alix_group + .add_members_by_inbox_id(&alix, vec![bola.inbox_id()]) + .await + .unwrap(); + + bola.sync_welcomes().await.unwrap(); + let bola_groups = bola.find_groups(None, None, None, None).unwrap(); + let bola_group = bola_groups.first().unwrap(); + // group consent state should default to unknown for users who did not create the group + assert_eq!(bola_group.consent_state().unwrap(), ConsentState::Unknown); + + bola_group + .send_message("hi from bola".as_bytes(), &bola) + .await + .unwrap(); + + // group consent state should be allowed if user sends a message to the group + assert_eq!(bola_group.consent_state().unwrap(), ConsentState::Allowed); + + alix_group + .add_members_by_inbox_id(&alix, vec![caro.inbox_id()]) + .await + .unwrap(); + + caro.sync_welcomes().await.unwrap(); + let caro_groups = caro.find_groups(None, None, None, None).unwrap(); + let caro_group = caro_groups.first().unwrap(); + + caro_group + .send_message_optimistic("hi from caro".as_bytes()) + .unwrap(); + + caro_group.publish_messages(&caro).await.unwrap(); + + // group consent state should be allowed if user publishes a message to the group + assert_eq!(caro_group.consent_state().unwrap(), ConsentState::Allowed); + } } diff --git a/xmtp_mls/src/groups/subscriptions.rs b/xmtp_mls/src/groups/subscriptions.rs index 0c06efcf6..30f814d41 100644 --- a/xmtp_mls/src/groups/subscriptions.rs +++ b/xmtp_mls/src/groups/subscriptions.rs @@ -1,14 +1,15 @@ use std::collections::HashMap; -use std::pin::Pin; use std::sync::Arc; use futures::Stream; use super::{extract_message_v1, GroupError, MlsGroup}; use crate::storage::group_message::StoredGroupMessage; -use crate::subscriptions::{MessagesStreamInfo, StreamCloser}; -use crate::Client; +use crate::storage::refresh_state::EntityKind; +use crate::storage::StorageError; +use crate::subscriptions::{MessagesStreamInfo, StreamHandle}; use crate::XmtpApi; +use crate::{retry::Retry, retry_async, Client}; use prost::Message; use xmtp_proto::xmtp::mls::api::v1::GroupMessage; @@ -16,49 +17,68 @@ impl MlsGroup { pub(crate) async fn process_stream_entry( &self, envelope: GroupMessage, - client: Arc>, + client: &Client, ) -> Result, GroupError> where ApiClient: XmtpApi, { let msgv1 = extract_message_v1(envelope)?; let msg_id = msgv1.id; - let client_id = client.inbox_id().clone(); - log::info!( + let client_id = client.inbox_id(); + tracing::info!( "client [{}] is about to process streamed envelope: [{}]", &client_id.clone(), &msg_id ); let created_ns = msgv1.created_ns; - let client_pointer = client.clone(); - let process_result = self - .context - .store - .transaction_async(|provider| async move { - let mut openmls_group = self.load_mls_group(&provider)?; - - // Attempt processing immediately, but fail if the message is not an Application Message - // Returning an error should roll back the DB tx - log::info!( - "current epoch for [{}] in process_stream_entry() is Epoch: [{}]", - &client_id.clone(), - self.load_mls_group(&provider).unwrap().epoch() - ); - self.process_message( - client_pointer.as_ref(), - &mut openmls_group, - &provider, - &msgv1, - false, - ) - .await - .map_err(GroupError::ReceiveError) - }) - .await; - - if let Some(GroupError::ReceiveError(_)) = process_result.err() { - self.sync(&client).await?; + if !self.has_already_synced(msg_id).await? { + let process_result = retry_async!( + Retry::default(), + (async { + let client_id = client_id.clone(); + let msgv1 = msgv1.clone(); + self.context + .store + .transaction_async(|provider| async move { + let mut openmls_group = self.load_mls_group(&provider)?; + + // Attempt processing immediately, but fail if the message is not an Application Message + // Returning an error should roll back the DB tx + tracing::info!( + "current epoch for [{}] in process_stream_entry() is Epoch: [{}]", + client_id, + openmls_group.epoch() + ); + + self.process_message( + client, + &mut openmls_group, + &provider, + &msgv1, + false, + ) + .await + .map_err(GroupError::ReceiveError) + }) + .await + }) + ); + + if let Some(GroupError::ReceiveError(_)) = process_result.as_ref().err() { + // Swallow errors here, since another process may have successfully saved the message + // to the DB + match self.sync_with_conn(&client.mls_provider()?, client).await { + Ok(_) => { + tracing::debug!("Sync triggered by streamed message successful") + } + Err(err) => { + tracing::warn!("Sync triggered by streamed message failed: {}", err); + } + }; + } else if process_result.is_err() { + tracing::error!("Process stream entry {:?}", process_result.err()); + } } // Load the message from the DB to handle cases where it may have been already processed in @@ -72,10 +92,21 @@ impl MlsGroup { Ok(new_message) } + // Checks if a message has already been processed through a sync + async fn has_already_synced(&self, id: u64) -> Result { + let check_for_last_cursor = || -> Result { + let conn = self.context.store.conn()?; + conn.get_last_cursor_for_id(&self.group_id, EntityKind::Group) + }; + + let last_id = retry_async!(Retry::default(), (async { check_for_last_cursor() }))?; + Ok(last_id >= id as i64) + } + pub async fn process_streamed_group_message( &self, envelope_bytes: Vec, - client: Arc>, + client: &Client, ) -> Result where ApiClient: XmtpApi, @@ -84,24 +115,24 @@ impl MlsGroup { .map_err(|e| GroupError::Generic(e.to_string()))?; let message = self.process_stream_entry(envelope, client).await?; - Ok(message.unwrap()) + message.ok_or(GroupError::MissingMessage) } - pub async fn stream( - &self, - client: Arc>, - ) -> Result + Send + '_>>, GroupError> + pub async fn stream<'a, ApiClient>( + &'a self, + client: &'a Client, + ) -> Result + '_, GroupError> where - ApiClient: crate::XmtpApi, + ApiClient: crate::XmtpApi + 'static, { Ok(client - .stream_messages(HashMap::from([( + .stream_messages(Arc::new(HashMap::from([( self.group_id.clone(), MessagesStreamInfo { convo_created_at_ns: self.created_at_ns, cursor: 0, }, - )])) + )]))) .await?) } @@ -110,11 +141,11 @@ impl MlsGroup { group_id: Vec, created_at_ns: i64, callback: impl FnMut(StoredGroupMessage) + Send + 'static, - ) -> Result + ) -> StreamHandle> where - ApiClient: crate::XmtpApi, + ApiClient: crate::XmtpApi + 'static, { - Ok(Client::::stream_messages_with_callback( + Client::::stream_messages_with_callback( client, HashMap::from([( group_id, @@ -124,19 +155,20 @@ impl MlsGroup { }, )]), callback, - )?) + ) } } #[cfg(test)] mod tests { - use prost::Message; - use std::sync::Arc; + use super::*; + use std::time::Duration; + use tokio_stream::wrappers::UnboundedReceiverStream; use xmtp_cryptography::utils::generate_local_wallet; use crate::{ builder::ClientBuilder, groups::GroupMetadataOptions, - storage::group_message::GroupMessageKind, + storage::group_message::GroupMessageKind, utils::test::Delivery, }; use futures::StreamExt; @@ -167,7 +199,7 @@ mod tests { let mut message_bytes: Vec = Vec::new(); message.encode(&mut message_bytes).unwrap(); let message_again = amal_group - .process_streamed_group_message(message_bytes, Arc::new(amal)) + .process_streamed_group_message(message_bytes, &amal) .await; if let Ok(message) = message_again { @@ -180,7 +212,7 @@ mod tests { #[tokio::test(flavor = "multi_thread", worker_threads = 10)] async fn test_subscribe_messages() { let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; - let bola = ClientBuilder::new_test_client(&generate_local_wallet()).await; + let bola = Arc::new(ClientBuilder::new_test_client(&generate_local_wallet()).await); let amal_group = amal .create_group(None, GroupMetadataOptions::default()) @@ -193,15 +225,31 @@ mod tests { // Get bola's version of the same group let bola_groups = bola.sync_welcomes().await.unwrap(); - let bola_group = bola_groups.first().unwrap(); + let bola_group = Arc::new(bola_groups.first().unwrap().clone()); + + let bola_ptr = bola.clone(); + let bola_group_ptr = bola_group.clone(); + let notify = Delivery::new(Some(Duration::from_secs(10))); + let notify_ptr = notify.clone(); + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + let mut stream = UnboundedReceiverStream::new(rx); + tokio::spawn(async move { + let stream = bola_group_ptr.stream(&bola_ptr).await.unwrap(); + futures::pin_mut!(stream); + while let Some(item) = stream.next().await { + let _ = tx.send(item); + notify_ptr.notify_one(); + } + }); - let mut stream = bola_group.stream(Arc::new(bola)).await.unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(50)).await; amal_group .send_message("hello".as_bytes(), &amal) .await .unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(50)).await; + notify + .wait_for_delivery() + .await + .expect("timed out waiting for first message"); let first_val = stream.next().await.unwrap(); assert_eq!(first_val.decrypted_message_bytes, "hello".as_bytes()); @@ -210,6 +258,10 @@ mod tests { .await .unwrap(); + notify + .wait_for_delivery() + .await + .expect("timed out waiting for second message"); let second_val = stream.next().await.unwrap(); assert_eq!(second_val.decrypted_message_bytes, "goodbye".as_bytes()); } @@ -217,13 +269,22 @@ mod tests { #[tokio::test(flavor = "multi_thread", worker_threads = 10)] async fn test_subscribe_multiple() { let amal = Arc::new(ClientBuilder::new_test_client(&generate_local_wallet()).await); - let group = amal - .create_group(None, GroupMetadataOptions::default()) - .unwrap(); - - let stream = group.stream(amal.clone()).await.unwrap(); + let group = Arc::new( + amal.create_group(None, GroupMetadataOptions::default()) + .unwrap(), + ); - tokio::time::sleep(std::time::Duration::from_millis(100)).await; + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + let stream = tokio_stream::wrappers::UnboundedReceiverStream::new(rx); + let amal_ptr = amal.clone(); + let group_ptr = group.clone(); + tokio::spawn(async move { + let stream = group_ptr.stream(&amal_ptr).await.unwrap(); + futures::pin_mut!(stream); + while let Some(item) = stream.next().await { + let _ = tx.send(item); + } + }); for i in 0..10 { group @@ -252,15 +313,36 @@ mod tests { .create_group(None, GroupMetadataOptions::default()) .unwrap(); - let mut stream = amal_group.stream(amal.clone()).await.unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(50)).await; + let amal_ptr = amal.clone(); + let amal_group_ptr = amal_group.clone(); + let notify = Delivery::new(Some(Duration::from_secs(20))); + let notify_ptr = notify.clone(); + + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + let (start_tx, start_rx) = tokio::sync::oneshot::channel(); + let mut stream = UnboundedReceiverStream::new(rx); + tokio::spawn(async move { + let stream = amal_group_ptr.stream(&amal_ptr).await.unwrap(); + futures::pin_mut!(stream); + let _ = start_tx.send(()); + while let Some(item) = stream.next().await { + let _ = tx.send(item); + notify_ptr.notify_one(); + } + }); + // just to make sure stream is started + let _ = start_rx.await; + // Adding in a sleep, since the HTTP API client may acknowledge requests before they are ready + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; amal_group .add_members_by_inbox_id(&amal, vec![bola.inbox_id()]) .await .unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(50)).await; - + notify + .wait_for_delivery() + .await + .expect("Never received group membership change from stream"); let first_val = stream.next().await.unwrap(); assert_eq!(first_val.kind, GroupMessageKind::MembershipChange); @@ -268,6 +350,10 @@ mod tests { .send_message("hello".as_bytes(), &amal) .await .unwrap(); + notify + .wait_for_delivery() + .await + .expect("Never received second message from stream"); let second_val = stream.next().await.unwrap(); assert_eq!(second_val.decrypted_message_bytes, "hello".as_bytes()); } diff --git a/xmtp_mls/src/groups/sync.rs b/xmtp_mls/src/groups/sync.rs index d0d8c7c7e..c5c10f824 100644 --- a/xmtp_mls/src/groups/sync.rs +++ b/xmtp_mls/src/groups/sync.rs @@ -4,60 +4,60 @@ use std::{ }; use super::{ - build_group_membership_extension, build_mutable_metadata_extensions_for_admin_lists_update, - build_mutable_metadata_extensions_for_metadata_update, + build_extensions_for_admin_lists_update, build_extensions_for_metadata_update, + build_extensions_for_permissions_update, build_group_membership_extension, intents::{ Installation, PostCommitAction, SendMessageIntentData, SendWelcomesAction, - UpdateAdminListIntentData, UpdateGroupMembershipIntentData, + UpdateAdminListIntentData, UpdateGroupMembershipIntentData, UpdatePermissionIntentData, }, validated_commit::extract_group_membership, GroupError, MlsGroup, }; +#[cfg(feature = "message-history")] +use crate::groups::message_history::MessageHistoryContent; use crate::{ client::MessageProcessingError, codecs::{group_updated::GroupUpdatedCodec, ContentCodec}, configuration::{ - DELIMITER, GRPC_DATA_LIMIT, MAX_GROUP_SIZE, MAX_INTENT_PUBLISH_ATTEMPTS, - UPDATE_INSTALLATIONS_INTERVAL_NS, - }, - groups::{ - intents::UpdateMetadataIntentData, - message_history::{decrypt_history_file, download_history_bundle}, - validated_commit::ValidatedCommit, + GRPC_DATA_LIMIT, MAX_GROUP_SIZE, MAX_INTENT_PUBLISH_ATTEMPTS, MAX_PAST_EPOCHS, + SYNC_UPDATE_INSTALLATIONS_INTERVAL_NS, }, + groups::{intents::UpdateMetadataIntentData, validated_commit::ValidatedCommit}, hpke::{encrypt_welcome, HpkeError}, identity::parse_credential, identity_updates::load_identity_updates, - retry::Retry, + retry::{Retry, RetryableError}, retry_async, storage::{ db_connection::DbConnection, group_intent::{IntentKind, IntentState, NewGroupIntent, StoredGroupIntent, ID}, group_message::{DeliveryStatus, GroupMessageKind, StoredGroupMessage}, refresh_state::EntityKind, - StorageError, + serialization::{db_deserialize, db_serialize}, }, utils::{hash::sha256, id::calculate_message_id}, xmtp_openmls_provider::XmtpOpenMlsProvider, - Client, Delete, Fetch, Store, XmtpApi, + Client, Delete, Fetch, StoreOrIgnore, XmtpApi, }; use futures::future::try_join_all; -use log::debug; use openmls::{ credentials::BasicCredential, extensions::Extensions, - framing::{MlsMessageOut, ProtocolMessage}, + framing::{ContentType, ProtocolMessage}, + group::{GroupEpoch, StagedCommit}, prelude::{ tls_codec::{Deserialize, Serialize}, LeafNodeIndex, MlsGroup as OpenMlsGroup, MlsMessageBodyIn, MlsMessageIn, PrivateMessageIn, ProcessedMessage, ProcessedMessageContent, Sender, }, prelude_test::KeyPackage, + treesync::LeafNodeParameters, }; use openmls_basic_credential::SignatureKeyPair; use openmls_traits::OpenMlsProvider; use prost::bytes::Bytes; use prost::Message; +use tracing::debug; use xmtp_id::InboxId; use xmtp_proto::xmtp::mls::{ api::v1::{ @@ -68,61 +68,73 @@ use xmtp_proto::xmtp::mls::{ GroupMessage, WelcomeMessageInput, }, message_contents::{ - plaintext_envelope::{ - v2::MessageType::{Reply, Request}, - Content, V1, V2, - }, - GroupUpdated, MessageHistoryReply, MessageHistoryRequest, PlaintextEnvelope, + plaintext_envelope::{Content, V1, V2}, + GroupUpdated, PlaintextEnvelope, }, }; +#[cfg(feature = "message-history")] +use xmtp_proto::xmtp::mls::message_contents::plaintext_envelope::v2::MessageType::{ + Reply, Request, +}; + +#[derive(Debug)] +struct PublishIntentData { + staged_commit: Option>, + post_commit_action: Option>, + payload_to_publish: Vec, +} + impl MlsGroup { pub async fn sync(&self, client: &Client) -> Result<(), GroupError> where ApiClient: XmtpApi, { let conn = self.context.store.conn()?; - let mls_provider = client.mls_provider(conn.clone()); + let mls_provider = XmtpOpenMlsProvider::from(conn); - log::info!("[{}] syncing group", client.inbox_id()); - log::info!( + tracing::info!("[{}] syncing group", client.inbox_id()); + tracing::info!( "current epoch for [{}] in sync() is Epoch: [{}]", client.inbox_id(), - self.load_mls_group(mls_provider).unwrap().epoch() + self.load_mls_group(&mls_provider)?.epoch() ); - self.maybe_update_installations(conn.clone(), None, client) + self.maybe_update_installations(&mls_provider, None, client) .await?; - self.sync_with_conn(conn, client).await + self.sync_with_conn(&mls_provider, client).await } - #[tracing::instrument(level = "trace", skip(client, conn))] - pub(super) async fn sync_with_conn( + #[tracing::instrument(level = "trace", skip(self, provider, client))] + pub(crate) async fn sync_with_conn( &self, - conn: DbConnection, + provider: &XmtpOpenMlsProvider, client: &Client, ) -> Result<(), GroupError> where ApiClient: XmtpApi, { + let _mutex = self.mutex.lock().await; let mut errors: Vec = vec![]; + let conn = provider.conn_ref(); + // Even if publish fails, continue to receiving - if let Err(publish_error) = self.publish_intents(conn.clone(), client).await { - log::error!("Sync: error publishing intents {:?}", publish_error); + if let Err(publish_error) = self.publish_intents(provider, client).await { + tracing::error!("Sync: error publishing intents {:?}", publish_error); errors.push(publish_error); } // Even if receiving fails, continue to post_commit - if let Err(receive_error) = self.receive(&conn, client).await { - log::error!("receive error {:?}", receive_error); + if let Err(receive_error) = self.receive(provider, client).await { + tracing::error!("receive error {:?}", receive_error); // We don't return an error if receive fails, because it's possible this is caused // by malicious data sent over the network, or messages from before the user was // added to the group } - if let Err(post_commit_err) = self.post_commit(&conn, client).await { - log::error!("post commit error {:?}", post_commit_err); + if let Err(post_commit_err) = self.post_commit(conn, client).await { + tracing::error!("post commit error {:?}", post_commit_err); errors.push(post_commit_err); } @@ -130,10 +142,31 @@ impl MlsGroup { if !errors.is_empty() { return Err(GroupError::Sync(errors)); } - Ok(()) } + pub(super) async fn sync_until_last_intent_resolved( + &self, + provider: &XmtpOpenMlsProvider, + client: &Client, + ) -> Result<(), GroupError> + where + ApiClient: XmtpApi, + { + let intents = provider.conn_ref().find_group_intents( + self.group_id.clone(), + Some(vec![IntentState::ToPublish, IntentState::Published]), + None, + )?; + + if intents.is_empty() { + return Ok(()); + } + + self.sync_until_intent_resolved(provider, intents[intents.len() - 1].id, client) + .await + } + /** * Sync the group and wait for the intent to be deleted * Group syncing may involve picking up messages unrelated to the intent, so simply checking for errors @@ -141,10 +174,10 @@ impl MlsGroup { * * This method will retry up to `crate::configuration::MAX_GROUP_SYNC_RETRIES` times. */ - #[tracing::instrument(level = "trace", skip(client, conn))] + #[tracing::instrument(level = "trace", skip(client, self, provider))] pub(super) async fn sync_until_intent_resolved( &self, - conn: DbConnection, + provider: &XmtpOpenMlsProvider, intent_id: ID, client: &Client, ) -> Result<(), GroupError> @@ -155,43 +188,72 @@ impl MlsGroup { // Return the last error to the caller if we fail to sync let mut last_err: Option = None; while num_attempts < crate::configuration::MAX_GROUP_SYNC_RETRIES { - if let Err(err) = self.sync_with_conn(conn.clone(), client).await { - log::error!("error syncing group {:?}", err); + if let Err(err) = self.sync_with_conn(provider, client).await { + tracing::error!("error syncing group {:?}", err); last_err = Some(err); } - // This will return early if the fetch fails - let intent: Result, StorageError> = conn.fetch(&intent_id); - match intent { + match Fetch::::fetch(provider.conn_ref(), &intent_id) { Ok(None) => { // This is expected. The intent gets deleted on success return Ok(()); } - Ok(Some(intent)) => { - if intent.state == IntentState::Error { - log::warn!( - "not retrying intent ID {}. since it is in state Error", - intent.id, - ); - return Err(last_err.unwrap_or(GroupError::Generic( - "Group intent could not be committed".to_string(), - ))); - } - log::warn!( - "retrying intent ID {}. intent currently in state {:?}", - intent.id, - intent.state + Ok(Some(StoredGroupIntent { + id, + state: IntentState::Error, + .. + })) => { + tracing::warn!( + "not retrying intent ID {id}. since it is in state Error. {:?}", + last_err ); + return Err(last_err.unwrap_or(GroupError::Generic( + "Group intent could not be committed".to_string(), + ))); + } + Ok(Some(StoredGroupIntent { id, state, .. })) => { + tracing::warn!("retrying intent ID {id}. intent currently in state {state:?}"); } Err(err) => { - log::error!("database error fetching intent {:?}", err); + tracing::error!("database error fetching intent {:?}", err); last_err = Some(GroupError::Storage(err)); } }; num_attempts += 1; } - Err(last_err.unwrap_or(GroupError::Generic("failed to wait for intent".to_string()))) + Err(last_err.unwrap_or(GroupError::SyncFailedToWait)) + } + + fn is_valid_epoch( + inbox_id: InboxId, + intent_id: i32, + group_epoch: GroupEpoch, + message_epoch: GroupEpoch, + max_past_epochs: usize, + ) -> bool { + if message_epoch.as_u64() + max_past_epochs as u64 <= group_epoch.as_u64() { + tracing::warn!( + "[{}] own message epoch {} is {} or more less than group epoch {} for intent {}. Retrying message", + inbox_id, + message_epoch, + max_past_epochs, + group_epoch, + intent_id + ); + return false; + } else if message_epoch.as_u64() > group_epoch.as_u64() { + // Should not happen, logging proactively + tracing::error!( + "[{}] own message epoch {} is greater than group epoch {} for intent {}. Retrying message", + inbox_id, + message_epoch, + group_epoch, + intent_id + ); + return false; + } + true } #[allow(clippy::too_many_arguments)] @@ -204,132 +266,107 @@ impl MlsGroup { provider: &XmtpOpenMlsProvider, message: ProtocolMessage, envelope_timestamp_ns: u64, - allow_epoch_increment: bool, - ) -> Result<(), MessageProcessingError> { + ) -> Result { if intent.state == IntentState::Committed { - return Ok(()); + return Ok(IntentState::Committed); } + let message_epoch = message.epoch(); + let group_epoch = openmls_group.epoch(); debug!( - "[{}] processing own message for intent {} / {:?}", + "[{}]-[{}] processing own message for intent {} / {:?}, group epoch: {}, message_epoch: {}", self.context.inbox_id(), + hex::encode(self.group_id.clone()), intent.id, - intent.kind + intent.kind, + group_epoch, + message_epoch ); - let conn = provider.conn(); + let conn = provider.conn_ref(); match intent.kind { IntentKind::KeyUpdate | IntentKind::UpdateGroupMembership | IntentKind::UpdateAdminList - | IntentKind::MetadataUpdate => { - if !allow_epoch_increment { - return Err(MessageProcessingError::EpochIncrementNotAllowed); + | IntentKind::MetadataUpdate + | IntentKind::UpdatePermission => { + if let Some(published_in_epoch) = intent.published_in_epoch { + let published_in_epoch_u64 = published_in_epoch as u64; + let group_epoch_u64 = group_epoch.as_u64(); + + if published_in_epoch_u64 != group_epoch_u64 { + tracing::warn!( + "Intent was published in epoch {} but group is currently in epoch {}", + published_in_epoch_u64, + group_epoch_u64 + ); + return Ok(IntentState::ToPublish); + } } - let maybe_pending_commit = openmls_group.pending_commit(); - // We don't get errors with merge_pending_commit when there are no commits to merge - if maybe_pending_commit.is_none() { - let message_epoch = message.epoch(); - let group_epoch = openmls_group.epoch(); - debug!( - "no pending commit to merge. Group epoch: {}. Message epoch: {}", - group_epoch, message_epoch - ); - conn.set_group_intent_to_publish(intent.id)?; - // Return OK here, because an error will roll back the transaction - return Ok(()); - } - log::info!( + let pending_commit = if let Some(staged_commit) = intent.staged_commit { + decode_staged_commit(staged_commit)? + } else { + return Err(MessageProcessingError::IntentMissingStagedCommit); + }; + + tracing::info!( "[{}] Validating commit for intent {}. Message timestamp: {}", self.context.inbox_id(), intent.id, envelope_timestamp_ns ); + let maybe_validated_commit = ValidatedCommit::from_staged_commit( client, - &conn, - maybe_pending_commit.expect("already checked"), + conn, + &pending_commit, openmls_group, ) .await; - if maybe_validated_commit.is_err() { - log::warn!("error validating commit: {:?}", maybe_validated_commit); - match openmls_group.clear_pending_commit(provider.storage()) { - Ok(_) => (), - Err(_) => { - return Err(MessageProcessingError::Generic( - "Error clearing pending commit after failed validation".to_string(), - )) - } - } - conn.set_group_intent_error(intent.id)?; + if let Err(err) = maybe_validated_commit { + tracing::error!( + "Error validating commit for own message. Intent ID [{}]: {:?}", + intent.id, + err + ); // Return before merging commit since it does not pass validation - // An error will roll back clearing pending commit, so we return Ok here - return Ok(()); + // Return OK so that the group intent update is still written to the DB + return Ok(IntentState::Error); } - let validated_commit = maybe_validated_commit.unwrap(); + let validated_commit = maybe_validated_commit.expect("Checked for error"); - log::info!( + tracing::info!( "[{}] merging pending commit for intent {}", self.context.inbox_id(), intent.id ); - if let Err(err) = openmls_group.merge_pending_commit(&provider) { - log::error!("error merging commit: {}", err); - match openmls_group.clear_pending_commit(provider.storage()) { - Ok(_) => (), - Err(_) => { - return Err(MessageProcessingError::Generic( - "Error clearing pending commit".to_string(), - )) - } - } - - conn.set_group_intent_to_publish(intent.id)?; + if let Err(err) = openmls_group.merge_staged_commit(&provider, pending_commit) { + tracing::error!("error merging commit: {}", err); + return Ok(IntentState::ToPublish); } else { // If no error committing the change, write a transcript message - self.save_transcript_message(&conn, validated_commit, envelope_timestamp_ns)?; + self.save_transcript_message(conn, validated_commit, envelope_timestamp_ns)?; } } IntentKind::SendMessage => { - let intent_data = SendMessageIntentData::from_bytes(intent.data.as_slice())?; - let group_id = openmls_group.group_id().as_slice(); - let decrypted_message_data = intent_data.message.as_slice(); - - let envelope = PlaintextEnvelope::decode(decrypted_message_data) - .map_err(MessageProcessingError::DecodeError)?; - - match envelope.content { - Some(Content::V1(V1 { - idempotency_key, - content, - })) => { - let message_id = calculate_message_id(group_id, &content, &idempotency_key); - - conn.set_delivery_status_to_published(&message_id, envelope_timestamp_ns)?; - } - Some(Content::V2(V2 { - idempotency_key: _, - message_type, - })) => { - debug!( - "Send Message History Request with message_type {:#?}", - message_type - ); - - // return Empty Ok because it is okay to not process this self message - return Ok(()); - } - None => return Err(MessageProcessingError::InvalidPayload), - }; + if !Self::is_valid_epoch( + self.context.inbox_id(), + intent.id, + group_epoch, + message_epoch, + MAX_PAST_EPOCHS, + ) { + return Ok(IntentState::ToPublish); + } + if let Some(id) = intent.message_id()? { + conn.set_delivery_status_to_published(&id, envelope_timestamp_ns)?; + } } }; - conn.set_group_intent_committed(intent.id)?; - - Ok(()) + Ok(IntentState::Committed) } #[tracing::instrument(level = "trace", skip_all)] @@ -340,19 +377,18 @@ impl MlsGroup { provider: &XmtpOpenMlsProvider, message: PrivateMessageIn, envelope_timestamp_ns: u64, - allow_epoch_increment: bool, ) -> Result<(), MessageProcessingError> { let decrypted_message = openmls_group.process_message(provider, message)?; let (sender_inbox_id, sender_installation_id) = extract_message_sender(openmls_group, &decrypted_message, envelope_timestamp_ns)?; - log::info!( + tracing::info!( "[{}] extracted sender inbox id: {}", self.context.inbox_id(), sender_inbox_id ); match decrypted_message.into_content() { ProcessedMessageContent::ApplicationMessage(application_message) => { - log::info!("[{}] decoding application message", self.context.inbox_id()); + tracing::info!("[{}] decoding application message", self.context.inbox_id()); let message_bytes = application_message.into_bytes(); let mut bytes = Bytes::from(message_bytes.clone()); @@ -376,129 +412,60 @@ impl MlsGroup { sender_inbox_id, delivery_status: DeliveryStatus::Published, } - .store(provider.conn_ref())? + .store_or_ignore(provider.conn_ref())? } + #[cfg_attr(not(feature = "message-history"), allow(unused_variables))] Some(Content::V2(V2 { idempotency_key, message_type, })) => match message_type { - Some(Request(MessageHistoryRequest { - request_id, - pin_code, - })) => { + #[cfg(feature = "message-history")] + Some(Request(history_request)) => { + let content: MessageHistoryContent = + MessageHistoryContent::Request(history_request); + let content_bytes = serde_json::to_vec(&content)?; + let message_id = calculate_message_id( + &self.group_id, + &content_bytes, + &idempotency_key, + ); + // store the request message - let contents = - format!("{request_id}{DELIMITER}{pin_code}").into_bytes(); - let message_id = - calculate_message_id(&self.group_id, &contents, &idempotency_key); - let message = StoredGroupMessage { + StoredGroupMessage { id: message_id, group_id: self.group_id.clone(), - decrypted_message_bytes: contents, + decrypted_message_bytes: content_bytes, sent_at_ns: envelope_timestamp_ns as i64, kind: GroupMessageKind::Application, sender_installation_id, sender_inbox_id: sender_inbox_id.clone(), delivery_status: DeliveryStatus::Published, - }; - message.store(provider.conn_ref())?; - - // ensure the requester is a member of all the groups - let _ = client - .ensure_member_of_all_groups(sender_inbox_id) - .await - .map_err(|e| MessageProcessingError::Group(Box::new(e))); - - // prepare and send the reply - match client - .prepare_history_reply(&request_id, "https://example.com") - .await - { - Ok(history_reply) => client - .send_history_reply(history_reply.into()) - .await - .map_err(|e| { - MessageProcessingError::Generic(format!( - "could not send history reply: {e}" - )) - })?, - Err(e) => { - return Err(MessageProcessingError::Generic(format!( - "error preparing history reply: {e}" - ))); - } } + .store_or_ignore(provider.conn_ref())?; } - Some(Reply(MessageHistoryReply { - request_id: _, - url, - encryption_key, - signing_key, - bundle_hash, - })) => { - if signing_key.is_none() { - return Err(MessageProcessingError::InvalidPayload); - } - - if encryption_key.is_none() { - return Err(MessageProcessingError::InvalidPayload); - } - - let signing_key = signing_key.unwrap(); - - let encryption_key = encryption_key.unwrap(); + #[cfg(feature = "message-history")] + Some(Reply(history_reply)) => { + let content: MessageHistoryContent = + MessageHistoryContent::Reply(history_reply); + let content_bytes = serde_json::to_vec(&content)?; + let message_id = calculate_message_id( + &self.group_id, + &content_bytes, + &idempotency_key, + ); // store the reply message - let contents = format!( - "{url}{DELIMITER}{:?}{DELIMITER}{:?}{DELIMITER}{:?}", - encryption_key, signing_key, bundle_hash - ) - .into_bytes(); - let message_id = - calculate_message_id(&self.group_id, &contents, &idempotency_key); StoredGroupMessage { id: message_id, group_id: self.group_id.clone(), - decrypted_message_bytes: contents, + decrypted_message_bytes: content_bytes, sent_at_ns: envelope_timestamp_ns as i64, kind: GroupMessageKind::Application, sender_installation_id, sender_inbox_id, delivery_status: DeliveryStatus::Published, } - .store(provider.conn_ref())?; - - // handle the reply and fetch the history - let enc_file_path = - download_history_bundle(&url, bundle_hash, signing_key) - .await - .map_err(|e| MessageProcessingError::Generic(format!("{e}")))?; - - let messages_path = std::env::temp_dir().join("messages.jsonl"); - - decrypt_history_file(&enc_file_path, &messages_path, encryption_key) - .map_err(|e| MessageProcessingError::Generic(format!("{e}")))?; - - client - .insert_history_bundle(&messages_path) - .map_err(|e| MessageProcessingError::Generic(format!("{e}")))?; - - client - .sync_welcomes() - .await - .map_err(|e| MessageProcessingError::Generic(e.to_string()))?; - - let conn = provider.conn_ref(); - let groups = conn.find_groups(None, None, None, None)?; - for crate::storage::group::StoredGroup { id, .. } in groups.into_iter() - { - let group = client - .group(id) - .map_err(|e| MessageProcessingError::Generic(e.to_string()))?; - Box::pin(group.sync(client)) - .await - .map_err(|e| MessageProcessingError::Generic(e.to_string()))?; - } + .store_or_ignore(provider.conn_ref())?; } _ => { return Err(MessageProcessingError::InvalidPayload); @@ -514,10 +481,7 @@ impl MlsGroup { // intentionally left blank. } ProcessedMessageContent::StagedCommitMessage(staged_commit) => { - if !allow_epoch_increment { - return Err(MessageProcessingError::EpochIncrementNotAllowed); - } - log::info!( + tracing::info!( "[{}] received staged commit. Merging and clearing any pending commits", self.context.inbox_id() ); @@ -532,7 +496,7 @@ impl MlsGroup { openmls_group, ) .await?; - log::info!( + tracing::info!( "[{}] staged commit is valid, will attempt to merge", self.context.inbox_id() ); @@ -566,37 +530,54 @@ impl MlsGroup { )), }?; + if !allow_epoch_increment && message.content_type() == ContentType::Commit { + return Err(MessageProcessingError::EpochIncrementNotAllowed); + } + let intent = provider - .conn() + .conn_ref() .find_group_intent_by_payload_hash(sha256(envelope.data.as_slice())); match intent { // Intent with the payload hash matches Ok(Some(intent)) => { - log::info!( - "client [{}] is about to process own envelope [{}]", + let intent_id = intent.id; + tracing::info!( + "client [{}] is about to process own envelope [{}] for intent [{}]", client.inbox_id(), - envelope.id - ); - log::info!( - "envelope [{}] is equal to intent [{}]", envelope.id, - intent.id + intent_id ); - self.process_own_message( - client, - intent, - openmls_group, - provider, - message.into(), - envelope.created_ns, - allow_epoch_increment, - ) - .await + match self + .process_own_message( + client, + intent, + openmls_group, + provider, + message.into(), + envelope.created_ns, + ) + .await? + { + IntentState::ToPublish => { + Ok(provider.conn_ref().set_group_intent_to_publish(intent_id)?) + } + IntentState::Committed => { + Ok(provider.conn_ref().set_group_intent_committed(intent_id)?) + } + IntentState::Published => { + tracing::error!("Unexpected behaviour: returned intent state published from process_own_message"); + Ok(()) + } + IntentState::Error => { + tracing::warn!("Intent [{}] moved to error status", intent_id); + Ok(provider.conn_ref().set_group_intent_error(intent_id)?) + } + } } // No matching intent found Ok(None) => { - log::info!( + tracing::info!( "client [{}] is about to process external envelope [{}]", client.inbox_id(), envelope.id @@ -607,7 +588,6 @@ impl MlsGroup { provider, message, envelope.created_ns, - allow_epoch_increment, ) .await } @@ -649,14 +629,13 @@ impl MlsGroup { pub async fn process_messages( &self, messages: Vec, - conn: DbConnection, + provider: &XmtpOpenMlsProvider, client: &Client, ) -> Result<(), GroupError> where ApiClient: XmtpApi, { - let provider = self.context.mls_provider(conn); - let mut openmls_group = self.load_mls_group(&provider)?; + let mut openmls_group = self.load_mls_group(provider)?; let mut receive_errors = vec![]; for message in messages.into_iter() { @@ -668,30 +647,42 @@ impl MlsGroup { }) ); if let Err(e) = result { + let is_retryable = e.is_retryable(); + let error_message = e.to_string(); receive_errors.push(e); + // If the error is retryable we cannot move on to the next message + // otherwise you can get into a forked group state. + if is_retryable { + tracing::error!( + "Aborting message processing for retryable error: {}", + error_message + ); + break; + } } } if receive_errors.is_empty() { Ok(()) } else { - log::error!("Message processing errors: {:?}", receive_errors); + tracing::error!("Message processing errors: {:?}", receive_errors); Err(GroupError::ReceiveErrors(receive_errors)) } } - #[tracing::instrument(level = "trace", skip(conn, client))] + #[tracing::instrument(level = "trace", skip_all)] pub(super) async fn receive( &self, - conn: &DbConnection, + provider: &XmtpOpenMlsProvider, client: &Client, ) -> Result<(), GroupError> where ApiClient: XmtpApi, { - let messages = client.query_group_messages(&self.group_id, conn).await?; - self.process_messages(messages, conn.clone(), client) + let messages = client + .query_group_messages(&self.group_id, provider.conn_ref()) .await?; + self.process_messages(messages, provider, client).await?; Ok(()) } @@ -705,7 +696,7 @@ impl MlsGroup { return Ok(None); } - log::info!( + tracing::info!( "{}: Storing a transcript message with {} members added and {} members removed and {} metadata changes", self.context.inbox_id(), validated_commit.added_inboxes.len(), @@ -714,7 +705,7 @@ impl MlsGroup { ); let sender_installation_id = validated_commit.actor_installation_id(); let sender_inbox_id = validated_commit.actor_inbox_id(); - // TODO:nm replace with new membership change codec + let payload: GroupUpdated = validated_commit.into(); let encoded_payload = GroupUpdatedCodec::encode(payload)?; let mut encoded_payload_bytes = Vec::new(); @@ -738,23 +729,22 @@ impl MlsGroup { delivery_status: DeliveryStatus::Published, }; - msg.store(conn)?; + msg.store_or_ignore(conn)?; Ok(Some(msg)) } - #[tracing::instrument(level = "trace", skip(conn, client))] - pub(super) async fn publish_intents( + #[tracing::instrument(level = "trace", skip(self, provider, client))] + pub(super) async fn publish_intents( &self, - conn: DbConnection, - client: &Client, + provider: &XmtpOpenMlsProvider, + client: &Client, ) -> Result<(), GroupError> where - ClientApi: XmtpApi, + ApiClient: XmtpApi, { - let provider = self.context.mls_provider(conn); - let mut openmls_group = self.load_mls_group(&provider)?; + let mut openmls_group = self.load_mls_group(provider)?; - let intents = provider.conn().find_group_intents( + let intents = provider.conn_ref().find_group_intents( self.group_id.clone(), Some(vec![IntentState::ToPublish]), None, @@ -764,50 +754,78 @@ impl MlsGroup { let result = retry_async!( Retry::default(), (async { - self.get_publish_intent_data(&provider, client, &mut openmls_group, &intent) + self.get_publish_intent_data(provider, client, &mut openmls_group, &intent) .await }) ); - if let Err(err) = result { - log::error!("error getting publish intent data {:?}", err); - if (intent.publish_attempts + 1) as usize >= MAX_INTENT_PUBLISH_ATTEMPTS { - log::error!("intent {} has reached max publish attempts", intent.id); - // TODO: Eventually clean up errored attempts - provider.conn().set_group_intent_error(intent.id)?; - } else { - provider - .conn() - .increment_intent_publish_attempt_count(intent.id)?; - } + match result { + Err(err) => { + tracing::error!("error getting publish intent data {:?}", err); + if (intent.publish_attempts + 1) as usize >= MAX_INTENT_PUBLISH_ATTEMPTS { + tracing::error!("intent {} has reached max publish attempts", intent.id); + // TODO: Eventually clean up errored attempts + provider + .conn_ref() + .set_group_intent_error_and_fail_msg(&intent)?; + } else { + provider + .conn_ref() + .increment_intent_publish_attempt_count(intent.id)?; + } - return Err(err); - } + return Err(err); + } + Ok(Some(PublishIntentData { + payload_to_publish, + post_commit_action, + staged_commit, + })) => { + let payload_slice = payload_to_publish.as_slice(); + let has_staged_commit = staged_commit.is_some(); + provider.conn_ref().set_group_intent_published( + intent.id, + sha256(payload_slice), + post_commit_action, + staged_commit, + openmls_group.epoch().as_u64() as i64, + )?; + tracing::debug!( + "client [{}] set stored intent [{}] to state `published`", + client.inbox_id(), + intent.id + ); - let (payload, post_commit_data) = result.expect("already checked"); - let payload_slice = payload.as_slice(); + client + .api_client + .send_group_messages(vec![payload_slice]) + .await?; - client - .api_client - .send_group_messages(vec![payload_slice]) - .await?; - log::info!( - "[{}] published intent [{}] of type [{}]", - client.inbox_id(), - intent.id, - intent.kind - ); - provider.conn().set_group_intent_published( - intent.id, - sha256(payload_slice), - post_commit_data, - )?; + tracing::info!( + "[{}] published intent [{}] of type [{}]", + client.inbox_id(), + intent.id, + intent.kind + ); + if has_staged_commit { + tracing::info!("Commit sent. Stopping further publishes for this round"); + return Ok(()); + } + } + Ok(None) => { + tracing::info!("Skipping intent because no publish data returned"); + let deleter: &dyn Delete = provider.conn_ref(); + deleter.delete(intent.id)?; + } + } } Ok(()) } // Takes a StoredGroupIntent and returns the payload and post commit data as a tuple + // A return value of [`Option::None`] means this intent would not change the group. + #[allow(clippy::type_complexity)] #[tracing::instrument(level = "trace", skip_all)] async fn get_publish_intent_data( &self, @@ -815,7 +833,7 @@ impl MlsGroup { client: &Client, openmls_group: &mut OpenMlsGroup, intent: &StoredGroupIntent, - ) -> Result<(Vec, Option>), GroupError> + ) -> Result, GroupError> where ApiClient: XmtpApi, { @@ -823,19 +841,14 @@ impl MlsGroup { IntentKind::UpdateGroupMembership => { let intent_data = UpdateGroupMembershipIntentData::try_from(&intent.data)?; let signer = &self.context.identity.installation_keys; - let (commit, post_commit_action) = apply_update_group_membership_intent( + apply_update_group_membership_intent( client, provider, openmls_group, intent_data, signer, ) - .await?; - - Ok(( - commit.tls_serialize_detached()?, - post_commit_action.map(|action| action.to_bytes()), - )) + .await } IntentKind::SendMessage => { // We can safely assume all SendMessage intents have data @@ -847,23 +860,32 @@ impl MlsGroup { intent_data.message.as_slice(), )?; - let msg_bytes = msg.tls_serialize_detached()?; - Ok((msg_bytes, None)) + Ok(Some(PublishIntentData { + payload_to_publish: msg.tls_serialize_detached()?, + post_commit_action: None, + staged_commit: None, + })) } IntentKind::KeyUpdate => { - let (commit, _, _) = openmls_group - .self_update(&provider, &self.context.identity.installation_keys)?; + let (commit, _, _) = openmls_group.self_update( + &provider, + &self.context.identity.installation_keys, + LeafNodeParameters::default(), + )?; - Ok((commit.tls_serialize_detached()?, None)) + Ok(Some(PublishIntentData { + payload_to_publish: commit.tls_serialize_detached()?, + staged_commit: get_and_clear_pending_commit(openmls_group, provider)?, + post_commit_action: None, + })) } IntentKind::MetadataUpdate => { let metadata_intent = UpdateMetadataIntentData::try_from(intent.data.clone())?; - let mutable_metadata_extensions = - build_mutable_metadata_extensions_for_metadata_update( - openmls_group, - metadata_intent.field_name, - metadata_intent.field_value, - )?; + let mutable_metadata_extensions = build_extensions_for_metadata_update( + openmls_group, + metadata_intent.field_name, + metadata_intent.field_value, + )?; let (commit, _, _) = openmls_group.update_group_context_extensions( &provider, @@ -873,16 +895,19 @@ impl MlsGroup { let commit_bytes = commit.tls_serialize_detached()?; - Ok((commit_bytes, None)) + Ok(Some(PublishIntentData { + payload_to_publish: commit_bytes, + staged_commit: get_and_clear_pending_commit(openmls_group, provider)?, + post_commit_action: None, + })) } IntentKind::UpdateAdminList => { let admin_list_update_intent = UpdateAdminListIntentData::try_from(intent.data.clone())?; - let mutable_metadata_extensions = - build_mutable_metadata_extensions_for_admin_lists_update( - openmls_group, - admin_list_update_intent, - )?; + let mutable_metadata_extensions = build_extensions_for_admin_lists_update( + openmls_group, + admin_list_update_intent, + )?; let (commit, _, _) = openmls_group.update_group_context_extensions( provider, @@ -890,12 +915,36 @@ impl MlsGroup { &self.context.identity.installation_keys, )?; let commit_bytes = commit.tls_serialize_detached()?; - Ok((commit_bytes, None)) + + Ok(Some(PublishIntentData { + payload_to_publish: commit_bytes, + staged_commit: get_and_clear_pending_commit(openmls_group, provider)?, + post_commit_action: None, + })) + } + IntentKind::UpdatePermission => { + let update_permissions_intent = + UpdatePermissionIntentData::try_from(intent.data.clone())?; + let group_permissions_extensions = build_extensions_for_permissions_update( + openmls_group, + update_permissions_intent, + )?; + let (commit, _, _) = openmls_group.update_group_context_extensions( + provider, + group_permissions_extensions, + &self.context.identity.installation_keys, + )?; + let commit_bytes = commit.tls_serialize_detached()?; + Ok(Some(PublishIntentData { + payload_to_publish: commit_bytes, + staged_commit: get_and_clear_pending_commit(openmls_group, provider)?, + post_commit_action: None, + })) } } } - #[tracing::instrument(level = "trace", skip(conn, client))] + #[tracing::instrument(level = "trace", skip_all)] pub(crate) async fn post_commit( &self, conn: &DbConnection, @@ -911,8 +960,7 @@ impl MlsGroup { )?; for intent in intents { - if intent.post_commit_data.is_some() { - let post_commit_data = intent.post_commit_data.unwrap(); + if let Some(post_commit_data) = intent.post_commit_data { let post_commit_action = PostCommitAction::from_bytes(post_commit_data.as_slice())?; match post_commit_action { PostCommitAction::SendWelcomes(action) => { @@ -927,28 +975,31 @@ impl MlsGroup { Ok(()) } - pub(super) async fn maybe_update_installations( + pub async fn maybe_update_installations( &self, - conn: DbConnection, - update_interval: Option, + provider: &XmtpOpenMlsProvider, + update_interval_ns: Option, client: &Client, ) -> Result<(), GroupError> where ApiClient: XmtpApi, { // determine how long of an interval in time to use before updating list - let interval = match update_interval { + let interval_ns = match update_interval_ns { Some(val) => val, - None => UPDATE_INSTALLATIONS_INTERVAL_NS, + None => SYNC_UPDATE_INSTALLATIONS_INTERVAL_NS, }; - let now = crate::utils::time::now_ns(); - let last = conn.get_installations_time_checked(self.group_id.clone())?; - let elapsed = now - last; - if elapsed > interval { - let provider = self.context.mls_provider(conn.clone()); - self.add_missing_installations(&provider, client).await?; - conn.update_installations_time_checked(self.group_id.clone())?; + let now_ns = crate::utils::time::now_ns(); + let last_ns = provider + .conn_ref() + .get_installations_time_checked(self.group_id.clone())?; + let elapsed_ns = now_ns - last_ns; + if elapsed_ns > interval_ns { + self.add_missing_installations(provider, client).await?; + provider + .conn_ref() + .update_installations_time_checked(self.group_id.clone())?; } Ok(()) @@ -981,14 +1032,14 @@ impl MlsGroup { debug!("Adding missing installations {:?}", intent_data); - let conn = provider.conn(); + let conn = provider.conn_ref(); let intent = conn.insert_group_intent(NewGroupIntent::new( IntentKind::UpdateGroupMembership, self.group_id.clone(), intent_data.into(), ))?; - self.sync_until_intent_resolved(conn, intent.id, client) + self.sync_until_intent_resolved(provider, intent.id, client) .await } @@ -1040,11 +1091,11 @@ impl MlsGroup { updates.insert(inbox_id.to_string(), *latest_sequence_id as u64); } (_, _) => { - log::warn!( + tracing::warn!( "Could not find existing sequence ID for inbox {}", inbox_id ); - return Err(GroupError::NoChanges); + return Err(GroupError::MissingSequenceId); } } @@ -1096,13 +1147,13 @@ impl MlsGroup { .map(|w| match w { WelcomeMessageInputVersion::V1(w) => { let w = w.installation_key.len() + w.data.len() + w.hpke_public_key.len(); - log::debug!("total welcome message proto bytes={w}"); + tracing::debug!("total welcome message proto bytes={w}"); w } }) .unwrap_or(GRPC_DATA_LIMIT / usize::from(MAX_GROUP_SIZE)); - log::debug!("welcome chunk_size={chunk_size}"); + tracing::debug!("welcome chunk_size={chunk_size}"); let mut futures = vec![]; for welcomes in welcomes.chunks(chunk_size) { futures.push(client.api_client.send_welcome_messages(welcomes)); @@ -1145,7 +1196,7 @@ async fn apply_update_group_membership_intent( openmls_group: &mut OpenMlsGroup, intent_data: UpdateGroupMembershipIntentData, signer: &SignatureKeyPair, -) -> Result<(MlsMessageOut, Option), GroupError> { +) -> Result, GroupError> { let extensions: Extensions = openmls_group.extensions().clone(); let old_group_membership = extract_group_membership(&extensions)?; @@ -1158,7 +1209,7 @@ async fn apply_update_group_membership_intent( // This function goes to the network and fills in any missing Identity Updates let installation_diff = client .get_installation_diff( - &provider.conn(), + provider.conn_ref(), &old_group_membership, &new_group_membership, &membership_diff, @@ -1195,14 +1246,14 @@ async fn apply_update_group_membership_intent( && new_key_packages.is_empty() && membership_diff.updated_inboxes.is_empty() { - return Err(GroupError::NoChanges); + return Ok(None); } // Update the extensions to have the new GroupMembership let mut new_extensions = extensions.clone(); new_extensions.add_or_replace(build_group_membership_extension(&new_group_membership)); - // Commit to the pending proposals, which will clear the proposal queue + // Create the commit let (commit, maybe_welcome_message, _) = openmls_group.update_group_membership( provider, signer, @@ -1219,7 +1270,14 @@ async fn apply_update_group_membership_intent( None => None, }; - Ok((commit, post_commit_action)) + let staged_commit = get_and_clear_pending_commit(openmls_group, provider)? + .ok_or_else(|| GroupError::MissingPendingCommit)?; + + Ok(Some(PublishIntentData { + payload_to_publish: commit.tls_serialize_detached()?, + post_commit_action: post_commit_action.map(|action| action.to_bytes()), + staged_commit: Some(staged_commit), + })) } fn get_removed_leaf_nodes( @@ -1232,3 +1290,52 @@ fn get_removed_leaf_nodes( .map(|member| member.index) .collect() } + +fn get_and_clear_pending_commit( + openmls_group: &mut OpenMlsGroup, + provider: &XmtpOpenMlsProvider, +) -> Result>, GroupError> { + // TODO: remove clone + if let Some(commit) = openmls_group.clone().pending_commit() { + openmls_group.clear_pending_commit(provider.storage())?; + return Ok(Some(db_serialize(&commit)?)); + } + Ok(None) +} + +fn decode_staged_commit(data: Vec) -> Result { + Ok(db_deserialize(&data)?) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::builder::ClientBuilder; + use futures::future; + use std::sync::Arc; + use xmtp_cryptography::utils::generate_local_wallet; + + #[tokio::test(flavor = "multi_thread")] + async fn publish_intents_worst_case_scenario() { + let wallet = generate_local_wallet(); + let amal = Arc::new(ClientBuilder::new_test_client(&wallet).await); + let amal_group: Arc = + Arc::new(amal.create_group(None, Default::default()).unwrap()); + + amal_group.send_message_optimistic(b"1").unwrap(); + amal_group.send_message_optimistic(b"2").unwrap(); + amal_group.send_message_optimistic(b"3").unwrap(); + amal_group.send_message_optimistic(b"4").unwrap(); + amal_group.send_message_optimistic(b"5").unwrap(); + amal_group.send_message_optimistic(b"6").unwrap(); + + let conn = amal.context().store.conn().unwrap(); + let provider: XmtpOpenMlsProvider = conn.into(); + + let mut futures = vec![]; + for _ in 0..10 { + futures.push(amal_group.publish_intents(&provider, &amal)) + } + future::join_all(futures).await; + } +} diff --git a/xmtp_mls/src/groups/validated_commit.rs b/xmtp_mls/src/groups/validated_commit.rs index cd9164886..3ce770bc4 100644 --- a/xmtp_mls/src/groups/validated_commit.rs +++ b/xmtp_mls/src/groups/validated_commit.rs @@ -24,6 +24,8 @@ use xmtp_proto::xmtp::{ use crate::{ configuration::GROUP_MEMBERSHIP_EXTENSION_ID, identity_updates::{InstallationDiff, InstallationDiffError}, + retry::RetryableError, + retryable, storage::db_connection::DbConnection, Client, XmtpApi, }; @@ -34,7 +36,9 @@ use super::{ group_mutable_metadata::{ find_mutable_metadata_extension, GroupMutableMetadata, GroupMutableMetadataError, }, - group_permissions::{extract_group_permissions, GroupMutablePermissionsError}, + group_permissions::{ + extract_group_permissions, GroupMutablePermissions, GroupMutablePermissionsError, + }, }; #[derive(Debug, Error)] @@ -82,6 +86,15 @@ pub enum CommitValidationError { NoPSKSupport, } +impl RetryableError for CommitValidationError { + fn is_retryable(&self) -> bool { + match self { + CommitValidationError::InstallationDiff(diff_error) => retryable!(diff_error), + _ => false, + } + } +} + #[derive(Debug, Clone, PartialEq, Hash)] pub struct CommitParticipant { pub inbox_id: String, @@ -182,11 +195,11 @@ impl MetadataFieldChange { * Commit Validation Rules: * 1. If the `sequence_id` for an inbox has changed, it can only increase * 2. The client must create an expected diff of installations added and removed based on the difference between the current - * [`GroupMembership`] and the [`GroupMembership`] found in the [`StagedCommit`] + * [`GroupMembership`] and the [`GroupMembership`] found in the [`StagedCommit`] * 3. Installations may only be added or removed in the commit if they were added/removed in the expected diff * 4. For updates (either updating a path or via an Update Proposal) clients must verify that the `installation_id` is - * present in the [`AssociationState`] for the `inbox_id` presented in the credential at the `to_sequence_id` found in the - * new [`GroupMembership`]. + * present in the [`AssociationState`] for the `inbox_id` presented in the credential at the `to_sequence_id` found in the + * new [`GroupMembership`]. * 5. All proposals in a commit must come from the same installation * 6. No PSK proposals will be allowed */ @@ -196,6 +209,7 @@ pub struct ValidatedCommit { pub added_inboxes: Vec, pub removed_inboxes: Vec, pub metadata_changes: MutableMetadataChanges, + pub permissions_changed: bool, } impl ValidatedCommit { @@ -209,6 +223,7 @@ impl ValidatedCommit { let extensions = openmls_group.extensions(); let immutable_metadata: GroupMetadata = extensions.try_into()?; let mutable_metadata: GroupMutableMetadata = extensions.try_into()?; + let group_permissions: GroupMutablePermissions = extensions.try_into()?; let current_group_members = get_current_group_members(openmls_group); let existing_group_context = openmls_group.export_group_context(); @@ -220,6 +235,9 @@ impl ValidatedCommit { existing_group_context, new_group_context, )?; + + let permissions_changed = + extract_permissions_changed(&group_permissions, new_group_context)?; // Get the actor who created the commit. // Because we don't allow for multiple actors in a commit, this will error if two proposals come from different authors. let actor = extract_actor( @@ -307,6 +325,7 @@ impl ValidatedCommit { added_inboxes, removed_inboxes, metadata_changes, + permissions_changed, }; let policy_set = extract_group_permissions(openmls_group)?; @@ -407,7 +426,7 @@ fn get_latest_group_membership( Proposal::GroupContextExtensions(group_context_extensions) => { let new_group_membership: GroupMembership = extract_group_membership(group_context_extensions.extensions())?; - log::info!( + tracing::info!( "Group context extensions proposal found: {:?}", new_group_membership ); @@ -641,6 +660,16 @@ fn extract_metadata_changes( }) } +// Returns true if the permissions have changed, false otherwise +fn extract_permissions_changed( + old_group_permissions: &GroupMutablePermissions, + new_group_context: &GroupContext, +) -> Result { + let new_group_permissions: GroupMutablePermissions = + new_group_context.extensions().try_into()?; + Ok(!old_group_permissions.eq(&new_group_permissions)) +} + fn get_added_members( old: &[String], new: &[String], @@ -753,14 +782,15 @@ fn extract_actor( )?; // If there is both a path update and there are proposals we need to make sure that they are from the same actor - if path_update_leaf_node.is_some() && proposal_author_leaf_index.is_some() { + if let (Some(path_update_leaf_node), Some(proposal_author_leaf_index)) = + (path_update_leaf_node, proposal_author_leaf_index) + { let proposal_author = openmls_group - .member_at(*proposal_author_leaf_index.unwrap()) + .member_at(*proposal_author_leaf_index) .ok_or(CommitValidationError::ActorCouldNotBeFound)?; // Verify that the signature keys are the same if path_update_leaf_node - .unwrap() .signature_key() .as_slice() .to_vec() @@ -832,200 +862,204 @@ impl From for GroupUpdatedProto { } // TODO:nm bring these tests back in add/remove members PR +/* +#[cfg(test)] +mod tests { + use openmls::{ + credentials::{BasicCredential, CredentialWithKey}, + extensions::ExtensionType, + messages::proposals::ProposalType, + prelude::Capabilities, + prelude_test::KeyPackage, + }; + use xmtp_api_grpc::Client as GrpcClient; + use xmtp_cryptography::utils::generate_local_wallet; + + use super::ValidatedCommit; + use crate::{ + builder::ClientBuilder, + configuration::{ + CIPHERSUITE, GROUP_MEMBERSHIP_EXTENSION_ID, MUTABLE_METADATA_EXTENSION_ID, + }, + Client, + }; + + fn get_key_package(client: &Client) -> KeyPackage { + client + .identity() + .new_key_package(client.mls_provider().unwrap()) + .unwrap() + } + + #[tokio::test] + async fn test_membership_changes() { + let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; + let bola = ClientBuilder::new_test_client(&generate_local_wallet()).await; + let bola_key_package = get_key_package(&bola); + + let amal_group = amal.create_group(None, Default::default()).unwrap(); + let amal_provider = amal.mls_provider().unwrap(); + let mut mls_group = amal_group.load_mls_group(&amal_provider).unwrap(); + // Create a pending commit to add bola to the group + mls_group + .add_members( + &amal_provider, + &amal.identity().installation_keys, + &[bola_key_package], + ) + .unwrap(); + + let mut staged_commit = mls_group.pending_commit().unwrap(); + + let validated_commit = ValidatedCommit::from_staged_commit( + &amal, + amal_provider.conn_ref(), + staged_commit, + &mls_group, + ) + .await + .unwrap(); + + assert_eq!(validated_commit.added_inboxes.len(), 1); + assert_eq!(validated_commit.added_inboxes[0].inbox_id, bola.inbox_id()); + // Amal is the creator of the group and the actor + assert!(validated_commit.actor.is_creator); + // Bola is not the creator of the group + assert!(!validated_commit.added_inboxes[0].is_creator); + + // Merge the commit adding bola + mls_group.merge_pending_commit(&amal_provider).unwrap(); + // Now we are going to remove bola + + let bola_leaf_node = mls_group + .members() + .find(|m| { + m.signature_key + .eq(&bola.identity().installation_keys.public()) + }) + .unwrap() + .index; + mls_group + .remove_members( + &amal_provider, + &amal.identity().installation_keys, + &[bola_leaf_node], + ) + .unwrap(); + + staged_commit = mls_group.pending_commit().unwrap(); + let remove_message = ValidatedCommit::from_staged_commit( + &amal, + amal_provider.conn_ref(), + staged_commit, + &mls_group, + ) + .await + .unwrap(); + + assert_eq!(remove_message.removed_inboxes.len(), 1); + } + + #[tokio::test] + async fn test_installation_changes() { + let wallet = generate_local_wallet(); + let amal_1 = ClientBuilder::new_test_client(&wallet).await; + let amal_2 = ClientBuilder::new_test_client(&wallet).await; + + let amal_1_provider = amal_1.mls_provider().unwrap(); + let amal_2_provider = amal_2.mls_provider().unwrap(); + + let amal_group = amal_1.create_group(None, Default::default()).unwrap(); + let mut amal_mls_group = amal_group.load_mls_group(&amal_1_provider).unwrap(); -// #[cfg(test)] -// mod tests { -// use openmls::{ -// credentials::{BasicCredential, CredentialWithKey}, -// extensions::ExtensionType, -// group::config::CryptoConfig, -// messages::proposals::ProposalType, -// prelude::Capabilities, -// prelude_test::KeyPackage, -// versions::ProtocolVersion, -// }; -// use xmtp_api_grpc::Client as GrpcClient; -// use xmtp_cryptography::utils::generate_local_wallet; - -// use super::ValidatedCommit; -// use crate::{ -// builder::ClientBuilder, -// configuration::{ -// CIPHERSUITE, GROUP_MEMBERSHIP_EXTENSION_ID, MUTABLE_METADATA_EXTENSION_ID, -// }, -// Client, -// }; - -// fn get_key_package(client: &Client) -> KeyPackage { -// client -// .identity() -// .new_key_package(&client.mls_provider(client.store().conn().unwrap())) -// .unwrap() -// } - -// #[tokio::test] -// async fn test_membership_changes() { -// let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; -// let bola = ClientBuilder::new_test_client(&generate_local_wallet()).await; -// let bola_key_package = get_key_package(&bola); - -// let amal_group = amal.create_group(None).unwrap(); -// let amal_conn = amal.store().conn().unwrap(); -// let amal_provider = amal.mls_provider(amal_conn); -// let mut mls_group = amal_group.load_mls_group(&amal_provider).unwrap(); -// // Create a pending commit to add bola to the group -// mls_group -// .add_members( -// &amal_provider, -// &amal.identity().installation_keys, -// &[bola_key_package], -// ) -// .unwrap(); - -// let mut staged_commit = mls_group.pending_commit().unwrap(); - -// let validated_commit = ValidatedCommit::from_staged_commit( -// &amal.store().conn().unwrap(), -// staged_commit, -// &mls_group, -// &amal, -// ) -// .await -// .unwrap(); - -// assert_eq!(validated_commit.added_inboxes.len(), 1); -// assert_eq!(validated_commit.added_inboxes[0].inbox_id, bola.inbox_id()); -// // Amal is the creator of the group and the actor -// assert!(validated_commit.actor.is_creator); -// // Bola is not the creator of the group -// assert!(!validated_commit.added_inboxes[0].is_creator); - -// // Merge the commit adding bola -// mls_group.merge_pending_commit(&amal_provider).unwrap(); -// // Now we are going to remove bola - -// let bola_leaf_node = mls_group -// .members() -// .find(|m| { -// m.signature_key -// .eq(&bola.identity.installation_keys.public()) -// }) -// .unwrap() -// .index; -// mls_group -// .remove_members( -// &amal_provider, -// &amal.identity.installation_keys, -// &[bola_leaf_node], -// ) -// .unwrap(); - -// staged_commit = mls_group.pending_commit().unwrap(); -// let remove_message = ValidatedCommit::from_staged_commit(staged_commit, &mls_group) -// .unwrap() -// .unwrap(); - -// assert_eq!(remove_message.members_removed.len(), 1); -// assert_eq!(remove_message.installations_removed.len(), 0); -// } - -// #[tokio::test] -// async fn test_installation_changes() { -// let wallet = generate_local_wallet(); -// let amal_1 = ClientBuilder::new_test_client(&wallet).await; -// let amal_2 = ClientBuilder::new_test_client(&wallet).await; - -// let amal_1_conn = amal_1.store().conn().unwrap(); -// let amal_2_conn = amal_2.store().conn().unwrap(); - -// let amal_1_provider = amal_1().mls_provider(&amal_1_conn); -// let amal_2_provider = amal_2().mls_provider(&amal_2_conn); - -// let amal_group = amal_1.create_group(None).unwrap(); -// let mut amal_mls_group = amal_group.load_mls_group(&amal_1_provider).unwrap(); - -// let amal_2_kp = amal_2.identity.new_key_package(&amal_2_provider).unwrap(); - -// // Add Amal's second installation to the existing group -// amal_mls_group -// .add_members( -// &amal_1_provider, -// &amal_1.identity.installation_keys, -// &[amal_2_kp], -// ) -// .unwrap(); - -// let staged_commit = amal_mls_group.pending_commit().unwrap(); - -// let validated_commit = ValidatedCommit::from_staged_commit(staged_commit, &amal_mls_group) -// .unwrap() -// .unwrap(); - -// assert_eq!(validated_commit.installations_added.len(), 1); -// assert_eq!( -// validated_commit.installations_added[0].installation_ids[0], -// amal_2.installation_public_key() -// ) -// } - -// #[tokio::test] -// async fn test_bad_key_package() { -// let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; -// let bola = ClientBuilder::new_test_client(&generate_local_wallet()).await; - -// let amal_conn = amal.store.conn().unwrap(); -// let bola_conn = bola.store.conn().unwrap(); - -// let amal_provider = amal.mls_provider(&amal_conn); -// let bola_provider = bola.mls_provider(&bola_conn); - -// let amal_group = amal.create_group(None).unwrap(); -// let mut amal_mls_group = amal_group.load_mls_group(&amal_provider).unwrap(); - -// let capabilities = Capabilities::new( -// None, -// Some(&[CIPHERSUITE]), -// Some(&[ -// ExtensionType::LastResort, -// ExtensionType::ApplicationId, -// ExtensionType::Unknown(MUTABLE_METADATA_EXTENSION_ID), -// ExtensionType::Unknown(GROUP_MEMBERSHIP_EXTENSION_ID), -// ExtensionType::ImmutableMetadata, -// ]), -// Some(&[ProposalType::GroupContextExtensions]), -// None, -// ); - -// // Create a key package with a malformed credential -// let bad_key_package = KeyPackage::builder() -// .leaf_node_capabilities(capabilities) -// .build( -// CryptoConfig { -// ciphersuite: CIPHERSUITE, -// version: ProtocolVersion::default(), -// }, -// &bola_provider, -// &bola.identity.installation_keys, -// CredentialWithKey { -// // Broken credential -// credential: BasicCredential::new(vec![1, 2, 3]).unwrap().into(), -// signature_key: bola.identity.installation_keys.to_public_vec().into(), -// }, -// ) -// .unwrap(); - -// amal_mls_group -// .add_members( -// &amal_provider, -// &amal.identity.installation_keys, -// &[bad_key_package], -// ) -// .unwrap(); - -// let staged_commit = amal_mls_group.pending_commit().unwrap(); - -// let validated_commit = ValidatedCommit::from_staged_commit(staged_commit, &amal_mls_group); - -// assert!(validated_commit.is_err()); -// } -// } + let amal_2_kp = amal_2.identity().new_key_package(&amal_2_provider).unwrap(); + + // Add Amal's second installation to the existing group + amal_mls_group + .add_members( + &amal_1_provider, + &amal_1.identity().installation_keys, + &[amal_2_kp], + ) + .unwrap(); + + let staged_commit = amal_mls_group.pending_commit().unwrap(); + + let validated_commit = ValidatedCommit::from_staged_commit( + &amal_1, + amal_1_provider.conn_ref(), + staged_commit, + &amal_mls_group, + ) + .await + .unwrap(); + + assert_eq!(validated_commit.added_inboxes.len(), 1); + assert_eq!( + validated_commit.added_inboxes[0].inbox_id, + amal_2.inbox_id() + ) + } + + #[tokio::test] + async fn test_bad_key_package() { + let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; + let bola = ClientBuilder::new_test_client(&generate_local_wallet()).await; + + let amal_provider = amal.mls_provider().unwrap(); + let bola_provider = bola.mls_provider().unwrap(); + + let amal_group = amal.create_group(None, Default::default()).unwrap(); + let mut amal_mls_group = amal_group.load_mls_group(&amal_provider).unwrap(); + + let capabilities = Capabilities::new( + None, + Some(&[CIPHERSUITE]), + Some(&[ + ExtensionType::LastResort, + ExtensionType::ApplicationId, + ExtensionType::Unknown(MUTABLE_METADATA_EXTENSION_ID), + ExtensionType::Unknown(GROUP_MEMBERSHIP_EXTENSION_ID), + ExtensionType::ImmutableMetadata, + ]), + Some(&[ProposalType::GroupContextExtensions]), + None, + ); + + // Create a key package with a malformed credential + let bad_key_package = KeyPackage::builder() + .leaf_node_capabilities(capabilities) + .build( + CIPHERSUITE, + &bola_provider, + &bola.identity().installation_keys, + CredentialWithKey { + // Broken credential + credential: BasicCredential::new(vec![1, 2, 3]).into(), + signature_key: bola.identity().installation_keys.to_public_vec().into(), + }, + ) + .unwrap(); + + amal_mls_group + .add_members( + &amal_provider, + &amal.identity().installation_keys, + &[bad_key_package.key_package().clone()], + ) + .unwrap(); + + let staged_commit = amal_mls_group.pending_commit().unwrap(); + + let validated_commit = ValidatedCommit::from_staged_commit( + &amal, + amal_provider.conn_ref(), + staged_commit, + &amal_mls_group, + ) + .await; + + assert!(validated_commit.is_err()); + } +} +*/ diff --git a/xmtp_mls/src/hpke.rs b/xmtp_mls/src/hpke.rs index 9948c4bcf..99964fb1d 100644 --- a/xmtp_mls/src/hpke.rs +++ b/xmtp_mls/src/hpke.rs @@ -1,6 +1,8 @@ use crate::{ configuration::{CIPHERSUITE, WELCOME_HPKE_LABEL}, - storage::sql_key_store::KEY_PACKAGE_REFERENCES, + retry::RetryableError, + retryable, + storage::sql_key_store::{SqlKeyStoreError, KEY_PACKAGE_REFERENCES}, xmtp_openmls_provider::XmtpOpenMlsProvider, }; use openmls::{ @@ -22,10 +24,21 @@ pub enum HpkeError { Hpke(#[from] OpenmlsHpkeError), #[error("TLS Codec error: {0}")] TlsError(#[from] TlsCodecError), + #[error("Storage error: {0}")] + StorageError(#[from] SqlKeyStoreError), #[error("Key not found")] KeyNotFound, } +impl RetryableError for HpkeError { + fn is_retryable(&self) -> bool { + match self { + Self::StorageError(storage) => retryable!(storage), + _ => false, + } + } +} + #[tracing::instrument(level = "trace", skip_all)] pub fn encrypt_welcome(welcome_payload: &[u8], hpke_key: &[u8]) -> Result, HpkeError> { let crypto = RustCrypto::default(); @@ -52,21 +65,13 @@ pub fn decrypt_welcome( let serialized_hpke_public_key = hpke_public_key.tls_serialize_detached()?; - let hash_ref: Option = match provider + let hash_ref: Option = provider .storage() - .read(KEY_PACKAGE_REFERENCES, &serialized_hpke_public_key) - { - Ok(hash_ref) => hash_ref, - Err(_) => return Err(HpkeError::KeyNotFound), - }; + .read(KEY_PACKAGE_REFERENCES, &serialized_hpke_public_key)?; if let Some(hash_ref) = hash_ref { // With the hash reference we can read the key package. - let key_package: Option = match provider.storage().key_package(&hash_ref) - { - Ok(key_package) => key_package, - Err(_) => return Err(HpkeError::KeyNotFound), - }; + let key_package: Option = provider.storage().key_package(&hash_ref)?; if let Some(kp) = key_package { return Ok(decrypt_with_label( diff --git a/xmtp_mls/src/identity.rs b/xmtp_mls/src/identity.rs index 297758c5c..acb481ff0 100644 --- a/xmtp_mls/src/identity.rs +++ b/xmtp_mls/src/identity.rs @@ -1,9 +1,11 @@ use std::array::TryFromSliceError; +use std::sync::atomic::{AtomicBool, Ordering}; use crate::configuration::GROUP_PERMISSIONS_EXTENSION_ID; +use crate::retry::RetryableError; use crate::storage::db_connection::DbConnection; use crate::storage::identity::StoredIdentity; -use crate::storage::sql_key_store::{MemoryStorageError, KEY_PACKAGE_REFERENCES}; +use crate::storage::sql_key_store::{SqlKeyStore, SqlKeyStoreError, KEY_PACKAGE_REFERENCES}; use crate::storage::EncryptedMessageStore; use crate::{ api::{ApiClientWrapper, WrappedApiError}, @@ -12,40 +14,39 @@ use crate::{ xmtp_openmls_provider::XmtpOpenMlsProvider, XmtpApi, }; -use crate::{Fetch, Store}; +use crate::{retryable, Fetch, Store}; use ed25519_dalek::SigningKey; -use ethers::signers::WalletError; -use log::debug; -use log::info; +use openmls::prelude::hash_ref::HashReference; use openmls::prelude::tls_codec::Serialize; use openmls::{ credentials::{errors::BasicCredentialError, BasicCredential, CredentialWithKey}, extensions::{ ApplicationIdExtension, Extension, ExtensionType, Extensions, LastResortExtension, }, - key_packages::Lifetime, messages::proposals::ProposalType, prelude::{Capabilities, Credential as OpenMlsCredential}, prelude_test::KeyPackage, }; use openmls_basic_credential::SignatureKeyPair; +use openmls_traits::storage::StorageProvider; use openmls_traits::types::CryptoError; use openmls_traits::OpenMlsProvider; use prost::Message; use sha2::{Digest, Sha512}; use thiserror::Error; +use tracing::debug; +use tracing::info; +use xmtp_id::associations::unverified::{UnverifiedInstallationKeySignature, UnverifiedSignature}; +use xmtp_id::scw_verifier::SmartContractSignatureVerifier; use xmtp_id::{ associations::{ builder::{SignatureRequest, SignatureRequestBuilder, SignatureRequestError}, - generate_inbox_id, sign_with_legacy_key, InstallationKeySignature, MemberIdentifier, + generate_inbox_id, sign_with_legacy_key, MemberIdentifier, }, constants::INSTALLATION_KEY_SIGNATURE_CONTEXT, InboxId, }; -use xmtp_proto::{ - api_client::{XmtpIdentityClient, XmtpMlsClient}, - xmtp::identity::MlsCredential, -}; +use xmtp_proto::xmtp::identity::MlsCredential; #[derive(Debug, Clone)] pub enum IdentityStrategy { @@ -59,18 +60,21 @@ pub enum IdentityStrategy { } impl IdentityStrategy { - pub(crate) async fn initialize_identity( + pub(crate) async fn initialize_identity( self, api_client: &ApiClientWrapper, store: &EncryptedMessageStore, + scw_signature_verifier: &dyn SmartContractSignatureVerifier, ) -> Result { info!("Initializing identity"); let conn = store.conn()?; let provider = XmtpOpenMlsProvider::new(conn); let stored_identity: Option = provider - .conn() + .conn_ref() .fetch(&())? - .map(|i: StoredIdentity| i.into()); + .map(|i: StoredIdentity| i.try_into()) + .transpose()?; + debug!("identity in store: {:?}", stored_identity); match self { IdentityStrategy::CachedOnly => { @@ -99,6 +103,7 @@ impl IdentityStrategy { legacy_signed_private_key, api_client, &provider, + scw_signature_verifier, ) .await } @@ -117,10 +122,10 @@ pub enum IdentityError { Decode(#[from] prost::DecodeError), #[error(transparent)] WrappedApi(#[from] WrappedApiError), - #[error("installation not found: {0}")] - InstallationIdNotFound(String), #[error(transparent)] Api(#[from] xmtp_proto::api_client::Error), + #[error("installation not found: {0}")] + InstallationIdNotFound(String), #[error(transparent)] SignatureRequestBuilder(#[from] SignatureRequestError), #[error(transparent)] @@ -142,13 +147,11 @@ pub enum IdentityError { #[error("legacy key does not match address")] LegacyKeyMismatch, #[error(transparent)] - WalletError(#[from] WalletError), - #[error(transparent)] OpenMls(#[from] openmls::prelude::Error), #[error(transparent)] StorageError(#[from] crate::storage::StorageError), #[error(transparent)] - OpenMlsStorageError(#[from] MemoryStorageError), + OpenMlsStorageError(#[from] SqlKeyStoreError), #[error(transparent)] KeyPackageGenerationError(#[from] openmls::key_packages::errors::KeyPackageNewError), #[error(transparent)] @@ -161,14 +164,42 @@ pub enum IdentityError { RequiredIdentityNotFound, #[error("error creating new identity: {0}")] NewIdentity(String), + #[error(transparent)] + DieselResult(#[from] diesel::result::Error), } -#[derive(Debug, Clone)] +impl RetryableError for IdentityError { + fn is_retryable(&self) -> bool { + match self { + Self::Api(_) => true, + Self::WrappedApi(err) => retryable!(err), + Self::StorageError(err) => retryable!(err), + Self::OpenMlsStorageError(err) => retryable!(err), + Self::DieselResult(err) => retryable!(err), + _ => false, + } + } +} + +#[derive(Debug)] pub struct Identity { pub(crate) inbox_id: InboxId, pub(crate) installation_keys: SignatureKeyPair, pub(crate) credential: OpenMlsCredential, pub(crate) signature_request: Option, + pub(crate) is_ready: AtomicBool, +} + +impl Clone for Identity { + fn clone(&self) -> Self { + Self { + inbox_id: self.inbox_id.clone(), + installation_keys: self.installation_keys.clone(), + credential: self.credential.clone(), + signature_request: self.signature_request(), + is_ready: AtomicBool::new(self.is_ready.load(Ordering::SeqCst)), + } + } } impl Identity { @@ -179,13 +210,14 @@ impl Identity { /// /// If the address is NOT associated with an inbox_id, a new inbox_id will be generated. /// Prioritize legacy key if provided, otherwise use wallet to sign. - pub(crate) async fn new( + pub(crate) async fn new( inbox_id: InboxId, address: String, nonce: u64, legacy_signed_private_key: Option>, api_client: &ApiClientWrapper, provider: &XmtpOpenMlsProvider, + scw_signature_verifier: &dyn SmartContractSignatureVerifier, ) -> Result { // check if address is already associated with an inbox_id let address = address.to_lowercase(); @@ -198,7 +230,7 @@ impl Identity { if let Some(associated_inbox_id) = associated_inbox_id { // If an inbox is associated with address, we'd use it to create Identity and ignore the nonce. // We would need a signature from user's wallet. - if associated_inbox_id != &inbox_id { + if *associated_inbox_id != inbox_id { return Err(IdentityError::NewIdentity("Inbox ID mismatch".to_string())); } let builder = SignatureRequestBuilder::new(associated_inbox_id.clone()); @@ -207,13 +239,16 @@ impl Identity { .build(); signature_request - .add_signature(Box::new( - sign_with_installation_key( - signature_request.signature_text(), - sized_installation_key(signature_keys.private())?, - ) - .await?, - )) + .add_signature( + UnverifiedSignature::InstallationKey( + sign_with_installation_key( + signature_request.signature_text(), + sized_installation_key(signature_keys.private())?, + ) + .await?, + ), + scw_signature_verifier, + ) .await?; let identity = Self { @@ -221,6 +256,7 @@ impl Identity { installation_keys: signature_keys, credential: create_credential(associated_inbox_id.clone())?, signature_request: Some(signature_request), + is_ready: AtomicBool::new(false), }; Ok(identity) @@ -242,35 +278,44 @@ impl Identity { .build(); signature_request - .add_signature(Box::new( - sign_with_installation_key( - signature_request.signature_text(), - sized_installation_key(signature_keys.private())?, - ) - .await?, - )) + .add_signature( + UnverifiedSignature::InstallationKey( + sign_with_installation_key( + signature_request.signature_text(), + sized_installation_key(signature_keys.private())?, + ) + .await?, + ), + scw_signature_verifier, + ) .await?; signature_request - .add_signature(Box::new( - sign_with_legacy_key( - signature_request.signature_text(), - legacy_signed_private_key, - ) - .await?, - )) + .add_signature( + UnverifiedSignature::LegacyDelegated( + sign_with_legacy_key( + signature_request.signature_text(), + legacy_signed_private_key, + ) + .await?, + ), + scw_signature_verifier, + ) .await?; - let identity_update = signature_request.build_identity_update()?; - api_client.publish_identity_update(identity_update).await?; + // Make sure to register the identity before applying the signature request let identity = Self { inbox_id: inbox_id.clone(), installation_keys: signature_keys, credential: create_credential(inbox_id)?, signature_request: None, + is_ready: AtomicBool::new(true), }; identity.register(provider, api_client).await?; + let identity_update = signature_request.build_identity_update()?; + api_client.publish_identity_update(identity_update).await?; + Ok(identity) } else { if inbox_id != generate_inbox_id(&address, &nonce) { @@ -288,13 +333,16 @@ impl Identity { // We can pre-sign the request with an installation key signature, since we have access to the key signature_request - .add_signature(Box::new( - sign_with_installation_key( - signature_request.signature_text(), - sized_installation_key(signature_keys.private())?, - ) - .await?, - )) + .add_signature( + UnverifiedSignature::InstallationKey( + sign_with_installation_key( + signature_request.signature_text(), + sized_installation_key(signature_keys.private())?, + ) + .await?, + ), + scw_signature_verifier, + ) .await?; let identity = Self { @@ -302,6 +350,7 @@ impl Identity { installation_keys: signature_keys, credential: create_credential(inbox_id.clone())?, signature_request: Some(signature_request), + is_ready: AtomicBool::new(false), }; Ok(identity) @@ -317,8 +366,8 @@ impl Identity { } #[allow(dead_code)] - fn is_ready(&self) -> bool { - self.signature_request.is_none() + pub fn is_ready(&self) -> bool { + self.is_ready.load(Ordering::SeqCst) } pub fn signature_request(&self) -> Option { @@ -340,7 +389,7 @@ impl Identity { pub(crate) fn new_key_package( &self, - provider: &XmtpOpenMlsProvider, + provider: impl OpenMlsProvider, ) -> Result { let last_resort = Extension::LastResort(LastResortExtension::default()); let key_package_extensions = Extensions::single(last_resort); @@ -367,10 +416,9 @@ impl Identity { .leaf_node_capabilities(capabilities) .leaf_node_extensions(leaf_node_extensions) .key_package_extensions(key_package_extensions) - .key_package_lifetime(Lifetime::new(6 * 30 * 86400)) .build( CIPHERSUITE, - provider, + &provider, &self.installation_keys, CredentialWithKey { credential: self.credential(), @@ -381,17 +429,7 @@ impl Identity { // This is needed to get to the private key when decrypting welcome messages. let public_init_key = kp.key_package().hpke_init_key().tls_serialize_detached()?; - let key_package_hash_ref = match kp.key_package().hash_ref(provider.crypto()) { - Ok(key_package_hash_ref) => key_package_hash_ref, - Err(_) => return Err(IdentityError::UninitializedIdentity), - }; - - // Serialize the hash reference - let hash_ref = match serde_json::to_vec(&key_package_hash_ref) { - Ok(hash_ref) => hash_ref, - Err(_) => return Err(IdentityError::UninitializedIdentity), - }; - + let hash_ref = serialize_key_package_hash_ref(kp.key_package(), &provider)?; // Store the hash reference, keyed with the public init key provider .storage() @@ -408,38 +446,88 @@ impl Identity { provider: &XmtpOpenMlsProvider, api_client: &ApiClientWrapper, ) -> Result<(), IdentityError> { - let stored_identity: Option = provider.conn().fetch(&())?; + let stored_identity: Option = provider.conn_ref().fetch(&())?; if stored_identity.is_some() { info!("Identity already registered. skipping key package publishing"); return Ok(()); } + + self.rotate_key_package(provider, api_client).await?; + self.is_ready.store(true, Ordering::SeqCst); + + Ok(StoredIdentity::try_from(self)?.store(provider.conn_ref())?) + } + + pub(crate) async fn rotate_key_package( + &self, + provider: &XmtpOpenMlsProvider, + api_client: &ApiClientWrapper, + ) -> Result<(), IdentityError> { let kp = self.new_key_package(provider)?; let kp_bytes = kp.tls_serialize_detached()?; - api_client.register_installation(kp_bytes, true).await?; + let conn = provider.conn_ref(); + let hash_ref = serialize_key_package_hash_ref(&kp, provider)?; + let history_id = conn.store_key_package_history_entry(hash_ref)?.id; + let old_id = history_id - 1; + + // Find all key packages that are not the current or previous KPs + // We can delete before uploading because this is either run inside a transaction or is being applied to a brand + // new identity + let old_key_packages = conn.find_key_package_history_entries_before_id(old_id)?; + for kp in old_key_packages { + self.delete_key_package(provider, kp.key_package_hash_ref)?; + } + conn.delete_key_package_history_entries_before_id(old_id)?; + + api_client.upload_key_package(kp_bytes, true).await?; + Ok(()) + } + + pub(crate) fn delete_key_package( + &self, + provider: &XmtpOpenMlsProvider, + hash_ref: Vec, + ) -> Result<(), IdentityError> { + let openmls_hash_ref = deserialize_key_package_hash_ref(&hash_ref)?; + provider.storage().delete_key_package(&openmls_hash_ref)?; - Ok(StoredIdentity::from(self).store(provider.conn_ref())?) + Ok(()) } } +pub(crate) fn serialize_key_package_hash_ref( + kp: &KeyPackage, + provider: &impl OpenMlsProvider, +) -> Result, IdentityError> { + let key_package_hash_ref = kp + .hash_ref(provider.crypto()) + .map_err(|_| IdentityError::UninitializedIdentity)?; + let serialized = bincode::serialize(&key_package_hash_ref) + .map_err(|_| IdentityError::UninitializedIdentity)?; + + Ok(serialized) +} + +fn deserialize_key_package_hash_ref(hash_ref: &[u8]) -> Result { + let key_package_hash_ref: HashReference = + bincode::deserialize(hash_ref).map_err(|_| IdentityError::UninitializedIdentity)?; + + Ok(key_package_hash_ref) +} + async fn sign_with_installation_key( signature_text: String, installation_private_key: &[u8; 32], -) -> Result { +) -> Result { let signing_key: SigningKey = SigningKey::from_bytes(installation_private_key); let verifying_key = signing_key.verifying_key(); let mut prehashed: Sha512 = Sha512::new(); prehashed.update(signature_text.clone()); - let sig = signing_key - .sign_prehashed(prehashed, Some(INSTALLATION_KEY_SIGNATURE_CONTEXT)) - .unwrap(); - - let installation_key_sig = InstallationKeySignature::new( - signature_text.clone(), - sig.to_vec(), - verifying_key.as_bytes().to_vec(), - ); + let sig = signing_key.sign_prehashed(prehashed, Some(INSTALLATION_KEY_SIGNATURE_CONTEXT))?; + let unverified_sig = + UnverifiedInstallationKeySignature::new(sig.to_vec(), verifying_key.as_bytes().to_vec()); - Ok(installation_key_sig) + Ok(unverified_sig) } fn sized_installation_key(installation_key: &[u8]) -> Result<&[u8; 32], IdentityError> { diff --git a/xmtp_mls/src/identity_updates.rs b/xmtp_mls/src/identity_updates.rs index 12647ffdb..40fe9f5dc 100644 --- a/xmtp_mls/src/identity_updates.rs +++ b/xmtp_mls/src/identity_updates.rs @@ -1,13 +1,24 @@ use std::collections::{HashMap, HashSet}; -use crate::storage::association_state::StoredAssociationState; -use prost::Message; +use crate::{ + retry::{Retry, RetryableError}, + retry_async, retryable, + storage::association_state::StoredAssociationState, +}; +use futures::future::try_join_all; use thiserror::Error; -use xmtp_id::associations::{ - apply_update, - builder::{SignatureRequest, SignatureRequestBuilder, SignatureRequestError}, - generate_inbox_id, get_state, AssociationError, AssociationState, AssociationStateDiff, - IdentityUpdate, InstallationKeySignature, MemberIdentifier, +use xmtp_id::{ + associations::{ + apply_update, + builder::{SignatureRequest, SignatureRequestBuilder, SignatureRequestError}, + generate_inbox_id, get_state, + unverified::{ + UnverifiedIdentityUpdate, UnverifiedInstallationKeySignature, UnverifiedSignature, + }, + AssociationError, AssociationState, AssociationStateDiff, IdentityUpdate, MemberIdentifier, + SignatureError, + }, + scw_verifier::SmartContractSignatureVerifier, }; use crate::{ @@ -36,6 +47,14 @@ pub enum InstallationDiffError { Client(#[from] ClientError), } +impl RetryableError for InstallationDiffError { + fn is_retryable(&self) -> bool { + match self { + InstallationDiffError::Client(client_error) => retryable!(client_error), + } + } +} + impl<'a, ApiClient> Client where ApiClient: XmtpApi, @@ -71,6 +90,24 @@ where Ok(needs_update) } + pub async fn batch_get_association_state>( + &self, + conn: &DbConnection, + identifiers: &[(InboxId, Option)], + ) -> Result, ClientError> { + let association_states = try_join_all( + identifiers + .iter() + .map(|(inbox_id, to_sequence_id)| { + self.get_association_state(conn, inbox_id, *to_sequence_id) + }) + .collect::>(), + ) + .await?; + + Ok(association_states) + } + pub async fn get_latest_association_state>( &self, conn: &DbConnection, @@ -94,22 +131,29 @@ where .last() .ok_or::(AssociationError::MissingIdentityUpdate.into())? .sequence_id; - if to_sequence_id.is_some() && to_sequence_id != Some(last_sequence_id) { - return Err(AssociationError::MissingIdentityUpdate.into()); + if let Some(to_sequence_id) = to_sequence_id { + if to_sequence_id != last_sequence_id { + return Err(AssociationError::MissingIdentityUpdate.into()); + } } if let Some(association_state) = StoredAssociationState::read_from_cache(conn, inbox_id.to_string(), last_sequence_id)? { - log::debug!("Loaded association state from cache"); return Ok(association_state); } - let updates = updates + let unverified_updates = updates .into_iter() - .map(IdentityUpdate::try_from) - .collect::, AssociationError>>()?; - let association_state = get_state(updates).await?; + .map(UnverifiedIdentityUpdate::try_from) + .collect::, AssociationError>>()?; + let updates = verify_updates( + unverified_updates, + self.smart_contract_signature_verifier().as_ref(), + ) + .await?; + + let association_state = get_state(updates)?; StoredAssociationState::write_to_cache( conn, @@ -117,7 +161,6 @@ where last_sequence_id, association_state.clone(), )?; - log::debug!("Wrote association state to cache"); Ok(association_state) } @@ -129,6 +172,12 @@ where starting_sequence_id: Option, ending_sequence_id: Option, ) -> Result { + tracing::debug!( + "Computing diff for {:?} from {:?} to {:?}", + inbox_id.as_ref(), + starting_sequence_id, + ending_sequence_id + ); if starting_sequence_id.is_none() { return Ok(self .get_association_state(conn, inbox_id.as_ref(), ending_sequence_id) @@ -140,15 +189,45 @@ where .get_association_state(conn, inbox_id.as_ref(), starting_sequence_id) .await?; - let incremental_updates = conn - .get_identity_updates(inbox_id, starting_sequence_id, ending_sequence_id)? + let incremental_updates = + conn.get_identity_updates(inbox_id.as_ref(), starting_sequence_id, ending_sequence_id)?; + + let last_sequence_id = incremental_updates.last().map(|update| update.sequence_id); + if ending_sequence_id.is_some() + && last_sequence_id.is_some() + && last_sequence_id != ending_sequence_id + { + tracing::error!( + "Did not find the expected last sequence id. Expected: {:?}, Found: {:?}", + ending_sequence_id, + last_sequence_id + ); + return Err(AssociationError::MissingIdentityUpdate.into()); + } + + let unverified_incremental_updates: Vec = incremental_updates .into_iter() .map(|update| update.try_into()) - .collect::, AssociationError>>()?; + .collect::, AssociationError>>()?; + let incremental_updates = verify_updates( + unverified_incremental_updates, + self.smart_contract_signature_verifier().as_ref(), + ) + .await?; let mut final_state = initial_state.clone(); for update in incremental_updates { - final_state = apply_update(final_state, update).await?; + final_state = apply_update(final_state, update)?; + } + + tracing::debug!("Final state at {:?}: {:?}", last_sequence_id, final_state); + if let Some(last_sequence_id) = last_sequence_id { + StoredAssociationState::write_to_cache( + conn, + inbox_id.as_ref().to_string(), + last_sequence_id, + final_state.clone(), + )?; } Ok(initial_state.diff(&final_state)) @@ -170,14 +249,19 @@ where .add_association(installation_public_key.to_vec().into(), member_identifier) .build(); + let sig_bytes = self + .identity() + .sign(signature_request.signature_text())? + .to_vec(); // We can pre-sign the request with an installation key signature, since we have access to the key signature_request - .add_signature(Box::new(InstallationKeySignature::new( - signature_request.signature_text(), - // TODO: Move this to a method on the new identity - self.identity().sign(signature_request.signature_text())?, - self.installation_public_key(), - ))) + .add_signature( + UnverifiedSignature::InstallationKey(UnverifiedInstallationKeySignature::new( + sig_bytes, + installation_public_key.to_vec(), + )), + self.smart_contract_signature_verifier().as_ref(), + ) .await?; Ok(signature_request) @@ -185,11 +269,11 @@ where pub fn associate_wallet( &self, - // TODO: Replace this argument with a value stored on the client - inbox_id: String, existing_wallet_address: String, new_wallet_address: String, ) -> Result { + tracing::info!("Associating new wallet with inbox_id {}", self.inbox_id()); + let inbox_id = self.inbox_id(); let builder = SignatureRequestBuilder::new(inbox_id); Ok(builder @@ -197,22 +281,54 @@ where .build()) } - pub async fn revoke_wallet( + pub async fn revoke_wallets( &self, - inbox_id: String, - wallet_to_revoke: String, + wallets_to_revoke: Vec, ) -> Result { - let current_state = self - .get_association_state(&self.store().conn()?, &inbox_id, None) - .await?; - let builder = SignatureRequestBuilder::new(inbox_id); + let inbox_id = self.inbox_id(); + let current_state = retry_async!( + Retry::default(), + (async { + self.get_association_state(&self.store().conn()?, &inbox_id, None) + .await + }) + )?; + let mut builder = SignatureRequestBuilder::new(inbox_id); - Ok(builder - .revoke_association( + for wallet in wallets_to_revoke { + builder = builder.revoke_association( current_state.recovery_address().clone().into(), - wallet_to_revoke.into(), + wallet.into(), ) - .build()) + } + + Ok(builder.build()) + } + + pub async fn revoke_installations( + &self, + installation_ids: Vec>, + ) -> Result { + let inbox_id = self.inbox_id(); + + let current_state = retry_async!( + Retry::default(), + (async { + self.get_association_state(&self.store().conn()?, &inbox_id, None) + .await + }) + )?; + + let mut builder = SignatureRequestBuilder::new(inbox_id); + + for installation_id in installation_ids { + builder = builder.revoke_association( + current_state.recovery_address().clone().into(), + installation_id.into(), + ) + } + + Ok(builder.build()) } pub async fn apply_signature_request( @@ -231,7 +347,17 @@ where .await?; // Load the identity updates for the inbox so that we have a record in our DB - load_identity_updates(&self.api_client, &self.store().conn()?, vec![inbox_id]).await?; + retry_async!( + Retry::default(), + (async { + load_identity_updates( + &self.api_client, + &self.store().conn()?, + vec![inbox_id.clone()], + ) + .await + }) + )?; Ok(()) } @@ -245,7 +371,7 @@ where new_group_membership: &GroupMembership, membership_diff: &MembershipDiff<'_>, ) -> Result { - log::info!( + tracing::info!( "Getting installation diff. Old: {:?}. New {:?}", old_group_membership, new_group_membership @@ -327,6 +453,7 @@ pub async fn load_identity_updates( if inbox_ids.is_empty() { return Ok(HashMap::new()); } + tracing::debug!("Fetching identity updates for: {:?}", inbox_ids); let existing_sequence_ids = conn.get_latest_sequence_id(&inbox_ids)?; let filters: Vec = inbox_ids @@ -350,7 +477,7 @@ pub async fn load_identity_updates( inbox_id: inbox_id.clone(), sequence_id: update.sequence_id as i64, server_timestamp_ns: update.server_timestamp_ns as i64, - payload: update.update.to_proto().encode_to_vec(), + payload: update.update.into(), }) }) .collect::>(); @@ -359,13 +486,26 @@ pub async fn load_identity_updates( Ok(updates) } +async fn verify_updates( + updates: Vec, + scw_verifier: &dyn SmartContractSignatureVerifier, +) -> Result, SignatureError> { + try_join_all( + updates + .iter() + .map(|update| update.to_verified(scw_verifier)), + ) + .await +} + #[cfg(test)] -mod tests { - use ethers::signers::LocalWallet; - use tracing_test::traced_test; +pub(crate) mod tests { use xmtp_cryptography::utils::generate_local_wallet; use xmtp_id::{ - associations::{builder::SignatureRequest, AssociationState, RecoverableEcdsaSignature}, + associations::{ + builder::SignatureRequest, test_utils::add_wallet_signature, AssociationState, + MemberIdentifier, + }, InboxOwner, }; @@ -380,21 +520,6 @@ mod tests { use super::load_identity_updates; - async fn sign_with_wallet(wallet: &LocalWallet, signature_request: &mut SignatureRequest) { - let wallet_signature: Vec = wallet - .sign(signature_request.signature_text().as_str()) - .unwrap() - .into(); - - signature_request - .add_signature(Box::new(RecoverableEcdsaSignature::new( - signature_request.signature_text(), - wallet_signature, - ))) - .await - .unwrap(); - } - async fn get_association_state( client: &Client, inbox_id: String, @@ -433,7 +558,7 @@ mod tests { .unwrap(); let inbox_id = signature_request.inbox_id(); - sign_with_wallet(&wallet, &mut signature_request).await; + add_wallet_signature(&mut signature_request, &wallet).await; client .apply_signature_request(signature_request) @@ -455,109 +580,84 @@ mod tests { let wallet_2_address = wallet_2.get_address(); let client = ClientBuilder::new_test_client(&wallet).await; - let mut signature_request: SignatureRequest = client - .create_inbox(wallet_address.clone(), None) - .await - .unwrap(); - let inbox_id = signature_request.inbox_id(); - - sign_with_wallet(&wallet, &mut signature_request).await; - - client - .apply_signature_request(signature_request) - .await - .unwrap(); - let mut add_association_request = client - .associate_wallet( - inbox_id.clone(), - wallet_address.clone(), - wallet_2_address.clone(), - ) + .associate_wallet(wallet_address.clone(), wallet_2_address.clone()) .unwrap(); - sign_with_wallet(&wallet, &mut add_association_request).await; - sign_with_wallet(&wallet_2, &mut add_association_request).await; + add_wallet_signature(&mut add_association_request, &wallet).await; + add_wallet_signature(&mut add_association_request, &wallet_2).await; client .apply_signature_request(add_association_request) .await .unwrap(); - let association_state = get_association_state(&client, inbox_id.clone()).await; + let association_state = get_association_state(&client, client.inbox_id()).await; + + let members = + association_state.members_by_parent(&MemberIdentifier::Address(wallet_address.clone())); + // Those members should have timestamps + for member in members { + assert!(member.client_timestamp_ns.is_some()); + } assert_eq!(association_state.members().len(), 3); assert_eq!(association_state.recovery_address(), &wallet_address); assert!(association_state.get(&wallet_2_address.into()).is_some()); } - #[tokio::test] - #[traced_test] - async fn cache_association_state() { - let wallet = generate_local_wallet(); - let wallet_2 = generate_local_wallet(); - let wallet_address = wallet.get_address(); - let wallet_2_address = wallet_2.get_address(); - let client = ClientBuilder::new_test_client(&wallet).await; - - let mut signature_request: SignatureRequest = client - .create_inbox(wallet_address.clone(), None) - .await - .unwrap(); - let inbox_id = signature_request.inbox_id(); + #[test] + fn cache_association_state() { + crate::traced_test(|| async { + let wallet = generate_local_wallet(); + let wallet_2 = generate_local_wallet(); + let wallet_address = wallet.get_address(); + let wallet_2_address = wallet_2.get_address(); + let client = ClientBuilder::new_test_client(&wallet).await; + let inbox_id = client.inbox_id(); - sign_with_wallet(&wallet, &mut signature_request).await; + get_association_state(&client, inbox_id.clone()).await; - client - .apply_signature_request(signature_request) - .await - .unwrap(); + assert_logged!("Loaded association", 0); + assert_logged!("Wrote association", 1); - get_association_state(&client, inbox_id.clone()).await; + let association_state = get_association_state(&client, inbox_id.clone()).await; - assert_logged!("Loaded association", 0); - assert_logged!("Wrote association", 1); + assert_eq!(association_state.members().len(), 2); + assert_eq!(association_state.recovery_address(), &wallet_address); + assert!(association_state + .get(&wallet_address.clone().into()) + .is_some()); - let association_state = get_association_state(&client, inbox_id.clone()).await; + assert_logged!("Loaded association", 1); + assert_logged!("Wrote association", 1); - assert_eq!(association_state.members().len(), 2); - assert_eq!(association_state.recovery_address(), &wallet_address); - assert!(association_state - .get(&wallet_address.clone().into()) - .is_some()); - - assert_logged!("Loaded association", 1); - assert_logged!("Wrote association", 1); - - let mut add_association_request = client - .associate_wallet( - inbox_id.clone(), - wallet_address.clone(), - wallet_2_address.clone(), - ) - .unwrap(); + let mut add_association_request = client + .associate_wallet(wallet_address.clone(), wallet_2_address.clone()) + .unwrap(); - sign_with_wallet(&wallet, &mut add_association_request).await; - sign_with_wallet(&wallet_2, &mut add_association_request).await; + add_wallet_signature(&mut add_association_request, &wallet).await; + add_wallet_signature(&mut add_association_request, &wallet_2).await; - client - .apply_signature_request(add_association_request) - .await - .unwrap(); + client + .apply_signature_request(add_association_request) + .await + .unwrap(); - get_association_state(&client, inbox_id.clone()).await; + get_association_state(&client, inbox_id.clone()).await; - assert_logged!("Loaded association", 1); - assert_logged!("Wrote association", 2); + assert_logged!("Loaded association", 1); + assert_logged!("Wrote association", 2); - let association_state = get_association_state(&client, inbox_id.clone()).await; + let association_state = get_association_state(&client, inbox_id.clone()).await; - assert_logged!("Loaded association", 2); - assert_logged!("Wrote association", 2); + assert_logged!("Loaded association", 2); + assert_logged!("Wrote association", 2); - assert_eq!(association_state.members().len(), 3); - assert_eq!(association_state.recovery_address(), &wallet_address); - assert!(association_state.get(&wallet_2_address.into()).is_some()); + assert_eq!(association_state.members().len(), 3); + assert_eq!(association_state.recovery_address(), &wallet_address); + assert!(association_state.get(&wallet_2_address.into()).is_some()); + }); } #[tokio::test] @@ -605,18 +705,18 @@ mod tests { let inbox_id = signature_request.inbox_id(); inbox_ids.push(inbox_id.clone()); - sign_with_wallet(&wallet, &mut signature_request).await; + add_wallet_signature(&mut signature_request, &wallet).await; client .apply_signature_request(signature_request) .await .unwrap(); let new_wallet = generate_local_wallet(); let mut add_association_request = client - .associate_wallet(inbox_id, wallet.get_address(), new_wallet.get_address()) + .associate_wallet(wallet.get_address(), new_wallet.get_address()) .unwrap(); - sign_with_wallet(&wallet, &mut add_association_request).await; - sign_with_wallet(&new_wallet, &mut add_association_request).await; + add_wallet_signature(&mut add_association_request, &wallet).await; + add_wallet_signature(&mut add_association_request, &new_wallet).await; client .apply_signature_request(add_association_request) @@ -684,4 +784,86 @@ mod tests { .removed_installations .contains(&client_2_installation_key)); } + + #[tokio::test] + pub async fn revoke_wallet() { + let recovery_wallet = generate_local_wallet(); + let second_wallet = generate_local_wallet(); + let client = ClientBuilder::new_test_client(&recovery_wallet).await; + + let mut add_wallet_signature_request = client + .associate_wallet(recovery_wallet.get_address(), second_wallet.get_address()) + .unwrap(); + + add_wallet_signature(&mut add_wallet_signature_request, &recovery_wallet).await; + add_wallet_signature(&mut add_wallet_signature_request, &second_wallet).await; + + client + .apply_signature_request(add_wallet_signature_request) + .await + .unwrap(); + + let association_state_after_add = get_association_state(&client, client.inbox_id()).await; + assert_eq!(association_state_after_add.account_addresses().len(), 2); + + // Make sure the inbox ID is correctly registered + let inbox_ids = client + .api_client + .get_inbox_ids(vec![second_wallet.get_address()]) + .await + .unwrap(); + assert_eq!(inbox_ids.len(), 1); + + // Now revoke the second wallet + + let mut revoke_signature_request = client + .revoke_wallets(vec![second_wallet.get_address()]) + .await + .unwrap(); + add_wallet_signature(&mut revoke_signature_request, &recovery_wallet).await; + + client + .apply_signature_request(revoke_signature_request) + .await + .unwrap(); + + // Make sure that the association state has removed the second wallet + let association_state_after_revoke = + get_association_state(&client, client.inbox_id()).await; + assert_eq!(association_state_after_revoke.account_addresses().len(), 1); + + // Make sure the inbox ID is correctly unregistered + let inbox_ids = client + .api_client + .get_inbox_ids(vec![second_wallet.get_address()]) + .await + .unwrap(); + assert_eq!(inbox_ids.len(), 0); + } + + #[tokio::test] + pub async fn revoke_installation() { + let wallet = generate_local_wallet(); + let client1 = ClientBuilder::new_test_client(&wallet).await; + let client2 = ClientBuilder::new_test_client(&wallet).await; + + let association_state = get_association_state(&client1, client1.inbox_id()).await; + // Ensure there are two installations on the inbox + assert_eq!(association_state.installation_ids().len(), 2); + + // Now revoke the second client + let mut revoke_installation_request = client1 + .revoke_installations(vec![client2.installation_public_key()]) + .await + .unwrap(); + add_wallet_signature(&mut revoke_installation_request, &wallet).await; + client1 + .apply_signature_request(revoke_installation_request) + .await + .unwrap(); + + // Make sure there is only one installation on the inbox + let association_state = get_association_state(&client1, client1.inbox_id()).await; + assert_eq!(association_state.installation_ids().len(), 1); + } } diff --git a/xmtp_mls/src/lib.rs b/xmtp_mls/src/lib.rs index e7bded9b7..4d847e6bb 100644 --- a/xmtp_mls/src/lib.rs +++ b/xmtp_mls/src/lib.rs @@ -1,40 +1,104 @@ +#![recursion_limit = "256"] +#![warn(clippy::unwrap_used)] + pub mod api; pub mod builder; pub mod client; pub mod codecs; pub mod configuration; -pub mod credential; pub mod groups; mod hpke; pub mod identity; mod identity_updates; -pub mod owner; +mod mutex_registry; pub mod retry; pub mod storage; pub mod subscriptions; pub mod types; pub mod utils; -pub mod verified_key_package; pub mod verified_key_package_v2; mod xmtp_openmls_provider; pub use client::{Client, Network}; use storage::StorageError; -use xmtp_cryptography::signature::{RecoverableSignature, SignatureError}; -use xmtp_proto::api_client::{XmtpIdentityClient, XmtpMlsClient}; + +pub use trait_impls::*; /// XMTP Api Super Trait /// Implements all Trait Network APIs for convenience. -pub trait XmtpApi: XmtpMlsClient + XmtpIdentityClient {} -impl XmtpApi for T where T: XmtpMlsClient + XmtpIdentityClient {} - -pub trait InboxOwner { - /// Get address of the wallet. - fn get_address(&self) -> String; - /// Sign text with the wallet. - fn sign(&self, text: &str) -> Result; +mod trait_impls { + pub use inner::*; + + // native, release + #[cfg(not(test))] + mod inner { + use xmtp_proto::api_client::{ + ClientWithMetadata, XmtpIdentityClient, XmtpMlsClient, XmtpMlsStreams, + }; + + pub trait XmtpApi + where + Self: XmtpMlsClient + + XmtpMlsStreams + + XmtpIdentityClient + + ClientWithMetadata + + Send + + Sync, + { + } + impl XmtpApi for T where + T: XmtpMlsClient + + XmtpMlsStreams + + XmtpIdentityClient + + ClientWithMetadata + + Send + + Sync + + ?Sized + { + } + } + + // test, native + #[cfg(test)] + mod inner { + use xmtp_proto::api_client::{ + ClientWithMetadata, XmtpIdentityClient, XmtpMlsClient, XmtpMlsStreams, + }; + + pub trait XmtpApi + where + Self: XmtpMlsClient + + XmtpMlsStreams + + XmtpIdentityClient + + crate::XmtpTestClient + + ClientWithMetadata + + Send + + Sync, + { + } + impl XmtpApi for T where + T: XmtpMlsClient + + XmtpMlsStreams + + XmtpIdentityClient + + crate::XmtpTestClient + + ClientWithMetadata + + Send + + Sync + + ?Sized + { + } + } } +#[cfg(any(test, feature = "test-utils", feature = "bench"))] +#[trait_variant::make(XmtpTestClient: Send)] +pub trait LocalXmtpTestClient { + async fn create_local() -> Self; + async fn create_dev() -> Self; +} + +pub use xmtp_id::InboxOwner; + /// Inserts a model to the underlying data store, erroring if it already exists pub trait Store { fn store(&self, into: &StorageConnection) -> Result<(), StorageError>; @@ -57,33 +121,136 @@ pub trait Delete { fn delete(&self, key: Self::Key) -> Result; } +#[cfg(test)] +pub use self::tests::traced_test; + #[cfg(test)] mod tests { - use tracing_test::traced_test; + use parking_lot::Mutex; + use std::{io, sync::Arc}; + use tracing_subscriber::{ + filter::EnvFilter, + fmt::{self, MakeWriter}, + prelude::*, + }; // Execute once before any tests are run #[ctor::ctor] - // Capture traces in a variable that can be checked in tests, as well as outputting them to stdout on test failure - #[traced_test] fn setup() { - // Capture logs (e.g. log::info!()) as traces too - let _ = tracing_log::LogTracer::init(); + let filter = EnvFilter::builder() + .with_default_directive(tracing::metadata::LevelFilter::INFO.into()) + .from_env_lossy(); + + tracing_subscriber::registry() + .with(fmt::layer()) + .with(filter) + .init(); + } + + thread_local! { + pub static LOG_BUFFER: TestWriter = TestWriter::new(); } - /// Note: tests that use this must have the #[traced_test] attribute + /// Thread local writer which stores logs in memory + pub struct TestWriter(Arc>>); + impl TestWriter { + pub fn new() -> Self { + Self(Arc::new(Mutex::new(vec![]))) + } + + pub fn as_string(&self) -> String { + let buf = self.0.lock(); + String::from_utf8(buf.clone()).expect("Not valid UTF-8") + } + + pub fn clear(&self) { + let mut buf = self.0.lock(); + buf.clear(); + } + pub fn flush(&self) { + let mut buf = self.0.lock(); + std::io::Write::flush(&mut *buf).unwrap(); + } + } + + impl io::Write for TestWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + let mut this = self.0.lock(); + // still print logs for tests + print!("{}", String::from_utf8_lossy(buf)); + Vec::::write(&mut this, buf) + } + + fn flush(&mut self) -> io::Result<()> { + let mut this = self.0.lock(); + Vec::::flush(&mut this) + } + } + + impl Clone for TestWriter { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + } + + impl MakeWriter<'_> for TestWriter { + type Writer = TestWriter; + + fn make_writer(&self) -> Self::Writer { + self.clone() + } + } + + /// Only works with current-thread + pub fn traced_test(f: impl Fn() -> Fut) + where + Fut: futures::Future, + { + LOG_BUFFER.with(|buf| { + let rt = tokio::runtime::Builder::new_current_thread() + .thread_name("tracing-test") + .enable_time() + .enable_io() + .build() + .unwrap(); + buf.clear(); + + let subscriber = fmt::Subscriber::builder() + .with_env_filter(format!("{}=debug", env!("CARGO_PKG_NAME"))) + .with_writer(buf.clone()) + .with_level(true) + .with_ansi(false) + .finish(); + + let dispatch = tracing::Dispatch::new(subscriber); + tracing::dispatcher::with_default(&dispatch, || { + rt.block_on(f()); + }); + + buf.clear(); + }); + } + + /// macro that can assert logs in tests. + /// Note: tests that use this must be used in `traced_test` function + /// and only with tokio's `current` runtime. #[macro_export] macro_rules! assert_logged { ( $search:expr , $occurrences:expr ) => { - logs_assert(|lines: &[&str]| { - let actual = lines.iter().filter(|line| line.contains($search)).count(); + $crate::tests::LOG_BUFFER.with(|buf| { + let lines = { + buf.flush(); + buf.as_string() + }; + let lines = lines.lines(); + let actual = lines.filter(|line| line.contains($search)).count(); if actual != $occurrences { - return Err(format!( + panic!( "Expected '{}' to be logged {} times, but was logged {} times instead", $search, $occurrences, actual - )); + ); } - Ok(()) - }); + }) }; } @@ -95,7 +262,7 @@ mod tests { #[macro_export] macro_rules! assert_err { ( $x:expr , $y:pat $(,)? ) => { - assert!(matches!($x, Err($y))); + assert!(matches!($x, Err($y))) }; ( $x:expr, $y:pat $(,)?, $($msg:tt)+) => {{ diff --git a/xmtp_mls/src/mutex_registry.rs b/xmtp_mls/src/mutex_registry.rs new file mode 100644 index 000000000..0946d0bdb --- /dev/null +++ b/xmtp_mls/src/mutex_registry.rs @@ -0,0 +1,23 @@ +use std::{collections::HashMap, sync::Arc}; + +use tokio::sync::Mutex; + +#[derive(Debug, Clone)] +pub struct MutexRegistry { + mutexes: HashMap, Arc>>, +} + +impl MutexRegistry { + pub fn new() -> Self { + Self { + mutexes: HashMap::new(), + } + } + + pub fn get_mutex(&mut self, key: Vec) -> Arc> { + self.mutexes + .entry(key) + .or_insert_with(|| Arc::new(Mutex::new(()))) + .clone() + } +} diff --git a/xmtp_mls/src/owner/evm_owner.rs b/xmtp_mls/src/owner/evm_owner.rs deleted file mode 100644 index 2832c8dda..000000000 --- a/xmtp_mls/src/owner/evm_owner.rs +++ /dev/null @@ -1,16 +0,0 @@ -pub use ethers::signers::{LocalWallet, Signer}; -use futures::executor; - -use xmtp_cryptography::signature::{h160addr_to_string, RecoverableSignature, SignatureError}; - -use crate::InboxOwner; - -impl InboxOwner for LocalWallet { - fn get_address(&self) -> String { - h160addr_to_string(self.address()) - } - - fn sign(&self, text: &str) -> Result { - Ok(executor::block_on(self.sign_message(text))?.to_vec().into()) - } -} diff --git a/xmtp_mls/src/owner/mod.rs b/xmtp_mls/src/owner/mod.rs deleted file mode 100644 index 1b15ea292..000000000 --- a/xmtp_mls/src/owner/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod evm_owner; diff --git a/xmtp_mls/src/retry.rs b/xmtp_mls/src/retry.rs index 4fbdf7468..6b3140e5e 100644 --- a/xmtp_mls/src/retry.rs +++ b/xmtp_mls/src/retry.rs @@ -18,26 +18,33 @@ use std::time::Duration; -use smart_default::SmartDefault; +use rand::Rng; /// Specifies which errors are retryable. /// All Errors are not retryable by-default. pub trait RetryableError: std::error::Error { - fn is_retryable(&self) -> bool { - false - } + fn is_retryable(&self) -> bool; } -// we use &T and make use of autoref specialization -impl RetryableError for &T where T: std::error::Error {} - /// Options to specify how to retry a function -#[derive(SmartDefault, Debug, PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct Retry { - #[default = 3] retries: usize, - #[default(_code = "std::time::Duration::from_millis(100)")] duration: std::time::Duration, + // The amount to multiply the duration on each subsequent attempt + multiplier: u32, + max_jitter_ms: usize, +} + +impl Default for Retry { + fn default() -> Self { + Self { + retries: 5, + duration: std::time::Duration::from_millis(50), + multiplier: 3, + max_jitter_ms: 25, + } + } } impl Retry { @@ -47,8 +54,16 @@ impl Retry { } /// Get the duration to wait between retries. - pub fn duration(&self) -> Duration { - self.duration + /// Multiples the duration by the multiplier for each subsequent attempt + /// and adds a random jitter to avoid repeated collisions + pub fn duration(&self, attempts: usize) -> Duration { + let mut duration = self.duration; + for _ in 0..attempts - 1 { + duration *= self.multiplier; + } + + let jitter = rand::thread_rng().gen_range(0..=self.max_jitter_ms); + duration + Duration::from_millis(jitter as u64) } } @@ -106,80 +121,11 @@ impl Retry { } } -/// Retry a function, specifying the strategy with $retry. -/// -/// # Example -/// ``` -/// use thiserror::Error; -/// use xmtp_mls::{retry_sync, retry::{RetryableError, Retry}}; -/// -/// #[derive(Debug, Error)] -/// enum MyError { -/// #[error("A retryable error")] -/// Retryable, -/// #[error("An error we don't want to retry")] -/// NotRetryable -/// } -/// -/// impl RetryableError for MyError { -/// fn is_retryable(&self) -> bool { -/// match self { -/// Self::Retryable => true, -/// _=> false, -/// } -/// } -/// } -/// -/// fn fallable_fn(i: usize) -> Result<(), MyError> { -/// if i == 2 { -/// return Ok(()); -/// } -/// -/// Err(MyError::Retryable) -/// } -/// -/// fn main() { -/// let mut i = 0; -/// retry_sync!(Retry::default(), (|| -> Result<(), MyError> { -/// let res = fallable_fn(i); -/// i += 1; -/// res -/// })).unwrap(); -/// -/// } -/// ``` -#[macro_export] -macro_rules! retry_sync { - ($retry: expr, $code: tt) => {{ - #[allow(unused)] - use $crate::retry::RetryableError; - let mut attempts = 0; - tracing::trace_span!("retry").in_scope(|| loop { - #[allow(clippy::redundant_closure_call)] - match $code() { - Ok(v) => break Ok(v), - Err(e) => { - if (&e).is_retryable() && attempts < $retry.retries() { - log::debug!( - "retrying function that failed with error=`{}`", - e.to_string() - ); - attempts += 1; - std::thread::sleep($retry.duration()); - } else { - break Err(e); - } - } - } - }) - }}; -} - /// Retry but for an async context /// ``` /// use xmtp_mls::{retry_async, retry::{RetryableError, Retry}}; /// use thiserror::Error; -/// use flume::bounded; +/// use tokio::sync::mpsc; /// /// #[derive(Debug, Error)] /// enum MyError { @@ -198,8 +144,8 @@ macro_rules! retry_sync { /// } /// } /// -/// async fn fallable_fn(rx: &flume::Receiver) -> Result<(), MyError> { -/// if rx.recv_async().await.unwrap() == 2 { +/// async fn fallable_fn(rx: &mut mpsc::Receiver) -> Result<(), MyError> { +/// if rx.recv().await.unwrap() == 2 { /// return Ok(()); /// } /// Err(MyError::Retryable) @@ -207,14 +153,14 @@ macro_rules! retry_sync { /// /// #[tokio::main] /// async fn main() -> Result<(), MyError> { -/// -/// let (tx, rx) = flume::bounded(3); +/// +/// let (tx, mut rx) = mpsc::channel(3); /// /// for i in 0..3 { -/// tx.send(i).unwrap(); +/// tx.send(i).await.unwrap(); /// } /// retry_async!(Retry::default(), (async { -/// fallable_fn(&rx.clone()).await +/// fallable_fn(&mut rx).await /// })) /// } /// ``` @@ -229,14 +175,19 @@ macro_rules! retry_async { loop { let span = span.clone(); #[allow(clippy::redundant_closure_call)] - match $code.instrument(span).await { + let res = $code.instrument(span).await; + match res { Ok(v) => break Ok(v), Err(e) => { if (&e).is_retryable() && attempts < $retry.retries() { - log::debug!("retrying function that failed with error={}", e.to_string()); + tracing::warn!( + "retrying function that failed with error={}", + e.to_string() + ); attempts += 1; - tokio::time::sleep($retry.duration()).await; + tokio::time::sleep($retry.duration(attempts)).await; } else { + tracing::info!("error is not retryable. {:?}", e); break Err(e); } } @@ -250,11 +201,11 @@ macro_rules! retryable { ($error: ident) => {{ #[allow(unused)] use $crate::retry::RetryableError; - (&$error).is_retryable() + $error.is_retryable() }}; ($error: expr) => {{ use $crate::retry::RetryableError; - (&$error).is_retryable() + $error.is_retryable() }}; } @@ -269,6 +220,7 @@ impl RetryableError for xmtp_proto::api_client::Error { mod tests { use super::*; use thiserror::Error; + use tokio::sync::mpsc; #[derive(Debug, Error)] enum SomeError { @@ -293,8 +245,8 @@ mod tests { Err(SomeError::ARetryableError) } - #[test] - fn it_retries_twice_and_succeeds() { + #[tokio::test] + async fn it_retries_twice_and_succeeds() { let mut i = 0; let mut test_fn = || -> Result<(), SomeError> { if i == 2 { @@ -305,11 +257,11 @@ mod tests { Ok(()) }; - retry_sync!(Retry::default(), test_fn).unwrap(); + retry_async!(Retry::default(), (async { test_fn() })).unwrap(); } - #[test] - fn it_works_with_random_args() { + #[tokio::test] + async fn it_works_with_random_args() { let mut i = 0; let list = vec!["String".into(), "Foo".into()]; let mut test_fn = || -> Result<(), SomeError> { @@ -320,37 +272,37 @@ mod tests { retryable_with_args(i, "Hello".to_string(), &list) }; - retry_sync!(Retry::default(), test_fn).unwrap(); + retry_async!(Retry::default(), (async { test_fn() })).unwrap(); } - #[test] - fn it_fails_on_three_retries() { + #[tokio::test] + async fn it_fails_on_three_retries() { let closure = || -> Result<(), SomeError> { retry_error_fn()?; Ok(()) }; - let result: Result<(), SomeError> = retry_sync!(Retry::default(), (closure)); + let result: Result<(), SomeError> = retry_async!(Retry::default(), (async { closure() })); assert!(result.is_err()) } - #[test] - fn it_only_runs_non_retryable_once() { + #[tokio::test] + async fn it_only_runs_non_retryable_once() { let mut attempts = 0; let mut test_fn = || -> Result<(), SomeError> { attempts += 1; Err(SomeError::DontRetryThis) }; - let _r = retry_sync!(Retry::default(), test_fn); + let _r = retry_async!(Retry::default(), (async { test_fn() })); assert_eq!(attempts, 1); } #[tokio::test] async fn it_works_async() { - async fn retryable_async_fn(rx: &flume::Receiver) -> Result<(), SomeError> { - let val = rx.recv_async().await.unwrap(); + async fn retryable_async_fn(rx: &mut mpsc::Receiver) -> Result<(), SomeError> { + let val = rx.recv().await.unwrap(); if val == 2 { return Ok(()); } @@ -359,14 +311,14 @@ mod tests { Err(SomeError::ARetryableError) } - let (tx, rx) = flume::bounded(3); + let (tx, mut rx) = mpsc::channel(3); for i in 0..3 { - tx.send(i).unwrap(); + tx.send(i).await.unwrap(); } retry_async!( Retry::default(), - (async { retryable_async_fn(&rx.clone()).await }) + (async { retryable_async_fn(&mut rx).await }) ) .unwrap(); assert!(rx.is_empty()); @@ -391,4 +343,13 @@ mod tests { ) .unwrap(); } + + #[test] + fn backoff_retry() { + let backoff_retry = Retry::default(); + + assert!(backoff_retry.duration(1).as_millis() - 50 <= 25); + assert!(backoff_retry.duration(2).as_millis() - 150 <= 25); + assert!(backoff_retry.duration(3).as_millis() - 450 <= 25); + } } diff --git a/xmtp_mls/src/storage/encrypted_store/association_state.rs b/xmtp_mls/src/storage/encrypted_store/association_state.rs index 9ff51cca1..a13398dba 100644 --- a/xmtp_mls/src/storage/encrypted_store/association_state.rs +++ b/xmtp_mls/src/storage/encrypted_store/association_state.rs @@ -40,12 +40,22 @@ impl StoredAssociationState { state: AssociationState, ) -> Result<(), StorageError> { let state_proto: AssociationStateProto = state.into(); - StoredAssociationState { - inbox_id, + let result = StoredAssociationState { + inbox_id: inbox_id.clone(), sequence_id, state: state_proto.encode_to_vec(), } - .store_or_ignore(conn) + .store_or_ignore(conn); + + if result.is_ok() { + tracing::debug!( + "Wrote association state to cache: {} {}", + inbox_id, + sequence_id + ); + } + + result } pub fn read_from_cache( @@ -56,7 +66,7 @@ impl StoredAssociationState { let stored_state: Option = conn.fetch(&(inbox_id.to_string(), sequence_id))?; - stored_state + let result = stored_state .map(|stored_state| { stored_state .try_into() @@ -66,14 +76,23 @@ impl StoredAssociationState { )) }) }) - .transpose() + .transpose(); + + if let Ok(Some(_)) = result { + tracing::debug!( + "Loaded association state from cache: {} {}", + inbox_id, + sequence_id + ); + } + + result } pub fn batch_read_from_cache( conn: &DbConnection, identifiers: Vec<(InboxId, i64)>, ) -> Result, StorageError> { - // If no identifier provided, return empty hash map if identifiers.is_empty() { return Ok(vec![]); } diff --git a/xmtp_mls/src/storage/encrypted_store/consent_record.rs b/xmtp_mls/src/storage/encrypted_store/consent_record.rs new file mode 100644 index 000000000..1a6cd5ef4 --- /dev/null +++ b/xmtp_mls/src/storage/encrypted_store/consent_record.rs @@ -0,0 +1,196 @@ +use crate::{impl_store, storage::StorageError}; + +use super::{ + db_connection::DbConnection, + schema::consent_records::{self, dsl}, +}; +use diesel::{ + backend::Backend, + deserialize::{self, FromSql, FromSqlRow}, + expression::AsExpression, + prelude::*, + serialize::{self, IsNull, Output, ToSql}, + sql_types::Integer, + sqlite::Sqlite, + upsert::excluded, +}; +use serde::{Deserialize, Serialize}; + +/// StoredConsentRecord holds a serialized ConsentRecord +#[derive(Insertable, Queryable, Debug, Clone, PartialEq, Eq)] +#[diesel(table_name = consent_records)] +#[diesel(primary_key(entity_type, entity))] +pub struct StoredConsentRecord { + /// Enum, [`ConsentType`] representing the type of consent (group_id inbox_id, etc..) + pub entity_type: ConsentType, + /// Enum, [`ConsentState`] representing the state of consent (allowed, denied, etc..) + pub state: ConsentState, + /// The entity of what was consented (0x00 etc..) + pub entity: String, +} + +impl StoredConsentRecord { + pub fn new(entity_type: ConsentType, state: ConsentState, entity: String) -> Self { + Self { + entity_type, + state, + entity, + } + } +} + +impl_store!(StoredConsentRecord, consent_records); + +impl DbConnection { + /// Returns the consent_records for the given entity up + pub fn get_consent_record( + &self, + entity: String, + entity_type: ConsentType, + ) -> Result, StorageError> { + Ok(self.raw_query(|conn| { + dsl::consent_records + .filter(dsl::entity.eq(entity)) + .filter(dsl::entity_type.eq(entity_type)) + .first(conn) + .optional() + })?) + } + + /// Insert consent_records, and replace existing entries + pub fn insert_or_replace_consent_records( + &self, + records: Vec, + ) -> Result<(), StorageError> { + self.raw_query(|conn| { + conn.transaction::<_, diesel::result::Error, _>(|conn| { + for record in records.iter() { + diesel::insert_into(dsl::consent_records) + .values(record) + .on_conflict((dsl::entity_type, dsl::entity)) + .do_update() + .set(dsl::state.eq(excluded(dsl::state))) + .execute(conn)?; + } + Ok(()) + }) + })?; + + Ok(()) + } +} + +#[repr(i32)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, Eq, PartialEq, AsExpression, FromSqlRow)] +#[diesel(sql_type = Integer)] +/// Type of consent record stored +pub enum ConsentType { + /// Consent is for a group + GroupId = 1, + /// Consent is for an inbox + InboxId = 2, + /// Consent is for an address + Address = 3, +} + +impl ToSql for ConsentType +where + i32: ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result { + out.set_value(*self as i32); + Ok(IsNull::No) + } +} + +impl FromSql for ConsentType +where + i32: FromSql, +{ + fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { + match i32::from_sql(bytes)? { + 1 => Ok(ConsentType::GroupId), + 2 => Ok(ConsentType::InboxId), + 3 => Ok(ConsentType::Address), + x => Err(format!("Unrecognized variant {}", x).into()), + } + } +} + +#[repr(i32)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, Eq, PartialEq, AsExpression, FromSqlRow)] +#[diesel(sql_type = Integer)] +/// The state of the consent +pub enum ConsentState { + /// Consent is unknown + Unknown = 0, + /// Consent is allowed + Allowed = 1, + /// Consent is denied + Denied = 2, +} + +impl ToSql for ConsentState +where + i32: ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result { + out.set_value(*self as i32); + Ok(IsNull::No) + } +} + +impl FromSql for ConsentState +where + i32: FromSql, +{ + fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { + match i32::from_sql(bytes)? { + 0 => Ok(ConsentState::Unknown), + 1 => Ok(ConsentState::Allowed), + 2 => Ok(ConsentState::Denied), + x => Err(format!("Unrecognized variant {}", x).into()), + } + } +} + +#[cfg(test)] +mod tests { + use crate::storage::encrypted_store::tests::with_connection; + + use super::*; + + fn generate_consent_record( + entity_type: ConsentType, + state: ConsentState, + entity: String, + ) -> StoredConsentRecord { + StoredConsentRecord { + entity_type, + state, + entity, + } + } + + #[test] + fn insert_and_read() { + with_connection(|conn| { + let inbox_id = "inbox_1"; + let consent_record = generate_consent_record( + ConsentType::InboxId, + ConsentState::Denied, + inbox_id.to_string(), + ); + let consent_record_entity = consent_record.entity.clone(); + + conn.insert_or_replace_consent_records(vec![consent_record]) + .expect("should store without error"); + + let consent_record = conn + .get_consent_record(inbox_id.to_owned(), ConsentType::InboxId) + .expect("query should work"); + + assert_eq!(consent_record.unwrap().entity, consent_record_entity); + }); + } +} diff --git a/xmtp_mls/src/storage/encrypted_store/db_connection.rs b/xmtp_mls/src/storage/encrypted_store/db_connection.rs index 7ed1e4930..5e143c49d 100644 --- a/xmtp_mls/src/storage/encrypted_store/db_connection.rs +++ b/xmtp_mls/src/storage/encrypted_store/db_connection.rs @@ -1,13 +1,17 @@ +use parking_lot::Mutex; use std::fmt; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use crate::storage::RawDbConnection; +use crate::xmtp_openmls_provider::XmtpOpenMlsProvider; /// A wrapper for RawDbConnection that houses all XMTP DB operations. /// Uses a [`Mutex]` internally for interior mutability, so that the connection /// and transaction state can be shared between the OpenMLS Provider and /// native XMTP operations -#[derive(Clone)] +// ~~~~ _NOTE_ ~~~~~ +// Do not derive clone here. +// callers should be able to accomplish everything with one conn/reference. pub struct DbConnection { wrapped_conn: Arc>, } @@ -15,30 +19,38 @@ pub struct DbConnection { /// Owned DBConnection Methods /// Lifetime is 'static' because we are using [`RefOrValue::Value`] variant. impl DbConnection { - pub(crate) fn new(conn: RawDbConnection) -> Self { + pub(super) fn new(conn: RawDbConnection) -> Self { Self { wrapped_conn: Arc::new(Mutex::new(conn)), } } + pub(super) fn from_arc_mutex(conn: Arc>) -> Self { + Self { wrapped_conn: conn } + } + // Note: F is a synchronous fn. If it ever becomes async, we need to use // tokio::sync::mutex instead of std::sync::Mutex pub(crate) fn raw_query(&self, fun: F) -> Result where F: FnOnce(&mut RawDbConnection) -> Result, { - let mut lock = self.wrapped_conn.lock().unwrap_or_else( - |err| { - log::error!( - "Recovering from poisoned mutex - a thread has previously panicked holding this lock" - ); - err.into_inner() - }, - ); + let mut lock = self.wrapped_conn.lock(); fun(&mut lock) } } +// Forces a move for conn +// This is an important distinction from deriving `Clone` on `DbConnection`. +// This way, conn will be moved into XmtpOpenMlsProvider. This forces codepaths to +// use a connection from the provider, rather than pulling a new one from the pool, resulting +// in two connections in the same scope. +impl From for XmtpOpenMlsProvider { + fn from(conn: DbConnection) -> XmtpOpenMlsProvider { + XmtpOpenMlsProvider::new(conn) + } +} + impl fmt::Debug for DbConnection { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DbConnection") diff --git a/xmtp_mls/src/storage/encrypted_store/group.rs b/xmtp_mls/src/storage/encrypted_store/group.rs index 595e02daf..fc41229a4 100644 --- a/xmtp_mls/src/storage/encrypted_store/group.rs +++ b/xmtp_mls/src/storage/encrypted_store/group.rs @@ -158,6 +158,21 @@ impl DbConnection { Ok(groups.into_iter().next()) } + /// Return a single group that matches the given welcome ID + pub fn find_group_by_welcome_id( + &self, + welcome_id: i64, + ) -> Result, StorageError> { + let mut query = dsl::groups.order(dsl::created_at_ns.asc()).into_boxed(); + query = query.filter(dsl::welcome_id.eq(welcome_id)); + let groups: Vec = self.raw_query(|conn| query.load(conn))?; + if groups.len() > 1 { + tracing::error!("More than one group found for welcome_id {}", welcome_id); + } + // Manually extract the first element + Ok(groups.into_iter().next()) + } + /// Updates group membership state pub fn update_group_membership>( &self, @@ -228,6 +243,7 @@ impl DbConnection { } pub fn insert_or_replace_group(&self, group: StoredGroup) -> Result { + tracing::info!("Trying to insert group"); let stored_group = self.raw_query(|conn| { let maybe_inserted_group: Option = diesel::insert_into(dsl::groups) .values(&group) @@ -236,16 +252,20 @@ impl DbConnection { .optional()?; if maybe_inserted_group.is_none() { - let existing_group: StoredGroup = dsl::groups.find(group.id).first(conn).unwrap(); + let existing_group: StoredGroup = dsl::groups.find(group.id).first(conn)?; if existing_group.welcome_id == group.welcome_id { + tracing::info!("Group welcome id already exists"); // Error so OpenMLS db transaction are rolled back on duplicate welcomes return Err(diesel::result::Error::DatabaseError( diesel::result::DatabaseErrorKind::UniqueViolation, Box::new("welcome id already exists".to_string()), )); } else { + tracing::info!("Group already exists"); return Ok(existing_group); } + } else { + tracing::info!("Group is inserted"); } match maybe_inserted_group { diff --git a/xmtp_mls/src/storage/encrypted_store/group_intent.rs b/xmtp_mls/src/storage/encrypted_store/group_intent.rs index ba9384541..4ff7615e7 100644 --- a/xmtp_mls/src/storage/encrypted_store/group_intent.rs +++ b/xmtp_mls/src/storage/encrypted_store/group_intent.rs @@ -7,14 +7,24 @@ use diesel::{ sql_types::Integer, sqlite::Sqlite, }; +use prost::Message; use super::{ db_connection::DbConnection, group, schema::{group_intents, group_intents::dsl}, }; -use crate::{impl_fetch, impl_store, storage::StorageError, Delete}; - +use crate::{ + groups::{intents::SendMessageIntentData, IntentError}, + impl_fetch, impl_store, + storage::StorageError, + utils::id::calculate_message_id, + Delete, +}; +use xmtp_proto::xmtp::mls::message_contents::{ + plaintext_envelope::{Content, V1}, + PlaintextEnvelope, +}; pub type ID = i32; #[repr(i32)] @@ -26,6 +36,7 @@ pub enum IntentKind { MetadataUpdate = 3, UpdateGroupMembership = 4, UpdateAdminList = 5, + UpdatePermission = 6, } impl std::fmt::Display for IntentKind { @@ -36,6 +47,7 @@ impl std::fmt::Display for IntentKind { IntentKind::MetadataUpdate => "MetadataUpdate", IntentKind::UpdateGroupMembership => "UpdateGroupMembership", IntentKind::UpdateAdminList => "UpdateAdminList", + IntentKind::UpdatePermission => "UpdatePermission", }; write!(f, "{}", description) } @@ -63,6 +75,43 @@ pub struct StoredGroupIntent { pub payload_hash: Option>, pub post_commit_data: Option>, pub publish_attempts: i32, + pub staged_commit: Option>, + pub published_in_epoch: Option, +} + +impl StoredGroupIntent { + /// Calculate the message id for this intent. + /// + /// # Note + /// This functions deserializes and decodes a [`PlaintextEnvelope`] from encoded bytes. + /// It would be costly to call this method while pulling extra data from a + /// [`PlaintextEnvelope`] elsewhere. The caller should consider combining implementations. + /// + /// # Returns + /// Returns [`Option::None`] if [`StoredGroupIntent`] is not [`IntentKind::SendMessage`] or if + /// an error occurs during decoding of intent data for [`IntentKind::SendMessage`]. + pub fn message_id(&self) -> Result>, IntentError> { + if self.kind != IntentKind::SendMessage { + return Ok(None); + } + + let data = SendMessageIntentData::from_bytes(&self.data)?; + let envelope: PlaintextEnvelope = PlaintextEnvelope::decode(data.message.as_slice())?; + + // optimistic message should always have a plaintext envelope + let PlaintextEnvelope { + content: + Some(Content::V1(V1 { + content: message, + idempotency_key: key, + })), + } = envelope + else { + return Ok(None); + }; + + Ok(Some(calculate_message_id(&self.group_id, &message, &key))) + } } impl_fetch!(StoredGroupIntent, group_intents, ID); @@ -142,17 +191,25 @@ impl DbConnection { intent_id: ID, payload_hash: Vec, post_commit_data: Option>, + staged_commit: Option>, + published_in_epoch: i64, ) -> Result<(), StorageError> { let res = self.raw_query(|conn| { diesel::update(dsl::group_intents) .filter(dsl::id.eq(intent_id)) // State machine requires that the only valid state transition to Published is from // ToPublish - .filter(dsl::state.eq(IntentState::ToPublish)) + .filter( + dsl::state + .eq(IntentState::ToPublish) + .or(dsl::state.eq(IntentState::Published)), + ) .set(( dsl::state.eq(IntentState::Published), dsl::payload_hash.eq(payload_hash), dsl::post_commit_data.eq(post_commit_data), + dsl::staged_commit.eq(staged_commit), + dsl::published_in_epoch.eq(published_in_epoch), )) .execute(conn) })?; @@ -201,6 +258,8 @@ impl DbConnection { // When moving to ToPublish, clear the payload hash and post commit data dsl::payload_hash.eq(None::>), dsl::post_commit_data.eq(None::>), + dsl::published_in_epoch.eq(None::), + dsl::staged_commit.eq(None::>), )) .execute(conn) })?; @@ -262,6 +321,17 @@ impl DbConnection { Ok(()) } + + pub fn set_group_intent_error_and_fail_msg( + &self, + intent: &StoredGroupIntent, + ) -> Result<(), StorageError> { + self.set_group_intent_error(intent.id)?; + if let Some(id) = intent.message_id()? { + self.set_delivery_status_to_failed(&id)?; + } + Ok(()) + } } impl ToSql for IntentKind @@ -285,6 +355,7 @@ where 3 => Ok(IntentKind::MetadataUpdate), 4 => Ok(IntentKind::UpdateGroupMembership), 5 => Ok(IntentKind::UpdateAdminList), + 6 => Ok(IntentKind::UpdatePermission), x => Err(format!("Unrecognized variant {}", x).into()), } } @@ -500,6 +571,8 @@ mod tests { intent.id, payload_hash.clone(), Some(post_commit_data.clone()), + None, + 1, ) .unwrap(); @@ -509,6 +582,7 @@ mod tests { .unwrap(); assert_eq!(find_result.id, intent.id); + assert_eq!(find_result.published_in_epoch, Some(1)); }) } @@ -537,6 +611,8 @@ mod tests { intent.id, payload_hash.clone(), Some(post_commit_data.clone()), + None, + 1, ) .unwrap(); @@ -579,6 +655,8 @@ mod tests { intent.id, payload_hash.clone(), Some(post_commit_data.clone()), + None, + 1, ) .unwrap(); diff --git a/xmtp_mls/src/storage/encrypted_store/group_message.rs b/xmtp_mls/src/storage/encrypted_store/group_message.rs index c3164a6f3..b6436c75e 100644 --- a/xmtp_mls/src/storage/encrypted_store/group_message.rs +++ b/xmtp_mls/src/storage/encrypted_store/group_message.rs @@ -13,7 +13,7 @@ use super::{ db_connection::DbConnection, schema::{group_messages, group_messages::dsl}, }; -use crate::{impl_fetch, impl_store, StorageError}; +use crate::{impl_fetch, impl_store, impl_store_or_ignore, StorageError}; #[derive( Debug, Clone, Serialize, Deserialize, Insertable, Identifiable, Queryable, Eq, PartialEq, @@ -106,6 +106,7 @@ where impl_fetch!(StoredGroupMessage, group_messages, Vec); impl_store!(StoredGroupMessage, group_messages); +impl_store_or_ignore!(StoredGroupMessage, group_messages); impl DbConnection { /// Query for group messages @@ -188,6 +189,18 @@ impl DbConnection { .execute(conn) })?) } + + pub fn set_delivery_status_to_failed>( + &self, + msg_id: &MessageId, + ) -> Result { + Ok(self.raw_query(|conn| { + diesel::update(dsl::group_messages) + .filter(dsl::id.eq(msg_id.as_ref())) + .set((dsl::delivery_status.eq(DeliveryStatus::Failed),)) + .execute(conn) + })?) + } } #[cfg(test)] diff --git a/xmtp_mls/src/storage/encrypted_store/identity.rs b/xmtp_mls/src/storage/encrypted_store/identity.rs index 68e36b598..88b23c638 100644 --- a/xmtp_mls/src/storage/encrypted_store/identity.rs +++ b/xmtp_mls/src/storage/encrypted_store/identity.rs @@ -1,4 +1,6 @@ -use crate::storage::encrypted_store::schema::identity; +use std::sync::atomic::AtomicBool; + +use crate::storage::{encrypted_store::schema::identity, StorageError}; use diesel::prelude::*; use xmtp_id::InboxId; @@ -33,25 +35,30 @@ impl StoredIdentity { } } -impl From<&Identity> for StoredIdentity { - fn from(identity: &Identity) -> Self { - StoredIdentity { +impl TryFrom<&Identity> for StoredIdentity { + type Error = StorageError; + + fn try_from(identity: &Identity) -> Result { + Ok(StoredIdentity { inbox_id: identity.inbox_id.clone(), - installation_keys: db_serialize(&identity.installation_keys).unwrap(), - credential_bytes: db_serialize(&identity.credential()).unwrap(), + installation_keys: db_serialize(&identity.installation_keys)?, + credential_bytes: db_serialize(&identity.credential())?, rowid: None, - } + }) } } -impl From for Identity { - fn from(identity: StoredIdentity) -> Self { - Identity { +impl TryFrom for Identity { + type Error = StorageError; + + fn try_from(identity: StoredIdentity) -> Result { + Ok(Identity { inbox_id: identity.inbox_id.clone(), - installation_keys: db_deserialize(&identity.installation_keys).unwrap(), - credential: db_deserialize(&identity.credential_bytes).unwrap(), + installation_keys: db_deserialize(&identity.installation_keys)?, + credential: db_deserialize(&identity.credential_bytes)?, signature_request: None, - } + is_ready: AtomicBool::new(true), + }) } } diff --git a/xmtp_mls/src/storage/encrypted_store/identity_update.rs b/xmtp_mls/src/storage/encrypted_store/identity_update.rs index d25b10cb7..e547c34dd 100644 --- a/xmtp_mls/src/storage/encrypted_store/identity_update.rs +++ b/xmtp_mls/src/storage/encrypted_store/identity_update.rs @@ -7,7 +7,7 @@ use super::{ schema::identity_updates::{self, dsl}, }; use diesel::{dsl::max, prelude::*}; -use xmtp_id::associations::{AssociationError, IdentityUpdate}; +use xmtp_id::associations::{unverified::UnverifiedIdentityUpdate, AssociationError}; /// StoredIdentityUpdate holds a serialized IdentityUpdate record #[derive(Insertable, Identifiable, Queryable, Debug, Clone, PartialEq, Eq)] @@ -36,11 +36,11 @@ impl StoredIdentityUpdate { } } -impl TryFrom for IdentityUpdate { +impl TryFrom for UnverifiedIdentityUpdate { type Error = AssociationError; fn try_from(update: StoredIdentityUpdate) -> Result { - Ok(IdentityUpdate::try_from(update.payload)?) + Ok(UnverifiedIdentityUpdate::try_from(update.payload)?) } } @@ -97,7 +97,7 @@ impl DbConnection { Ok(self.raw_query(|conn| query.first::(conn))?) } - /// Given a list of inbox_ids return a hashamp of each inbox ID -> highest known sequence ID + /// Given a list of inbox_ids return a HashMap of each inbox ID -> highest known sequence ID #[tracing::instrument(level = "trace", skip_all)] pub fn get_latest_sequence_id( &self, diff --git a/xmtp_mls/src/storage/encrypted_store/key_package_history.rs b/xmtp_mls/src/storage/encrypted_store/key_package_history.rs new file mode 100644 index 000000000..1f759a8bc --- /dev/null +++ b/xmtp_mls/src/storage/encrypted_store/key_package_history.rs @@ -0,0 +1,122 @@ +use diesel::prelude::*; + +use super::{db_connection::DbConnection, schema::key_package_history, StorageError}; +use crate::{impl_store_or_ignore, utils::time::now_ns, StoreOrIgnore}; + +#[derive(Insertable, Debug, Clone)] +#[diesel(table_name = key_package_history)] +pub struct NewKeyPackageHistoryEntry { + pub key_package_hash_ref: Vec, + pub created_at_ns: i64, +} + +#[derive(Queryable, Selectable, Debug, Clone)] +#[diesel(table_name = key_package_history)] +pub struct StoredKeyPackageHistoryEntry { + pub id: i32, + pub key_package_hash_ref: Vec, + pub created_at_ns: i64, +} + +impl_store_or_ignore!(NewKeyPackageHistoryEntry, key_package_history); + +impl DbConnection { + pub fn store_key_package_history_entry( + &self, + key_package_hash_ref: Vec, + ) -> Result { + let entry = NewKeyPackageHistoryEntry { + key_package_hash_ref: key_package_hash_ref.clone(), + created_at_ns: now_ns(), + }; + entry.store_or_ignore(self)?; + + self.find_key_package_history_entry_by_hash_ref(key_package_hash_ref) + } + + pub fn find_key_package_history_entry_by_hash_ref( + &self, + hash_ref: Vec, + ) -> Result { + let result = self.raw_query(|conn| { + key_package_history::dsl::key_package_history + .filter(key_package_history::dsl::key_package_hash_ref.eq(hash_ref)) + .first::(conn) + })?; + + Ok(result) + } + + pub fn find_key_package_history_entries_before_id( + &self, + id: i32, + ) -> Result, StorageError> { + let result = self.raw_query(|conn| { + key_package_history::dsl::key_package_history + .filter(key_package_history::dsl::id.lt(id)) + .load::(conn) + })?; + + Ok(result) + } + + pub fn delete_key_package_history_entries_before_id( + &self, + id: i32, + ) -> Result<(), StorageError> { + self.raw_query(|conn| { + diesel::delete( + key_package_history::dsl::key_package_history + .filter(key_package_history::dsl::id.lt(id)), + ) + .execute(conn) + })?; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::{storage::encrypted_store::tests::with_connection, utils::test::rand_vec}; + + #[test] + fn test_store_key_package_history_entry() { + with_connection(|conn| { + let hash_ref = rand_vec(); + let new_entry = conn + .store_key_package_history_entry(hash_ref.clone()) + .unwrap(); + assert_eq!(new_entry.key_package_hash_ref, hash_ref); + assert_eq!(new_entry.id, 1); + }) + } + + #[test] + fn test_store_multiple() { + with_connection(|conn| { + let hash_ref1 = rand_vec(); + let hash_ref2 = rand_vec(); + let hash_ref3 = rand_vec(); + + conn.store_key_package_history_entry(hash_ref1.clone()) + .unwrap(); + conn.store_key_package_history_entry(hash_ref2.clone()) + .unwrap(); + let entry_3 = conn + .store_key_package_history_entry(hash_ref3.clone()) + .unwrap(); + + let all_entries = conn + .find_key_package_history_entries_before_id(100) + .unwrap(); + + assert_eq!(all_entries.len(), 3); + + let earlier_entries = conn + .find_key_package_history_entries_before_id(entry_3.id) + .unwrap(); + assert_eq!(earlier_entries.len(), 2); + }) + } +} diff --git a/xmtp_mls/src/storage/encrypted_store/mod.rs b/xmtp_mls/src/storage/encrypted_store/mod.rs index 44f4938c9..8cd5baf9e 100644 --- a/xmtp_mls/src/storage/encrypted_store/mod.rs +++ b/xmtp_mls/src/storage/encrypted_store/mod.rs @@ -11,48 +11,46 @@ //! `diesel print-schema` or use `cargo run update-schema` which will update the files for you. pub mod association_state; +pub mod consent_record; pub mod db_connection; pub mod group; pub mod group_intent; pub mod group_message; pub mod identity; pub mod identity_update; +pub mod key_package_history; pub mod key_store_entry; pub mod refresh_state; pub mod schema; +mod sqlcipher_connection; -use std::{ - borrow::Cow, - sync::{Arc, RwLock}, -}; +use std::sync::Arc; use diesel::{ connection::{AnsiTransactionManager, SimpleConnection, TransactionManager}, prelude::*, r2d2::{ConnectionManager, Pool, PoolTransactionManager, PooledConnection}, result::{DatabaseErrorKind, Error}, + sql_query, }; use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; -use log::warn; -use rand::RngCore; -use xmtp_cryptography::utils as crypto_utils; +use parking_lot::RwLock; use self::db_connection::DbConnection; +pub use self::sqlcipher_connection::{EncryptedConnection, EncryptionKey}; + use super::StorageError; use crate::{xmtp_openmls_provider::XmtpOpenMlsProvider, Store}; pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("./migrations/"); - pub type RawDbConnection = PooledConnection>; -pub type EncryptionKey = [u8; 32]; - -#[derive(Default, Clone, Debug)] -pub enum StorageOption { - #[default] - Ephemeral, - Persistent(String), +// For PRAGMA query log statements +#[derive(QueryableByName, Debug)] +struct SqliteVersion { + #[diesel(sql_type = diesel::sql_types::Text)] + version: String, } pub fn ignore_unique_violation( @@ -65,19 +63,49 @@ pub fn ignore_unique_violation( } } +#[derive(Default, Clone, Debug)] +pub enum StorageOption { + #[default] + Ephemeral, + Persistent(String), +} + +impl StorageOption { + // create a completely new standalone connection + fn conn(&self) -> Result { + use StorageOption::*; + match self { + Persistent(path) => SqliteConnection::establish(path), + Ephemeral => SqliteConnection::establish(":memory:"), + } + } +} + +/// An Unencrypted Connection +/// Creates a Sqlite3 Database/Connection in WAL mode. +/// Sets `busy_timeout` on each connection. +/// _*NOTE:*_Unencrypted Connections are not validated and mostly meant for testing. +/// It is not recommended to use an unencrypted connection in production. +#[derive(Clone, Debug)] +pub struct UnencryptedConnection; + +impl diesel::r2d2::CustomizeConnection + for UnencryptedConnection +{ + fn on_acquire(&self, conn: &mut SqliteConnection) -> Result<(), diesel::r2d2::Error> { + conn.batch_execute("PRAGMA busy_timeout = 5000;") + .map_err(diesel::r2d2::Error::QueryError)?; + Ok(()) + } +} + #[allow(dead_code)] #[derive(Clone, Debug)] /// Manages a Sqlite db for persisting messages and other objects. pub struct EncryptedMessageStore { connect_opt: StorageOption, pool: Arc>>>>, - enc_key: Option, -} - -impl<'a> From<&'a EncryptedMessageStore> for Cow<'a, EncryptedMessageStore> { - fn from(store: &'a EncryptedMessageStore) -> Cow<'a, EncryptedMessageStore> { - Cow::Borrowed(store) - } + enc_opts: Option, } impl EncryptedMessageStore { @@ -94,59 +122,74 @@ impl EncryptedMessageStore { opts: StorageOption, enc_key: Option, ) -> Result { - log::info!("Setting up DB connection pool"); - let pool = - match opts { - StorageOption::Ephemeral => Pool::builder() - .max_size(1) - .build(ConnectionManager::::new(":memory:"))?, - StorageOption::Persistent(ref path) => Pool::builder() - .max_size(10) - .build(ConnectionManager::::new(path))?, - }; - - // TODO: Validate that sqlite is correctly configured. Bad EncKey is not detected until the - // migrations run which returns an unhelpful error. - - let mut obj = Self { + tracing::info!("Setting up DB connection pool"); + let mut builder = Pool::builder(); + + let enc_opts = if let Some(key) = enc_key { + let enc_opts = EncryptedConnection::new(key, &opts)?; + builder = builder.connection_customizer(Box::new(enc_opts.clone())); + Some(enc_opts) + } else if matches!(opts, StorageOption::Persistent(_)) { + builder = builder.connection_customizer(Box::new(UnencryptedConnection)); + None + } else { + None + }; + + let pool = match opts { + StorageOption::Ephemeral => builder + .max_size(1) + .build(ConnectionManager::::new(":memory:"))?, + StorageOption::Persistent(ref path) => builder + .max_size(25) + .build(ConnectionManager::::new(path))?, + }; + + let mut this = Self { connect_opt: opts, pool: Arc::new(Some(pool).into()), - enc_key, + enc_opts, }; - obj.init_db()?; - Ok(obj) + this.init_db()?; + Ok(this) } fn init_db(&mut self) -> Result<(), StorageError> { - let conn = &mut self.raw_conn()?; - conn.batch_execute("PRAGMA journal_mode = WAL;") - .map_err(|e| StorageError::DbInit(e.to_string()))?; + if let Some(ref encrypted_conn) = self.enc_opts { + encrypted_conn.validate(&self.connect_opt)?; + } - log::info!("Running DB migrations"); + let conn = &mut self.raw_conn()?; + conn.batch_execute("PRAGMA journal_mode = WAL;")?; + tracing::info!("Running DB migrations"); conn.run_pending_migrations(MIGRATIONS) - .map_err(|e| StorageError::DbInit(e.to_string()))?; + .map_err(|e| StorageError::DbInit(format!("Failed to run migrations: {}", e)))?; + + let sqlite_version = + sql_query("SELECT sqlite_version() AS version").load::(conn)?; + tracing::info!("sqlite_version={}", sqlite_version[0].version); - log::info!("Migrations successful"); + tracing::info!("Migrations successful"); Ok(()) } - fn raw_conn( + pub(crate) fn raw_conn( &self, ) -> Result>, StorageError> { - let pool_guard = self.pool.read()?; + let pool_guard = self.pool.read(); let pool = pool_guard .as_ref() .ok_or(StorageError::PoolNeedsConnection)?; - let mut conn = pool.get()?; - - if let Some(ref key) = self.enc_key { - conn.batch_execute(&format!("PRAGMA key = \"x'{}'\";", hex::encode(key)))?; - } + tracing::debug!( + "Pulling connection from pool, idle_connections={}, total_connections={}", + pool.state().idle_connections, + pool.state().connections + ); - Ok(conn) + Ok(pool.get()?) } pub fn conn(&self) -> Result { @@ -172,7 +215,7 @@ impl EncryptedMessageStore { F: FnOnce(&XmtpOpenMlsProvider) -> Result, E: From + From, { - log::debug!("Transaction beginning"); + tracing::debug!("Transaction beginning"); let mut connection = self.raw_conn()?; AnsiTransactionManager::begin_transaction(&mut *connection)?; @@ -185,11 +228,11 @@ impl EncryptedMessageStore { conn.raw_query(|conn| { PoolTransactionManager::::commit_transaction(&mut *conn) })?; - log::debug!("Transaction being committed"); + tracing::debug!("Transaction being committed"); Ok(value) } Err(err) => { - log::debug!("Transaction being rolled back"); + tracing::debug!("Transaction being rolled back"); match conn.raw_query(|conn| { PoolTransactionManager::::rollback_transaction( &mut *conn, @@ -223,30 +266,41 @@ impl EncryptedMessageStore { Fut: futures::Future>, E: From + From, { - log::debug!("Transaction async beginning"); + tracing::debug!("Transaction async beginning"); let mut connection = self.raw_conn()?; AnsiTransactionManager::begin_transaction(&mut *connection)?; - - let db_connection = DbConnection::new(connection); + let connection = Arc::new(parking_lot::Mutex::new(connection)); + let local_connection = Arc::clone(&connection); + let db_connection = DbConnection::from_arc_mutex(connection); let provider = XmtpOpenMlsProvider::new(db_connection); - let local_provider = provider.clone(); + // the other connection is dropped in the closure + // ensuring we have only one strong reference let result = fun(provider).await; + if Arc::strong_count(&local_connection) > 1 { + tracing::warn!( + "More than 1 strong connection references still exist during transaction" + ); + } + + if Arc::weak_count(&local_connection) > 1 { + tracing::warn!("More than 1 weak connection references still exist during transaction"); + } // after the closure finishes, `local_provider` should have the only reference ('strong') // to `XmtpOpenMlsProvider` inner `DbConnection`.. - let conn_ref = local_provider.conn_ref(); + let local_connection = DbConnection::from_arc_mutex(local_connection); match result { Ok(value) => { - conn_ref.raw_query(|conn| { + local_connection.raw_query(|conn| { PoolTransactionManager::::commit_transaction(&mut *conn) })?; - log::debug!("Transaction async being committed"); + tracing::debug!("Transaction async being committed"); Ok(value) } Err(err) => { - log::debug!("Transaction async being rolled back"); - match conn_ref.raw_query(|conn| { + tracing::debug!("Transaction async being rolled back"); + match local_connection.raw_query(|conn| { PoolTransactionManager::::rollback_transaction( &mut *conn, ) @@ -259,31 +313,29 @@ impl EncryptedMessageStore { } } - pub fn generate_enc_key() -> EncryptionKey { - // TODO: Handle Key Better/ Zeroize - let mut key = [0u8; 32]; - crypto_utils::rng().fill_bytes(&mut key[..]); - key - } - pub fn release_connection(&self) -> Result<(), StorageError> { - let mut pool_guard = self.pool.write()?; + let mut pool_guard = self.pool.write(); pool_guard.take(); Ok(()) } pub fn reconnect(&self) -> Result<(), StorageError> { - let pool = - match self.connect_opt { - StorageOption::Ephemeral => Pool::builder() - .max_size(1) - .build(ConnectionManager::::new(":memory:"))?, - StorageOption::Persistent(ref path) => Pool::builder() - .max_size(10) - .build(ConnectionManager::::new(path))?, - }; - - let mut pool_write = self.pool.write()?; + let mut builder = Pool::builder(); + + if let Some(ref opts) = self.enc_opts { + builder = builder.connection_customizer(Box::new(opts.clone())); + } + + let pool = match self.connect_opt { + StorageOption::Ephemeral => builder + .max_size(1) + .build(ConnectionManager::::new(":memory:"))?, + StorageOption::Persistent(ref path) => builder + .max_size(25) + .build(ConnectionManager::::new(path))?, + }; + + let mut pool_write = self.pool.write(); *pool_write = Some(pool); Ok(()) @@ -293,7 +345,7 @@ impl EncryptedMessageStore { #[allow(dead_code)] fn warn_length(list: &[T], str_id: &str, max_length: usize) { if list.len() > max_length { - warn!( + tracing::warn!( "EncryptedStore expected at most {} {} however found {}. Using the Oldest.", max_length, str_id, @@ -387,18 +439,16 @@ where #[cfg(test)] mod tests { - use super::{ - db_connection::DbConnection, identity::StoredIdentity, EncryptedMessageStore, StorageError, - StorageOption, - }; + use super::*; use std::sync::Barrier; use crate::{ storage::group::{GroupMembershipState, StoredGroup}, + storage::identity::StoredIdentity, utils::test::{rand_vec, tmp_path}, Fetch, Store, }; - use std::{fs, sync::Arc}; + use std::sync::Arc; /// Test harness that loads an Ephemeral store. pub fn with_connection(fun: F) -> R @@ -462,8 +512,7 @@ mod tests { let fetched_identity: StoredIdentity = conn.fetch(&()).unwrap().unwrap(); assert_eq!(fetched_identity.inbox_id, inbox_id); } - - fs::remove_file(db_path).unwrap(); + EncryptedMessageStore::remove_db_files(db_path) } #[test] @@ -487,14 +536,14 @@ mod tests { assert_eq!(fetched_identity.inbox_id, inbox_id); store.release_connection().unwrap(); - assert!(store.pool.read().unwrap().is_none()); + assert!(store.pool.read().is_none()); store.reconnect().unwrap(); let fetched_identity2: StoredIdentity = conn.fetch(&()).unwrap().unwrap(); assert_eq!(fetched_identity2.inbox_id, inbox_id); } - fs::remove_file(db_path).unwrap(); + EncryptedMessageStore::remove_db_files(db_path) } #[test] @@ -518,30 +567,34 @@ mod tests { // Ensure it fails assert!( - matches!(res.err(), Some(StorageError::DbInit(_))), - "Expected DbInitError" + matches!(res.err(), Some(StorageError::SqlCipherKeyIncorrect)), + "Expected SqlCipherKeyIncorrect error" ); - fs::remove_file(db_path).unwrap(); + EncryptedMessageStore::remove_db_files(db_path) } #[tokio::test] async fn encrypted_db_with_multiple_connections() { let db_path = tmp_path(); - let store = EncryptedMessageStore::new( - StorageOption::Persistent(db_path.clone()), - EncryptedMessageStore::generate_enc_key(), - ) - .unwrap(); - - let conn1 = &store.conn().unwrap(); - let inbox_id = "inbox_id"; - StoredIdentity::new(inbox_id.to_string(), rand_vec(), rand_vec()) - .store(conn1) + { + let store = EncryptedMessageStore::new( + StorageOption::Persistent(db_path.clone()), + EncryptedMessageStore::generate_enc_key(), + ) .unwrap(); - let conn2 = &store.conn().unwrap(); - let fetched_identity: StoredIdentity = conn2.fetch(&()).unwrap().unwrap(); - assert_eq!(fetched_identity.inbox_id, inbox_id); + let conn1 = &store.conn().unwrap(); + let inbox_id = "inbox_id"; + StoredIdentity::new(inbox_id.to_string(), rand_vec(), rand_vec()) + .store(conn1) + .unwrap(); + + let conn2 = &store.conn().unwrap(); + tracing::info!("Getting conn 2"); + let fetched_identity: StoredIdentity = conn2.fetch(&()).unwrap().unwrap(); + assert_eq!(fetched_identity.inbox_id, inbox_id); + } + EncryptedMessageStore::remove_db_files(db_path) } #[test] @@ -606,9 +659,9 @@ mod tests { let barrier_pointer = barrier.clone(); let handle = std::thread::spawn(move || { store_pointer.transaction(|provider| { - let conn1 = provider.conn(); + let conn1 = provider.conn_ref(); StoredIdentity::new("correct".to_string(), rand_vec(), rand_vec()) - .store(&conn1) + .store(conn1) .unwrap(); // wait for second transaction to start barrier_pointer.wait(); @@ -622,14 +675,14 @@ mod tests { let handle2 = std::thread::spawn(move || { barrier.wait(); let result = store_pointer.transaction(|provider| -> Result<(), anyhow::Error> { - let connection = provider.conn(); + let connection = provider.conn_ref(); let group = StoredGroup::new( b"should not exist".to_vec(), 0, GroupMembershipState::Allowed, "goodbye".to_string(), ); - group.store(&connection)?; + group.store(connection)?; Ok(()) }); barrier.wait(); @@ -669,9 +722,9 @@ mod tests { let handle = tokio::spawn(async move { store_pointer .transaction_async(|provider| async move { - let conn1 = provider.conn(); + let conn1 = provider.conn_ref(); StoredIdentity::new("crab".to_string(), rand_vec(), rand_vec()) - .store(&conn1) + .store(conn1) .unwrap(); let group = StoredGroup::new( @@ -680,7 +733,7 @@ mod tests { GroupMembershipState::Allowed, "goodbye".to_string(), ); - group.store(&conn1).unwrap(); + group.store(conn1).unwrap(); anyhow::bail!("force a rollback") }) diff --git a/xmtp_mls/src/storage/encrypted_store/refresh_state.rs b/xmtp_mls/src/storage/encrypted_store/refresh_state.rs index 1c9e03478..a72d92e32 100644 --- a/xmtp_mls/src/storage/encrypted_store/refresh_state.rs +++ b/xmtp_mls/src/storage/encrypted_store/refresh_state.rs @@ -9,7 +9,7 @@ use diesel::{ }; use super::{db_connection::DbConnection, schema::refresh_state}; -use crate::{impl_store, storage::StorageError, Store}; +use crate::{impl_store, impl_store_or_ignore, storage::StorageError, StoreOrIgnore}; #[repr(i32)] #[derive(Debug, Clone, Copy, PartialEq, Eq, AsExpression, Hash, FromSqlRow)] @@ -52,6 +52,7 @@ pub struct RefreshState { } impl_store!(RefreshState, refresh_state); +impl_store_or_ignore!(RefreshState, refresh_state); impl DbConnection { pub fn get_refresh_state>>( @@ -69,6 +70,7 @@ impl DbConnection { Ok(res) } + pub fn get_last_cursor_for_id>>( &self, id: IdType, @@ -83,7 +85,7 @@ impl DbConnection { entity_kind, cursor: 0, }; - new_state.store(self)?; + new_state.store_or_ignore(self)?; Ok(0) } } @@ -119,7 +121,7 @@ impl DbConnection { #[cfg(test)] pub(crate) mod tests { use super::*; - use crate::storage::encrypted_store::tests::with_connection; + use crate::{storage::encrypted_store::tests::with_connection, Store}; #[test] fn get_cursor_with_no_existing_state() { diff --git a/xmtp_mls/src/storage/encrypted_store/schema.rs b/xmtp_mls/src/storage/encrypted_store/schema.rs index 3f76d5661..24f9bed88 100644 --- a/xmtp_mls/src/storage/encrypted_store/schema.rs +++ b/xmtp_mls/src/storage/encrypted_store/schema.rs @@ -8,6 +8,14 @@ diesel::table! { } } +diesel::table! { + consent_records (entity_type, entity) { + entity_type -> Integer, + state -> Integer, + entity -> Text, + } +} + diesel::table! { group_intents (id) { id -> Integer, @@ -18,6 +26,8 @@ diesel::table! { payload_hash -> Nullable, post_commit_data -> Nullable, publish_attempts -> Integer, + staged_commit -> Nullable, + published_in_epoch -> Nullable, } } @@ -65,6 +75,14 @@ diesel::table! { } } +diesel::table! { + key_package_history (id) { + id -> Integer, + key_package_hash_ref -> Binary, + created_at_ns -> BigInt, + } +} + diesel::table! { openmls_key_store (key_bytes) { key_bytes -> Binary, @@ -93,11 +111,13 @@ diesel::joinable!(group_messages -> groups (group_id)); diesel::allow_tables_to_appear_in_same_query!( association_state, + consent_records, group_intents, group_messages, groups, identity, identity_updates, + key_package_history, openmls_key_store, openmls_key_value, refresh_state, diff --git a/xmtp_mls/src/storage/encrypted_store/sqlcipher_connection.rs b/xmtp_mls/src/storage/encrypted_store/sqlcipher_connection.rs new file mode 100644 index 000000000..4fa0e3f50 --- /dev/null +++ b/xmtp_mls/src/storage/encrypted_store/sqlcipher_connection.rs @@ -0,0 +1,351 @@ +//! SQLCipher-specific Connection +use diesel::{ + connection::{LoadConnection, SimpleConnection}, + deserialize::FromSqlRow, + prelude::*, + sql_query, +}; +use std::{ + fmt::Display, + fs::File, + io::{Read, Write}, + path::{Path, PathBuf}, +}; + +use crate::storage::StorageError; + +use super::StorageOption; + +pub type EncryptionKey = [u8; 32]; +pub type Salt = [u8; 16]; +const PLAINTEXT_HEADER_SIZE: usize = 32; +const SALT_FILE_NAME: &str = "sqlcipher_salt"; + +// For PRAGMA query log statements +#[derive(QueryableByName, Debug)] +struct CipherVersion { + #[diesel(sql_type = diesel::sql_types::Text)] + cipher_version: String, +} + +// For PRAGMA query log statements +#[derive(QueryableByName, Debug)] +struct CipherProviderVersion { + #[diesel(sql_type = diesel::sql_types::Text)] + cipher_provider_version: String, +} + +/// Specialized Connection for r2d2 connection pool. +#[derive(Clone, Debug)] +pub struct EncryptedConnection { + key: EncryptionKey, + /// We don't store the salt for Ephemeral Dbs + salt: Option, +} + +impl EncryptedConnection { + /// Creates a file for the salt and stores it + pub fn new(key: EncryptionKey, opts: &StorageOption) -> Result { + use super::StorageOption::*; + let salt = match opts { + Ephemeral => None, + Persistent(ref db_path) => { + let mut salt = [0u8; 16]; + let db_pathbuf = PathBuf::from(db_path); + let salt_path = Self::salt_file(db_path)?; + + match (salt_path.try_exists()?, db_pathbuf.try_exists()?) { + // db and salt exist + (true, true) => { + let file = File::open(salt_path)?; + salt = ::from_hex( + file.bytes().take(32).collect::, _>>()?, + )?; + } + // the db exists and needs to be migrated + (false, true) => { + tracing::debug!("migrating sqlcipher db to plaintext header."); + Self::migrate(db_path, key, &mut salt)?; + } + // the db doesn't exist yet and needs to be created + (false, false) => { + tracing::debug!("creating new sqlcipher db"); + Self::create(db_path, key, &mut salt)?; + } + // the db doesn't exist but the salt does + // This generally doesn't make sense & shouldn't happen. + // Create a new database and delete the salt file. + (true, false) => { + std::fs::remove_file(salt_path)?; + Self::create(db_path, key, &mut salt)?; + } + } + Some(salt) + } + }; + + Ok(Self { key, salt }) + } + + /// create a new database + salt file. + /// writes the 16-bytes hex-encoded salt to `salt` + fn create(path: &String, key: EncryptionKey, salt: &mut [u8]) -> Result<(), StorageError> { + let conn = &mut SqliteConnection::establish(path)?; + conn.batch_execute(&format!( + r#" + {} + {} + PRAGMA journal_mode = WAL; + "#, + pragma_key(hex::encode(key)), + pragma_plaintext_header() + ))?; + + Self::write_salt(path, conn, salt)?; + Ok(()) + } + + /// Executes the steps outlined in the [SQLCipher Docs](https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_plaintext_header_size) + /// Migrates the database to `cipher_plaintext_header_size` and returns the salt after + /// persisting it to SALT_FILE_NAME. + /// + /// if the salt file already exists, deletes it. + fn migrate(path: &String, key: EncryptionKey, salt: &mut [u8]) -> Result<(), StorageError> { + let conn = &mut SqliteConnection::establish(path)?; + + conn.batch_execute(&format!( + r#" + {} + select count(*) from sqlite_master; -- trigger header read, currently it is encrypted + "#, + pragma_key(hex::encode(key)) + ))?; + + // get the salt and save it for later use + Self::write_salt(path, conn, salt)?; + + conn.batch_execute(&format!( + r#" + {} + PRAGMA user_version = 1; -- force header write + "#, + pragma_plaintext_header() + ))?; + + Ok(()) + } + + /// Get the salt from the opened database, write it to `Self::salt_file(db_path)` as hex-encoded + /// bytes, and then copy it to `buf` after decoding hex bytes. + fn write_salt( + path: &String, + conn: &mut SqliteConnection, + buf: &mut [u8], + ) -> Result<(), StorageError> { + let mut row_iter = conn.load(sql_query("PRAGMA cipher_salt"))?; + // cipher salt should always exist. if it doesn't SQLCipher is misconfigured. + let row = row_iter.next().ok_or(StorageError::NotFound( + "Cipher salt doesn't exist in database".into(), + ))??; + let salt = >::build_from_row(&row)?; + tracing::debug!( + "writing salt={} to file {:?}", + salt, + Self::salt_file(PathBuf::from(path))?, + ); + let mut f = File::create(Self::salt_file(PathBuf::from(path))?)?; + + f.write_all(salt.as_bytes())?; + let mut perms = f.metadata()?.permissions(); + perms.set_readonly(true); + f.set_permissions(perms)?; + + let salt = hex::decode(salt)?; + buf.copy_from_slice(&salt); + Ok(()) + } + + /// Salt file is stored next to the sqlite3 db3 file as `{db_file_name}.SALT_FILE_NAME`. + /// If the db file is named `sqlite3_xmtp_db.db3`, the salt file would + /// be stored next to this file as `sqlite3_xmtp_db.db3.sqlcipher_salt` + pub(crate) fn salt_file>(db_path: P) -> std::io::Result { + let db_path: &Path = db_path.as_ref(); + let name = db_path.file_name().ok_or(std::io::Error::new( + std::io::ErrorKind::NotFound, + "database file has no name", + ))?; + let db_path = db_path.parent().ok_or(std::io::Error::new( + std::io::ErrorKind::NotFound, + "Parent directory could not be found", + ))?; + Ok(db_path.join(format!("{}.{}", name.to_string_lossy(), SALT_FILE_NAME))) + } + + pub(super) fn validate(&self, opts: &StorageOption) -> Result<(), StorageError> { + let conn = &mut opts.conn()?; + + let cipher_version = sql_query("PRAGMA cipher_version").load::(conn)?; + if cipher_version.is_empty() { + return Err(StorageError::SqlCipherNotLoaded); + } + + // test the key according to + // https://www.zetetic.net/sqlcipher/sqlcipher-api/#testing-the-key + conn.batch_execute(&format!( + "{} + SELECT count(*) FROM sqlite_master;", + self.pragmas() + )) + .map_err(|_| StorageError::SqlCipherKeyIncorrect)?; + + let CipherProviderVersion { + cipher_provider_version, + } = sql_query("PRAGMA cipher_provider_version") + .get_result::(conn)?; + tracing::info!( + "Sqlite cipher_version={:?}, cipher_provider_version={:?}", + cipher_version.first().as_ref().map(|v| &v.cipher_version), + cipher_provider_version + ); + + if tracing::enabled!(tracing::Level::INFO) { + conn.batch_execute("PRAGMA cipher_log = stderr; PRAGMA cipher_log_level = INFO;") + .ok(); + } else { + conn.batch_execute("PRAGMA cipher_log = stderr; PRAGMA cipher_log_level = WARN;") + .ok(); + } + tracing::debug!("SQLCipher Database validated."); + Ok(()) + } + + /// Output the corect order of PRAGMAS to instantiate a connection + fn pragmas(&self) -> impl Display { + let Self { ref key, ref salt } = self; + + if let Some(s) = salt { + format!( + "{}\n{}\n{}", + pragma_key(hex::encode(key)), + pragma_plaintext_header(), + pragma_salt(hex::encode(s)) + ) + } else { + format!( + "{}\n{}", + pragma_key(hex::encode(key)), + pragma_plaintext_header() + ) + } + } +} + +impl diesel::r2d2::CustomizeConnection + for EncryptedConnection +{ + fn on_acquire(&self, conn: &mut SqliteConnection) -> Result<(), diesel::r2d2::Error> { + conn.batch_execute(&format!( + "{} + PRAGMA busy_timeout = 5000;", + self.pragmas() + )) + .map_err(diesel::r2d2::Error::QueryError)?; + + Ok(()) + } +} + +fn pragma_key(key: impl Display) -> impl Display { + format!(r#"PRAGMA key = "x'{key}'";"#) +} + +fn pragma_salt(salt: impl Display) -> impl Display { + format!(r#"PRAGMA cipher_salt="x'{salt}'";"#) +} + +fn pragma_plaintext_header() -> impl Display { + format!(r#"PRAGMA cipher_plaintext_header_size={PLAINTEXT_HEADER_SIZE};"#) +} + +#[cfg(test)] +mod tests { + use crate::{storage::EncryptedMessageStore, utils::test::tmp_path}; + use diesel_migrations::MigrationHarness; + use std::fs::File; + + use super::*; + const SQLITE3_PLAINTEXT_HEADER: &str = "SQLite format 3\0"; + use StorageOption::*; + + #[test] + fn test_db_creates_with_plaintext_header() { + let db_path = tmp_path(); + { + let _ = EncryptedMessageStore::new( + Persistent(db_path.clone()), + EncryptedMessageStore::generate_enc_key(), + ) + .unwrap(); + + assert!(EncryptedConnection::salt_file(&db_path).unwrap().exists()); + let bytes = std::fs::read(EncryptedConnection::salt_file(&db_path).unwrap()).unwrap(); + let salt = hex::decode(bytes).unwrap(); + assert_eq!(salt.len(), 16); + + let mut plaintext_header = [0; 16]; + let mut file = File::open(&db_path).unwrap(); + file.read_exact(&mut plaintext_header).unwrap(); + + assert_eq!( + SQLITE3_PLAINTEXT_HEADER, + String::from_utf8(plaintext_header.into()).unwrap() + ); + } + EncryptedMessageStore::remove_db_files(db_path) + } + + #[test] + fn test_db_migrates() { + let db_path = tmp_path(); + { + let key = EncryptedMessageStore::generate_enc_key(); + { + let conn = &mut SqliteConnection::establish(&db_path).unwrap(); + conn.batch_execute(&format!( + r#" + {} + PRAGMA busy_timeout = 5000; + PRAGMA journal_mode = WAL; + "#, + pragma_key(hex::encode(key)) + )) + .unwrap(); + conn.run_pending_migrations(crate::storage::MIGRATIONS) + .unwrap(); + } + + // no plaintext header before migration + let mut plaintext_header = [0; 16]; + let mut file = File::open(&db_path).unwrap(); + file.read_exact(&mut plaintext_header).unwrap(); + assert!(String::from_utf8_lossy(&plaintext_header) != SQLITE3_PLAINTEXT_HEADER); + + let _ = EncryptedMessageStore::new(Persistent(db_path.clone()), key).unwrap(); + + assert!(EncryptedConnection::salt_file(&db_path).unwrap().exists()); + let bytes = std::fs::read(EncryptedConnection::salt_file(&db_path).unwrap()).unwrap(); + let salt = hex::decode(bytes).unwrap(); + assert_eq!(salt.len(), 16); + + let mut plaintext_header = [0; 16]; + let mut file = File::open(&db_path).unwrap(); + file.read_exact(&mut plaintext_header).unwrap(); + + assert_eq!( + SQLITE3_PLAINTEXT_HEADER, + String::from_utf8(plaintext_header.into()).unwrap() + ); + } + EncryptedMessageStore::remove_db_files(db_path) + } +} diff --git a/xmtp_mls/src/storage/errors.rs b/xmtp_mls/src/storage/errors.rs index 1bd1ecac6..e56f6f772 100644 --- a/xmtp_mls/src/storage/errors.rs +++ b/xmtp_mls/src/storage/errors.rs @@ -1,8 +1,11 @@ use std::sync::PoisonError; +use diesel::result::DatabaseErrorKind; use thiserror::Error; -use crate::{retry::RetryableError, retryable}; +use crate::{groups::intents::IntentError, retry::RetryableError, retryable}; + +use super::sql_key_store; #[derive(Debug, Error)] pub enum StorageError { @@ -30,6 +33,16 @@ pub enum StorageError { PoolNeedsConnection, #[error("Conflict")] Conflict(String), + #[error(transparent)] + Intent(#[from] IntentError), + #[error("The SQLCipher Sqlite extension is not present, but an encryption key is given")] + SqlCipherNotLoaded, + #[error("PRAGMA key or salt has incorrect value")] + SqlCipherKeyIncorrect, + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + FromHex(#[from] hex::FromHexError), } impl From> for StorageError { @@ -38,33 +51,62 @@ impl From> for StorageError { } } +impl RetryableError for diesel::result::Error { + fn is_retryable(&self) -> bool { + match self { + Self::DatabaseError(DatabaseErrorKind::UniqueViolation, _) => false, + Self::DatabaseError(DatabaseErrorKind::CheckViolation, _) => false, + Self::DatabaseError(DatabaseErrorKind::NotNullViolation, _) => false, + // TODO: Figure out the full list of non-retryable errors. + // The diesel code has a comment that "this type is not meant to be exhaustively matched" + // so best is probably to return true here and map known errors to something else + // that is not retryable. + _ => true, + } + } +} + impl RetryableError for StorageError { fn is_retryable(&self) -> bool { match self { - Self::DieselConnect(connection) => { - matches!(connection, diesel::ConnectionError::BadConnection(_)) - } + Self::DieselConnect(_) => true, + Self::DieselResult(result) => retryable!(result), Self::Pool(_) => true, + Self::Lock(_) => true, + Self::SqlCipherNotLoaded => true, + Self::PoolNeedsConnection => true, + Self::SqlCipherKeyIncorrect => false, _ => false, } } } // OpenMLS KeyStore errors -impl RetryableError for openmls::group::AddMembersError { +impl RetryableError for openmls::group::AddMembersError { fn is_retryable(&self) -> bool { match self { Self::CreateCommitError(commit) => retryable!(commit), + Self::StorageError(storage) => retryable!(storage), + Self::GroupStateError(group_state) => retryable!(group_state), _ => false, } } } -impl RetryableError for openmls::group::CreateCommitError { +impl RetryableError for openmls::group::CreateCommitError { fn is_retryable(&self) -> bool { match self { Self::KeyStoreError(storage) => retryable!(storage), - Self::KeyPackageGenerationError(generation) => retryable!(generation), + Self::LeafNodeUpdateError(leaf_node_update) => retryable!(leaf_node_update), + _ => false, + } + } +} + +impl RetryableError for openmls::treesync::LeafNodeUpdateError { + fn is_retryable(&self) -> bool { + match self { + Self::Storage(storage) => retryable!(storage), _ => false, } } @@ -76,35 +118,90 @@ impl RetryableError for openmls::key_packages::errors::KeyPackageNewError { } } -impl RetryableError for openmls::group::RemoveMembersError { +impl RetryableError for openmls::group::RemoveMembersError { fn is_retryable(&self) -> bool { match self { Self::CreateCommitError(commit) => retryable!(commit), + Self::GroupStateError(group_state) => retryable!(group_state), + Self::StorageError(storage) => retryable!(storage), + _ => false, + } + } +} + +impl RetryableError for openmls::group::NewGroupError { + fn is_retryable(&self) -> bool { + match self { + Self::StorageError(storage) => retryable!(storage), + _ => false, + } + } +} + +impl RetryableError + for openmls::group::UpdateGroupMembershipError +{ + fn is_retryable(&self) -> bool { + match self { + Self::CreateCommitError(create_commit) => retryable!(create_commit), + Self::GroupStateError(group_state) => retryable!(group_state), + Self::StorageError(storage) => retryable!(storage), _ => false, } } } -impl RetryableError for openmls::group::NewGroupError { +impl RetryableError for openmls::prelude::MlsGroupStateError { + fn is_retryable(&self) -> bool { + false + } +} + +impl RetryableError + for openmls::prelude::CreateGroupContextExtProposalError +{ fn is_retryable(&self) -> bool { match self { + Self::CreateCommitError(create_commit) => retryable!(create_commit), Self::StorageError(storage) => retryable!(storage), _ => false, } } } -impl RetryableError for openmls::group::SelfUpdateError { +impl RetryableError for openmls::group::SelfUpdateError { fn is_retryable(&self) -> bool { match self { Self::CreateCommitError(commit) => retryable!(commit), + Self::GroupStateError(group_state) => retryable!(group_state), + Self::StorageError(storage) => retryable!(storage), + _ => false, + } + } +} + +impl RetryableError + for openmls::prelude::CreationFromExternalError +{ + fn is_retryable(&self) -> bool { + match self { + Self::WriteToStorageError(storage) => retryable!(storage), + _ => false, + } + } +} + +impl RetryableError for openmls::prelude::WelcomeError { + fn is_retryable(&self) -> bool { + match self { + Self::PublicGroupError(creation_err) => retryable!(creation_err), Self::StorageError(storage) => retryable!(storage), _ => false, } } } -impl RetryableError for openmls::group::WelcomeError { +impl RetryableError for openmls::group::MergeCommitError { fn is_retryable(&self) -> bool { match self { Self::StorageError(storage) => retryable!(storage), @@ -112,3 +209,21 @@ impl RetryableError for openmls::group::WelcomeError { } } } + +impl RetryableError for openmls::group::MergePendingCommitError { + fn is_retryable(&self) -> bool { + match self { + Self::MlsGroupStateError(err) => retryable!(err), + Self::MergeCommitError(err) => retryable!(err), + } + } +} + +impl RetryableError for openmls::prelude::ProcessMessageError { + fn is_retryable(&self) -> bool { + match self { + Self::GroupStateError(err) => retryable!(err), + _ => false, + } + } +} diff --git a/xmtp_mls/src/storage/mod.rs b/xmtp_mls/src/storage/mod.rs index 82ca3f125..dcc9663c9 100644 --- a/xmtp_mls/src/storage/mod.rs +++ b/xmtp_mls/src/storage/mod.rs @@ -1,6 +1,6 @@ mod encrypted_store; mod errors; -mod serialization; +pub mod serialization; pub mod sql_key_store; pub use encrypted_store::*; diff --git a/xmtp_mls/src/storage/serialization.rs b/xmtp_mls/src/storage/serialization.rs index a877af5ef..c8efb2017 100644 --- a/xmtp_mls/src/storage/serialization.rs +++ b/xmtp_mls/src/storage/serialization.rs @@ -6,7 +6,7 @@ pub fn db_serialize(value: &T) -> Result, StorageError> where T: ?Sized + Serialize, { - serde_json::to_vec(value) + bincode::serialize(value) .map_err(|_| StorageError::Serialization("Failed to db_serialize".to_string())) } @@ -14,6 +14,6 @@ pub fn db_deserialize(bytes: &[u8]) -> Result where T: serde::de::DeserializeOwned, { - serde_json::from_slice(bytes) + bincode::deserialize::(bytes) .map_err(|_| StorageError::Deserialization("Failed to db_deserialize".to_string())) } diff --git a/xmtp_mls/src/storage/sql_key_store.rs b/xmtp_mls/src/storage/sql_key_store.rs index 0b8bdc87e..c37296bc1 100644 --- a/xmtp_mls/src/storage/sql_key_store.rs +++ b/xmtp_mls/src/storage/sql_key_store.rs @@ -1,13 +1,14 @@ +use crate::{retry::RetryableError, retryable}; + use super::encrypted_store::db_connection::DbConnection; +use bincode; use diesel::{ prelude::*, sql_types::Binary, {sql_query, RunQueryDsl}, }; -use log::error; use openmls_traits::storage::*; use serde::Serialize; -use serde_json::{from_slice, from_value, Value}; const SELECT_QUERY: &str = "SELECT value_bytes FROM openmls_key_value WHERE key_bytes = ? AND version = ?"; @@ -24,7 +25,7 @@ struct StorageData { value_bytes: Vec, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct SqlKeyStore { // Directly wrap the DbConnection which is a SqliteConnection in this case conn: DbConnection, @@ -35,10 +36,6 @@ impl SqlKeyStore { Self { conn } } - pub fn conn(&self) -> DbConnection { - self.conn.clone() - } - pub fn conn_ref(&self) -> &DbConnection { &self.conn } @@ -47,7 +44,7 @@ impl SqlKeyStore { &self, storage_key: &Vec, ) -> Result, diesel::result::Error> { - self.conn().raw_query(|conn| { + self.conn_ref().raw_query(|conn| { sql_query(SELECT_QUERY) .bind::(&storage_key) .bind::(VERSION as i32) @@ -60,7 +57,7 @@ impl SqlKeyStore { storage_key: &Vec, value: &[u8], ) -> Result { - self.conn().raw_query(|conn| { + self.conn_ref().raw_query(|conn| { sql_query(REPLACE_QUERY) .bind::(&storage_key) .bind::(VERSION as i32) @@ -74,7 +71,7 @@ impl SqlKeyStore { storage_key: &Vec, modified_data: &Vec, ) -> Result { - self.conn().raw_query(|conn| { + self.conn_ref().raw_query(|conn| { sql_query(UPDATE_QUERY) .bind::(&modified_data) .bind::(&storage_key) @@ -89,11 +86,11 @@ impl SqlKeyStore { key: &[u8], value: &[u8], ) -> Result<(), >::Error> { - log::debug!("write {}", String::from_utf8_lossy(label)); + tracing::debug!("write {}", String::from_utf8_lossy(label)); let storage_key = build_key_from_vec::(label, key.to_vec()); - let _ = self.replace_query::(&storage_key, value); + let _ = self.replace_query::(&storage_key, value)?; Ok(()) } @@ -104,40 +101,29 @@ impl SqlKeyStore { key: &[u8], value: &[u8], ) -> Result<(), >::Error> { - log::debug!("append {}", String::from_utf8_lossy(label)); + tracing::debug!("append {}", String::from_utf8_lossy(label)); let storage_key = build_key_from_vec::(label, key.to_vec()); - let current_data: Result, diesel::result::Error> = - self.select_query::(&storage_key); - - match current_data { - Ok(data) => { - if let Some(entry) = data.into_iter().next() { - // The value in the storage is an array of array of bytes, encoded as json. - match from_slice::(&entry.value_bytes) { - Ok(mut deserialized) => { - // Assuming value is JSON and needs to be added to an array - if let Value::Array(ref mut arr) = deserialized { - arr.push(Value::from(value)); - } - - let modified_data = serde_json::to_vec(&deserialized) - .map_err(|_| MemoryStorageError::SerializationError)?; - - let _ = self.update_query::(&storage_key, &modified_data); - Ok(()) - } - Err(_e) => Err(MemoryStorageError::SerializationError), - } - } else { - // Add a first entry - let value_bytes = &serde_json::to_vec(&[value])?; - let _ = self.replace_query::(&storage_key, value_bytes); + let data = self.select_query::(&storage_key)?; + if let Some(entry) = data.into_iter().next() { + // The value in the storage is an array of array of bytes + match bincode::deserialize::>>(&entry.value_bytes) { + Ok(mut deserialized) => { + deserialized.push(value.to_vec()); + let modified_data = bincode::serialize(&deserialized)?; + + let _ = self.update_query::(&storage_key, &modified_data)?; Ok(()) } + Err(_e) => Err(SqlKeyStoreError::SerializationError), } - Err(_) => Err(MemoryStorageError::None), + } else { + // Add a first entry + let value_bytes = &bincode::serialize(&vec![value])?; + let _ = self.replace_query::(&storage_key, value_bytes)?; + + Ok(()) } } @@ -147,48 +133,31 @@ impl SqlKeyStore { key: &[u8], value: &[u8], ) -> Result<(), >::Error> { - log::debug!("remove_item {}", String::from_utf8_lossy(label)); + tracing::debug!("remove_item {}", String::from_utf8_lossy(label)); let storage_key = build_key_from_vec::(label, key.to_vec()); - let current_data: Result, diesel::result::Error> = - self.select_query::(&storage_key); - - match current_data { - Ok(data) => { - if let Some(entry) = data.into_iter().next() { - // The value in the storage is an array of array of bytes, encoded as json. - match from_slice::(&entry.value_bytes) { - Ok(mut deserialized) => { - if let Value::Array(ref mut arr) = deserialized { - // Find and remove the value. - let vpos = arr.iter().position(|v| { - match from_value::>(v.clone()) { - Ok(deserialized_value) => deserialized_value == value, - Err(_) => false, - } - }); - - if let Some(pos) = vpos { - arr.remove(pos); - } - } - let modified_data = serde_json::to_vec(&deserialized) - .map_err(|_| MemoryStorageError::SerializationError)?; - - let _ = self.update_query::(&storage_key, &modified_data); - Ok(()) - } - Err(_) => Err(MemoryStorageError::SerializationError), - } - } else { - // Add a first entry - let value_bytes = serde_json::to_vec(&[value]) - .map_err(|_| MemoryStorageError::SerializationError)?; - let _ = self.replace_query::(&storage_key, &value_bytes); - Ok(()) - } + let data: Vec = self.select_query::(&storage_key)?; + + if let Some(entry) = data.into_iter().next() { + // The value in the storage is an array of array of bytes. + let mut deserialized = bincode::deserialize::>>(&entry.value_bytes) + .map_err(|_| SqlKeyStoreError::SerializationError)?; + let vpos = deserialized.iter().position(|v| v == value); + + if let Some(pos) = vpos { + deserialized.remove(pos); } - Err(_) => Err(MemoryStorageError::None), + let modified_data = bincode::serialize(&deserialized) + .map_err(|_| SqlKeyStoreError::SerializationError)?; + + let _ = self.update_query::(&storage_key, &modified_data)?; + Ok(()) + } else { + // Add a first entry + let value_bytes = + bincode::serialize(&[value]).map_err(|_| SqlKeyStoreError::SerializationError)?; + let _ = self.replace_query::(&storage_key, &value_bytes)?; + Ok(()) } } @@ -197,28 +166,19 @@ impl SqlKeyStore { label: &[u8], key: &[u8], ) -> Result, >::Error> { - log::debug!("read {}", String::from_utf8_lossy(label)); + tracing::debug!("read {}", String::from_utf8_lossy(label)); let storage_key = build_key_from_vec::(label, key.to_vec()); - let results: Result, diesel::result::Error> = - self.select_query::(&storage_key); - - match results { - Ok(data) => { - if let Some(entry) = data.into_iter().next() { - match serde_json::from_slice::(&entry.value_bytes) { - Ok(deserialized) => Ok(Some(deserialized)), - Err(e) => { - eprintln!("Error occurred: {}", e); - Err(MemoryStorageError::SerializationError) - } - } - } else { - Ok(None) - } - } - Err(_e) => Err(MemoryStorageError::None), + let data = self.select_query::(&storage_key)?; + + if let Some(entry) = data.into_iter().next() { + let deserialized = bincode::deserialize::(&entry.value_bytes) + .map_err(|_| SqlKeyStoreError::SerializationError)?; + + Ok(Some(deserialized)) + } else { + Ok(None) } } @@ -227,29 +187,28 @@ impl SqlKeyStore { label: &[u8], key: &[u8], ) -> Result, >::Error> { - log::debug!("read_list {}", String::from_utf8_lossy(label)); + tracing::debug!("read_list {}", String::from_utf8_lossy(label)); let storage_key = build_key_from_vec::(label, key.to_vec()); - - match self.select_query::(&storage_key) { - Ok(results) => { - if let Some(entry) = results.into_iter().next() { - let list = from_slice::>>(&entry.value_bytes)?; - - // Read the values from the bytes in the list - let mut deserialized_list = Vec::new(); - for v in list { - match serde_json::from_slice(&v) { - Ok(deserialized_value) => deserialized_list.push(deserialized_value), - Err(_) => return Err(MemoryStorageError::SerializationError), - } + let results = self.select_query::(&storage_key)?; + + if let Some(entry) = results.into_iter().next() { + let list = bincode::deserialize::>>(&entry.value_bytes)?; + + // Read the values from the bytes in the list + let mut deserialized_list = Vec::new(); + for v in list { + match bincode::deserialize::(&v) { + Ok(deserialized_value) => deserialized_list.push(deserialized_value), + Err(e) => { + tracing::error!("Error occurred: {}", e); + return Err(SqlKeyStoreError::SerializationError); } - Ok(deserialized_list) - } else { - Ok(vec![]) } } - Err(_e) => Err(MemoryStorageError::None), + Ok(deserialized_list) + } else { + Ok(vec![]) } } @@ -260,19 +219,19 @@ impl SqlKeyStore { ) -> Result<(), >::Error> { let storage_key = build_key_from_vec::(label, key.to_vec()); - let _ = self.conn().raw_query(|conn| { + let _ = self.conn_ref().raw_query(|conn| { sql_query(DELETE_QUERY) .bind::(&storage_key) .bind::(VERSION as i32) .execute(conn) - }); + })?; Ok(()) } } /// Errors thrown by the key store. -#[derive(thiserror::Error, Debug, Copy, Clone, PartialEq, Eq)] -pub enum MemoryStorageError { +#[derive(thiserror::Error, Debug, PartialEq)] +pub enum SqlKeyStoreError { #[error("The key store does not allow storing serialized values.")] UnsupportedValueTypeBytes, #[error("Updating is not supported by this key store.")] @@ -280,7 +239,18 @@ pub enum MemoryStorageError { #[error("Error serializing value.")] SerializationError, #[error("Value does not exist.")] - None, + NotFound, + #[error("database error: {0}")] + Storage(#[from] diesel::result::Error), +} + +impl RetryableError for SqlKeyStoreError { + fn is_retryable(&self) -> bool { + match self { + SqlKeyStoreError::Storage(err) => retryable!(err), + _ => false, + } + } } const KEY_PACKAGE_LABEL: &[u8] = b"KeyPackage"; @@ -299,19 +269,17 @@ const CONFIRMATION_TAG_LABEL: &[u8] = b"ConfirmationTag"; const OWN_LEAF_NODE_INDEX_LABEL: &[u8] = b"OwnLeafNodeIndex"; const EPOCH_SECRETS_LABEL: &[u8] = b"EpochSecrets"; const MESSAGE_SECRETS_LABEL: &[u8] = b"MessageSecrets"; -const USE_RATCHET_TREE_LABEL: &[u8] = b"UseRatchetTree"; // related to MlsGroup const JOIN_CONFIG_LABEL: &[u8] = b"MlsGroupJoinConfig"; const OWN_LEAF_NODES_LABEL: &[u8] = b"OwnLeafNodes"; -const AAD_LABEL: &[u8] = b"AAD"; const GROUP_STATE_LABEL: &[u8] = b"GroupState"; const QUEUED_PROPOSAL_LABEL: &[u8] = b"QueuedProposal"; const PROPOSAL_QUEUE_REFS_LABEL: &[u8] = b"ProposalQueueRefs"; const RESUMPTION_PSK_STORE_LABEL: &[u8] = b"ResumptionPskStore"; impl StorageProvider for SqlKeyStore { - type Error = MemoryStorageError; + type Error = SqlKeyStoreError; fn queue_proposal< GroupId: traits::GroupId, @@ -324,13 +292,13 @@ impl StorageProvider for SqlKeyStore { proposal: &QueuedProposal, ) -> Result<(), Self::Error> { // write proposal to key (group_id, proposal_ref) - let key = serde_json::to_vec(&(group_id, proposal_ref))?; - let value = serde_json::to_vec(proposal)?; + let key = bincode::serialize(&(group_id, proposal_ref))?; + let value = bincode::serialize(proposal)?; self.write::(QUEUED_PROPOSAL_LABEL, &key, &value)?; // update proposal list for group_id let key = build_key::(PROPOSAL_QUEUE_REFS_LABEL, group_id)?; - let value = serde_json::to_vec(proposal_ref)?; + let value = bincode::serialize(proposal_ref)?; self.append::(PROPOSAL_QUEUE_REFS_LABEL, &key, &value)?; Ok(()) @@ -345,7 +313,7 @@ impl StorageProvider for SqlKeyStore { tree: &TreeSync, ) -> Result<(), Self::Error> { let key = build_key::(TREE_LABEL, group_id)?; - let value = serde_json::to_vec(&tree)?; + let value = bincode::serialize(&tree)?; self.write::(TREE_LABEL, &key, &value) } @@ -358,7 +326,7 @@ impl StorageProvider for SqlKeyStore { interim_transcript_hash: &InterimTranscriptHash, ) -> Result<(), Self::Error> { let key = build_key::(INTERIM_TRANSCRIPT_HASH_LABEL, group_id)?; - let value = serde_json::to_vec(&interim_transcript_hash)?; + let value = bincode::serialize(&interim_transcript_hash)?; let _ = self.write::(INTERIM_TRANSCRIPT_HASH_LABEL, &key, &value); Ok(()) @@ -373,10 +341,9 @@ impl StorageProvider for SqlKeyStore { group_context: &GroupContext, ) -> Result<(), Self::Error> { let key = build_key::(GROUP_CONTEXT_LABEL, group_id)?; - let value = serde_json::to_vec(&group_context)?; - let _ = self.write::(GROUP_CONTEXT_LABEL, &key, &value); + let value = bincode::serialize(&group_context)?; - Ok(()) + self.write::(GROUP_CONTEXT_LABEL, &key, &value) } fn write_confirmation_tag< @@ -388,10 +355,9 @@ impl StorageProvider for SqlKeyStore { confirmation_tag: &ConfirmationTag, ) -> Result<(), Self::Error> { let key = build_key::(CONFIRMATION_TAG_LABEL, group_id)?; - let value = serde_json::to_vec(&confirmation_tag)?; - let _ = self.write::(CONFIRMATION_TAG_LABEL, &key, &value); + let value = bincode::serialize(&confirmation_tag)?; - Ok(()) + self.write::(CONFIRMATION_TAG_LABEL, &key, &value) } fn write_signature_key_pair< @@ -406,10 +372,9 @@ impl StorageProvider for SqlKeyStore { SIGNATURE_KEY_PAIR_LABEL, public_key, )?; - let value = serde_json::to_vec(&signature_key_pair)?; - let _ = self.write::(SIGNATURE_KEY_PAIR_LABEL, &key, &value); + let value = bincode::serialize(&signature_key_pair)?; - Ok(()) + self.write::(SIGNATURE_KEY_PAIR_LABEL, &key, &value) } fn queued_proposal_refs< @@ -436,16 +401,16 @@ impl StorageProvider for SqlKeyStore { refs.into_iter() .map(|proposal_ref| -> Result<_, _> { - let key = serde_json::to_vec(&(group_id, &proposal_ref))?; + let key = bincode::serialize(&(group_id, &proposal_ref))?; match self.read(QUEUED_PROPOSAL_LABEL, &key)? { Some(proposal) => Ok((proposal_ref, proposal)), - None => Err(MemoryStorageError::SerializationError), + None => Err(SqlKeyStoreError::NotFound), } }) .collect::, _>>() } - fn treesync< + fn tree< GroupId: traits::GroupId, TreeSync: traits::TreeSync, >( @@ -517,12 +482,10 @@ impl StorageProvider for SqlKeyStore { key_package: &KeyPackage, ) -> Result<(), Self::Error> { let key = build_key::(KEY_PACKAGE_LABEL, hash_ref)?; - let value = serde_json::to_vec(&key_package)?; + let value = bincode::serialize(&key_package)?; // Store the key package - self.write::(KEY_PACKAGE_LABEL, &key, &value)?; - - Ok(()) + self.write::(KEY_PACKAGE_LABEL, &key, &value) } fn write_psk< @@ -546,10 +509,11 @@ impl StorageProvider for SqlKeyStore { ) -> Result<(), Self::Error> { let key = build_key::(ENCRYPTION_KEY_PAIR_LABEL, public_key)?; + self.write::( ENCRYPTION_KEY_PAIR_LABEL, &key, - &serde_json::to_vec(key_pair)?, + &bincode::serialize(key_pair)?, ) } @@ -561,6 +525,7 @@ impl StorageProvider for SqlKeyStore { hash_ref: &HashReference, ) -> Result, Self::Error> { let key = build_key::(KEY_PACKAGE_LABEL, hash_ref)?; + self.read(KEY_PACKAGE_LABEL, &key) } @@ -580,6 +545,7 @@ impl StorageProvider for SqlKeyStore { ) -> Result, Self::Error> { let key = build_key::(ENCRYPTION_KEY_PAIR_LABEL, public_key)?; + self.read(ENCRYPTION_KEY_PAIR_LABEL, &key) } @@ -593,6 +559,7 @@ impl StorageProvider for SqlKeyStore { SIGNATURE_KEY_PAIR_LABEL, public_key, )?; + self.delete::(SIGNATURE_KEY_PAIR_LABEL, &key) } @@ -602,6 +569,7 @@ impl StorageProvider for SqlKeyStore { ) -> Result<(), Self::Error> { let key = build_key::(ENCRYPTION_KEY_PAIR_LABEL, public_key)?; + self.delete::(ENCRYPTION_KEY_PAIR_LABEL, &key) } @@ -617,7 +585,7 @@ impl StorageProvider for SqlKeyStore { &self, _psk_id: &PskKey, ) -> Result<(), Self::Error> { - Err(MemoryStorageError::UnsupportedMethod) + Err(SqlKeyStoreError::UnsupportedMethod) } fn group_state< @@ -628,6 +596,7 @@ impl StorageProvider for SqlKeyStore { group_id: &GroupId, ) -> Result, Self::Error> { let key = build_key::(GROUP_STATE_LABEL, group_id)?; + self.read(GROUP_STATE_LABEL, &key) } @@ -640,7 +609,8 @@ impl StorageProvider for SqlKeyStore { group_state: &GroupState, ) -> Result<(), Self::Error> { let key = build_key::(GROUP_STATE_LABEL, group_id)?; - self.write::(GROUP_STATE_LABEL, &key, &serde_json::to_vec(group_state)?) + + self.write::(GROUP_STATE_LABEL, &key, &bincode::serialize(group_state)?) } fn delete_group_state>( @@ -648,6 +618,7 @@ impl StorageProvider for SqlKeyStore { group_id: &GroupId, ) -> Result<(), Self::Error> { let key = build_key::(GROUP_STATE_LABEL, group_id)?; + self.delete::(GROUP_STATE_LABEL, &key) } @@ -659,6 +630,7 @@ impl StorageProvider for SqlKeyStore { group_id: &GroupId, ) -> Result, Self::Error> { let key = build_key::(MESSAGE_SECRETS_LABEL, group_id)?; + self.read(MESSAGE_SECRETS_LABEL, &key) } @@ -671,10 +643,11 @@ impl StorageProvider for SqlKeyStore { message_secrets: &MessageSecrets, ) -> Result<(), Self::Error> { let key = build_key::(MESSAGE_SECRETS_LABEL, group_id)?; + self.write::( MESSAGE_SECRETS_LABEL, &key, - &serde_json::to_vec(message_secrets)?, + &bincode::serialize(message_secrets)?, ) } @@ -683,6 +656,7 @@ impl StorageProvider for SqlKeyStore { group_id: &GroupId, ) -> Result<(), Self::Error> { let key = build_key::(MESSAGE_SECRETS_LABEL, group_id)?; + self.delete::(MESSAGE_SECRETS_LABEL, &key) } @@ -693,7 +667,7 @@ impl StorageProvider for SqlKeyStore { &self, group_id: &GroupId, ) -> Result, Self::Error> { - self.read(RESUMPTION_PSK_STORE_LABEL, &serde_json::to_vec(group_id)?) + self.read(RESUMPTION_PSK_STORE_LABEL, &bincode::serialize(group_id)?) } fn write_resumption_psk_store< @@ -706,8 +680,8 @@ impl StorageProvider for SqlKeyStore { ) -> Result<(), Self::Error> { self.write::( RESUMPTION_PSK_STORE_LABEL, - &serde_json::to_vec(group_id)?, - &serde_json::to_vec(resumption_psk_store)?, + &bincode::serialize(group_id)?, + &bincode::serialize(resumption_psk_store)?, ) } @@ -715,7 +689,7 @@ impl StorageProvider for SqlKeyStore { &self, group_id: &GroupId, ) -> Result<(), Self::Error> { - self.delete::(RESUMPTION_PSK_STORE_LABEL, &serde_json::to_vec(group_id)?) + self.delete::(RESUMPTION_PSK_STORE_LABEL, &bincode::serialize(group_id)?) } fn own_leaf_index< @@ -741,7 +715,7 @@ impl StorageProvider for SqlKeyStore { self.write::( OWN_LEAF_NODE_INDEX_LABEL, &key, - &serde_json::to_vec(own_leaf_index)?, + &bincode::serialize(own_leaf_index)?, ) } @@ -753,31 +727,6 @@ impl StorageProvider for SqlKeyStore { self.delete::(OWN_LEAF_NODE_INDEX_LABEL, &key) } - fn use_ratchet_tree_extension>( - &self, - group_id: &GroupId, - ) -> Result, Self::Error> { - let key = build_key::(USE_RATCHET_TREE_LABEL, group_id)?; - self.read(USE_RATCHET_TREE_LABEL, &key) - } - - fn set_use_ratchet_tree_extension>( - &self, - group_id: &GroupId, - value: bool, - ) -> Result<(), Self::Error> { - let key = build_key::(USE_RATCHET_TREE_LABEL, group_id)?; - self.write::(USE_RATCHET_TREE_LABEL, &key, &serde_json::to_vec(&value)?) - } - - fn delete_use_ratchet_tree_extension>( - &self, - group_id: &GroupId, - ) -> Result<(), Self::Error> { - let key = build_key::(USE_RATCHET_TREE_LABEL, group_id)?; - self.delete::(USE_RATCHET_TREE_LABEL, &key) - } - fn group_epoch_secrets< GroupId: traits::GroupId, GroupEpochSecrets: traits::GroupEpochSecrets, @@ -801,7 +750,7 @@ impl StorageProvider for SqlKeyStore { self.write::( EPOCH_SECRETS_LABEL, &key, - &serde_json::to_vec(group_epoch_secrets)?, + &bincode::serialize(group_epoch_secrets)?, ) } @@ -825,10 +774,10 @@ impl StorageProvider for SqlKeyStore { key_pairs: &[HpkeKeyPair], ) -> Result<(), Self::Error> { let key = epoch_key_pairs_id(group_id, epoch, leaf_index)?; - let value = serde_json::to_vec(key_pairs)?; - log::debug!("Writing encryption epoch key pairs"); - log::debug!(" key: {}", hex::encode(&key)); - log::debug!(" value: {}", hex::encode(&value)); + let value = bincode::serialize(key_pairs)?; + tracing::debug!("Writing encryption epoch key pairs"); + tracing::debug!(" key: {}", hex::encode(&key)); + tracing::debug!(" value: {}", hex::encode(&value)); self.write::(EPOCH_KEY_PAIRS_LABEL, &key, &value) } @@ -843,37 +792,31 @@ impl StorageProvider for SqlKeyStore { epoch: &EpochKey, leaf_index: u32, ) -> Result, Self::Error> { - log::debug!("Reading encryption epoch key pairs"); + tracing::debug!("Reading encryption epoch key pairs"); let key = epoch_key_pairs_id(group_id, epoch, leaf_index)?; let storage_key = build_key_from_vec::(EPOCH_KEY_PAIRS_LABEL, key); - log::debug!(" key: {}", hex::encode(&storage_key)); + tracing::debug!(" key: {}", hex::encode(&storage_key)); let query = "SELECT value_bytes FROM openmls_key_value WHERE key_bytes = ? AND version = ?"; - let results: Result, diesel::result::Error> = - self.conn().raw_query(|conn| { - sql_query(query) - .bind::(&storage_key) - .bind::(CURRENT_VERSION as i32) - .load(conn) - }); - - match results { - Ok(data) => { - if let Some(entry) = data.into_iter().next() { - match serde_json::from_slice::>(&entry.value_bytes) { - Ok(deserialized) => Ok(deserialized), - Err(e) => { - eprintln!("Error occurred: {}", e); - Err(MemoryStorageError::SerializationError) - } - } - } else { - Ok(vec![]) + let data: Vec = self.conn_ref().raw_query(|conn| { + sql_query(query) + .bind::(&storage_key) + .bind::(CURRENT_VERSION as i32) + .load(conn) + })?; + + if let Some(entry) = data.into_iter().next() { + match bincode::deserialize::>(&entry.value_bytes) { + Ok(deserialized) => Ok(deserialized), + Err(e) => { + eprintln!("Error occurred: {}", e); + Err(SqlKeyStoreError::SerializationError) } } - Err(_e) => Err(MemoryStorageError::None), + } else { + Ok(vec![]) } } @@ -887,6 +830,7 @@ impl StorageProvider for SqlKeyStore { leaf_index: u32, ) -> Result<(), Self::Error> { let key = epoch_key_pairs_id(group_id, epoch, leaf_index)?; + self.delete::(EPOCH_KEY_PAIRS_LABEL, &key) } @@ -901,14 +845,13 @@ impl StorageProvider for SqlKeyStore { let proposal_refs: Vec = self.read_list(PROPOSAL_QUEUE_REFS_LABEL, &key)?; for proposal_ref in proposal_refs { - let key = serde_json::to_vec(&(group_id, proposal_ref))?; - let _ = self.delete::(QUEUED_PROPOSAL_LABEL, &key); + let key = bincode::serialize(&(group_id, proposal_ref))?; + self.delete::(QUEUED_PROPOSAL_LABEL, &key)?; } let key = build_key::(PROPOSAL_QUEUE_REFS_LABEL, group_id)?; - let _ = self.delete::(PROPOSAL_QUEUE_REFS_LABEL, &key); - Ok(()) + self.delete::(PROPOSAL_QUEUE_REFS_LABEL, &key) } fn mls_group_join_config< @@ -919,6 +862,7 @@ impl StorageProvider for SqlKeyStore { group_id: &GroupId, ) -> Result, Self::Error> { let key = build_key::(JOIN_CONFIG_LABEL, group_id)?; + self.read(JOIN_CONFIG_LABEL, &key) } @@ -931,7 +875,7 @@ impl StorageProvider for SqlKeyStore { config: &MlsGroupJoinConfig, ) -> Result<(), Self::Error> { let key = build_key::(JOIN_CONFIG_LABEL, group_id)?; - let value = serde_json::to_vec(config)?; + let value = bincode::serialize(config)?; self.write::(JOIN_CONFIG_LABEL, &key, &value) } @@ -943,8 +887,9 @@ impl StorageProvider for SqlKeyStore { &self, group_id: &GroupId, ) -> Result, Self::Error> { - log::debug!("own_leaf_nodes"); + tracing::debug!("own_leaf_nodes"); let key = build_key::(OWN_LEAF_NODES_LABEL, group_id)?; + self.read_list(OWN_LEAF_NODES_LABEL, &key) } @@ -957,47 +902,9 @@ impl StorageProvider for SqlKeyStore { leaf_node: &LeafNode, ) -> Result<(), Self::Error> { let key = build_key::(OWN_LEAF_NODES_LABEL, group_id)?; - let value = serde_json::to_vec(leaf_node)?; - self.append::(OWN_LEAF_NODES_LABEL, &key, &value) - } - - fn clear_own_leaf_nodes>( - &self, - group_id: &GroupId, - ) -> Result<(), Self::Error> { - let key = build_key::(OWN_LEAF_NODES_LABEL, group_id)?; - self.delete::(OWN_LEAF_NODES_LABEL, &key) - } - - fn aad>( - &self, - group_id: &GroupId, - ) -> Result, Self::Error> { - let key = build_key::(AAD_LABEL, group_id)?; - match self.read::>(AAD_LABEL, &key) { - Ok(Some(value)) => Ok(value), - Ok(None) => Ok(Vec::new()), - Err(e) => Err(e), - } - } - - fn write_aad>( - &self, - group_id: &GroupId, - aad: &[u8], - ) -> Result<(), Self::Error> { - let key = build_key::(AAD_LABEL, group_id)?; - let value = serde_json::to_vec(&aad)?; + let value = bincode::serialize(leaf_node)?; - self.write::(AAD_LABEL, &key, &value) - } - - fn delete_aad>( - &self, - group_id: &GroupId, - ) -> Result<(), Self::Error> { - let key = build_key::(AAD_LABEL, group_id)?; - self.delete::(AAD_LABEL, &key) + self.append::(OWN_LEAF_NODES_LABEL, &key, &value) } fn delete_own_leaf_nodes>( @@ -1021,6 +928,7 @@ impl StorageProvider for SqlKeyStore { group_id: &GroupId, ) -> Result<(), Self::Error> { let key = build_key::(TREE_LABEL, group_id)?; + self.delete::(TREE_LABEL, &key) } @@ -1029,6 +937,7 @@ impl StorageProvider for SqlKeyStore { group_id: &GroupId, ) -> Result<(), Self::Error> { let key = build_key::(CONFIRMATION_TAG_LABEL, group_id)?; + self.delete::(CONFIRMATION_TAG_LABEL, &key) } @@ -1037,6 +946,7 @@ impl StorageProvider for SqlKeyStore { group_id: &GroupId, ) -> Result<(), Self::Error> { let key = build_key::(GROUP_CONTEXT_LABEL, group_id)?; + self.delete::(GROUP_CONTEXT_LABEL, &key) } @@ -1045,6 +955,7 @@ impl StorageProvider for SqlKeyStore { group_id: &GroupId, ) -> Result<(), Self::Error> { let key = build_key::(INTERIM_TRANSCRIPT_HASH_LABEL, group_id)?; + self.delete::(INTERIM_TRANSCRIPT_HASH_LABEL, &key) } @@ -1058,11 +969,11 @@ impl StorageProvider for SqlKeyStore { ) -> Result<(), Self::Error> { // Delete the proposal ref let key = build_key::(PROPOSAL_QUEUE_REFS_LABEL, group_id)?; - let value = serde_json::to_vec(proposal_ref)?; + let value = bincode::serialize(proposal_ref)?; self.remove_item::(PROPOSAL_QUEUE_REFS_LABEL, &key, &value)?; // Delete the proposal - let key = serde_json::to_vec(&(group_id, proposal_ref))?; + let key = bincode::serialize(&(group_id, proposal_ref))?; self.delete::(QUEUED_PROPOSAL_LABEL, &key) } } @@ -1079,8 +990,8 @@ fn build_key_from_vec(label: &[u8], key: Vec) -> Vec { fn build_key( label: &[u8], key: K, -) -> Result, MemoryStorageError> { - let key_vec = serde_json::to_vec(&key)?; +) -> Result, SqlKeyStoreError> { + let key_vec = bincode::serialize(&key)?; Ok(build_key_from_vec::(label, key_vec)) } @@ -1088,15 +999,15 @@ fn epoch_key_pairs_id( group_id: &impl traits::GroupId, epoch: &impl traits::EpochKey, leaf_index: u32, -) -> Result, MemoryStorageError> { - let mut key = serde_json::to_vec(group_id)?; - key.extend_from_slice(&serde_json::to_vec(epoch)?); - key.extend_from_slice(&serde_json::to_vec(&leaf_index)?); +) -> Result, SqlKeyStoreError> { + let mut key = bincode::serialize(group_id)?; + key.extend_from_slice(&bincode::serialize(epoch)?); + key.extend_from_slice(&bincode::serialize(&leaf_index)?); Ok(key) } -impl From for MemoryStorageError { - fn from(_: serde_json::Error) -> Self { +impl From for SqlKeyStoreError { + fn from(_: bincode::Error) -> Self { Self::SerializationError } } @@ -1106,7 +1017,10 @@ mod tests { use openmls::group::GroupId; use openmls_basic_credential::{SignatureKeyPair, StorageId}; use openmls_traits::{ - storage::{traits, Entity, Key, StorageProvider, CURRENT_VERSION}, + storage::{ + traits::{self}, + Entity, Key, StorageProvider, CURRENT_VERSION, + }, OpenMlsProvider, }; use serde::{Deserialize, Serialize}; @@ -1114,7 +1028,7 @@ mod tests { use super::SqlKeyStore; use crate::{ configuration::CIPHERSUITE, - storage::{sql_key_store::MemoryStorageError, EncryptedMessageStore, StorageOption}, + storage::{sql_key_store::SqlKeyStoreError, EncryptedMessageStore, StorageOption}, utils::test::tmp_path, xmtp_openmls_provider::XmtpOpenMlsProvider, }; @@ -1128,8 +1042,8 @@ mod tests { ) .unwrap(); - let conn = &store.conn().unwrap(); - let key_store = SqlKeyStore::new(conn.clone()); + let conn = store.conn().unwrap(); + let key_store = SqlKeyStore::new(conn); let signature_keys = SignatureKeyPair::new(CIPHERSUITE.signature_algorithm()).unwrap(); let public_key = StorageId::from(signature_keys.to_public_vec()); @@ -1157,32 +1071,6 @@ mod tests { .is_none()); } - #[test] - fn list_write_remove() { - let db_path = tmp_path(); - let store = EncryptedMessageStore::new( - StorageOption::Persistent(db_path), - EncryptedMessageStore::generate_enc_key(), - ) - .unwrap(); - let conn = store.conn().unwrap(); - let key_store = SqlKeyStore::new(conn.clone()); - let provider = XmtpOpenMlsProvider::new(conn); - let group_id = GroupId::random(provider.rand()); - - assert!(key_store.aad::(&group_id).unwrap().is_empty()); - - key_store - .write_aad::(&group_id, "test".as_bytes()) - .unwrap(); - - assert!(!key_store.aad::(&group_id).unwrap().is_empty()); - - key_store.delete_aad::(&group_id).unwrap(); - - assert!(key_store.aad::(&group_id).unwrap().is_empty()); - } - #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] struct Proposal(Vec); impl traits::QueuedProposal for Proposal {} @@ -1203,7 +1091,6 @@ mod tests { ) .unwrap(); let conn = store.conn().unwrap(); - let key_store = SqlKeyStore::new(conn.clone()); let provider = XmtpOpenMlsProvider::new(conn); let group_id = GroupId::random(provider.rand()); let proposals = (0..10) @@ -1212,18 +1099,22 @@ mod tests { // Store proposals for (i, proposal) in proposals.iter().enumerate() { - key_store + provider + .storage() .queue_proposal::( &group_id, &ProposalRef(i), proposal, ) - .unwrap(); + .expect("Failed to queue proposal"); } + tracing::debug!("Finished with queued proposals"); // Read proposal refs - let proposal_refs_read: Vec = - key_store.queued_proposal_refs(&group_id).unwrap(); + let proposal_refs_read: Vec = provider + .storage() + .queued_proposal_refs(&group_id) + .expect("Failed to read proposal refs"); assert_eq!( (0..10).map(ProposalRef).collect::>(), proposal_refs_read @@ -1231,7 +1122,7 @@ mod tests { // Read proposals let proposals_read: Vec<(ProposalRef, Proposal)> = - key_store.queued_proposals(&group_id).unwrap(); + provider.storage().queued_proposals(&group_id).unwrap(); let proposals_expected: Vec<(ProposalRef, Proposal)> = (0..10) .map(ProposalRef) .zip(proposals.clone().into_iter()) @@ -1239,18 +1130,19 @@ mod tests { assert_eq!(proposals_expected, proposals_read); // Remove proposal 5 - key_store + provider + .storage() .remove_proposal(&group_id, &ProposalRef(5)) .unwrap(); let proposal_refs_read: Vec = - key_store.queued_proposal_refs(&group_id).unwrap(); + provider.storage().queued_proposal_refs(&group_id).unwrap(); let mut expected = (0..10).map(ProposalRef).collect::>(); expected.remove(5); assert_eq!(expected, proposal_refs_read); let proposals_read: Vec<(ProposalRef, Proposal)> = - key_store.queued_proposals(&group_id).unwrap(); + provider.storage().queued_proposals(&group_id).unwrap(); let mut proposals_expected: Vec<(ProposalRef, Proposal)> = (0..10) .map(ProposalRef) .zip(proposals.clone().into_iter()) @@ -1259,15 +1151,16 @@ mod tests { assert_eq!(proposals_expected, proposals_read); // Clear all proposals - key_store + provider + .storage() .clear_proposal_queue::(&group_id) .unwrap(); - let proposal_refs_read: Result, MemoryStorageError> = - key_store.queued_proposal_refs(&group_id); + let proposal_refs_read: Result, SqlKeyStoreError> = + provider.storage().queued_proposal_refs(&group_id); assert!(proposal_refs_read.unwrap().is_empty()); - let proposals_read: Result, MemoryStorageError> = - key_store.queued_proposals(&group_id); + let proposals_read: Result, SqlKeyStoreError> = + provider.storage().queued_proposals(&group_id); assert!(proposals_read.unwrap().is_empty()); } @@ -1280,7 +1173,6 @@ mod tests { ) .unwrap(); let conn = store.conn().unwrap(); - let key_store = SqlKeyStore::new(conn.clone()); let provider = XmtpOpenMlsProvider::new(conn); #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy)] @@ -1291,12 +1183,13 @@ mod tests { let group_id = GroupId::random(provider.rand()); // Group state - key_store + provider + .storage() .write_group_state(&group_id, &GroupState(77)) .unwrap(); // Read group state - let group_state: Option = key_store.group_state(&group_id).unwrap(); + let group_state: Option = provider.storage().group_state(&group_id).unwrap(); assert_eq!(GroupState(77), group_state.unwrap()); } } diff --git a/xmtp_mls/src/subscriptions.rs b/xmtp_mls/src/subscriptions.rs index c990510ff..2e19338a3 100644 --- a/xmtp_mls/src/subscriptions.rs +++ b/xmtp_mls/src/subscriptions.rs @@ -1,84 +1,121 @@ -use std::{ - collections::HashMap, - pin::Pin, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, Mutex, - }, -}; +use std::{collections::HashMap, sync::Arc}; -use futures::{Stream, StreamExt}; +use futures::{FutureExt, Stream, StreamExt}; use prost::Message; -use tokio::sync::oneshot::{self, Sender}; +use tokio::{sync::oneshot, task::JoinHandle}; +use tokio_stream::wrappers::errors::BroadcastStreamRecvError; use xmtp_proto::xmtp::mls::api::v1::WelcomeMessage; use crate::{ api::GroupFilter, client::{extract_welcome_message, ClientError}, groups::{extract_group_id, GroupError, MlsGroup}, - storage::group_message::StoredGroupMessage, + retry::Retry, + retry_async, + storage::{group::StoredGroup, group_message::StoredGroupMessage}, Client, XmtpApi, }; -// TODO simplify FfiStreamCloser + StreamCloser duplication -pub struct StreamCloser { - pub close_fn: Arc>>>, - pub is_closed_atomic: Arc, +#[derive(Debug)] +/// Wrapper around a [`tokio::task::JoinHandle`] but with a oneshot receiver +/// which allows waiting for a `with_callback` stream fn to be ready for stream items. +pub struct StreamHandle { + pub handle: JoinHandle, + start: Option>, } -impl StreamCloser { - pub fn end(&self) { - match self.close_fn.lock() { - Ok(mut close_fn_option) => { - let _ = close_fn_option.take().map(|close_fn| close_fn.send(())); - } - _ => { - log::warn!("close_fn already closed"); - } +/// Events local to this client +/// are broadcast across all senders/receivers of streams +#[derive(Clone, Debug)] +pub(crate) enum LocalEvents { + // a new group was created + NewGroup(MlsGroup), +} + +impl StreamHandle { + /// Waits for the stream to be fully spawned + pub async fn wait_for_ready(&mut self) { + if let Some(s) = self.start.take() { + let _ = s.await; } } +} - pub fn is_closed(&self) -> bool { - self.is_closed_atomic.load(Ordering::Relaxed) +impl From> for JoinHandle { + fn from(stream: StreamHandle) -> JoinHandle { + stream.handle } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub(crate) struct MessagesStreamInfo { pub convo_created_at_ns: i64, pub cursor: u64, } +impl From for (Vec, MessagesStreamInfo) { + fn from(group: StoredGroup) -> (Vec, MessagesStreamInfo) { + ( + group.id, + MessagesStreamInfo { + convo_created_at_ns: group.created_at_ns, + cursor: 0, + }, + ) + } +} + impl Client where - ApiClient: XmtpApi, + ApiClient: XmtpApi + 'static, { async fn process_streamed_welcome( &self, welcome: WelcomeMessage, ) -> Result { let welcome_v1 = extract_welcome_message(welcome)?; - - let creation_result = self - .context - .store - .transaction_async(|provider| async move { - MlsGroup::create_from_encrypted_welcome( - self, - &provider, - welcome_v1.hpke_public_key.as_slice(), - welcome_v1.data, - welcome_v1.id as i64, - ) - .await + let creation_result = retry_async!( + Retry::default(), + (async { + tracing::info!("Trying to process streamed welcome"); + let welcome_v1 = welcome_v1.clone(); + self.context + .store + .transaction_async(|provider| async move { + MlsGroup::create_from_encrypted_welcome( + self, + &provider, + welcome_v1.hpke_public_key.as_slice(), + welcome_v1.data, + welcome_v1.id as i64, + ) + .await + }) + .await }) - .await; + ); if let Some(err) = creation_result.as_ref().err() { - return Err(ClientError::Generic(err.to_string())); + let conn = self.context.store.conn()?; + let result = conn.find_group_by_welcome_id(welcome_v1.id as i64); + match result { + Ok(Some(group)) => { + tracing::info!( + "Loading existing group for welcome_id: {:?}", + group.welcome_id + ); + return Ok(MlsGroup::new( + self.context.clone(), + group.id, + group.created_at_ns, + )); + } + Ok(None) => return Err(ClientError::Generic(err.to_string())), + Err(e) => return Err(ClientError::Generic(e.to_string())), + } } - Ok(creation_result.unwrap()) + Ok(creation_result?) } pub async fn process_streamed_welcome_message( @@ -94,273 +131,357 @@ where pub async fn stream_conversations( &self, - ) -> Result + Send + '_>>, ClientError> { + ) -> Result + '_, ClientError> { + let event_queue = + tokio_stream::wrappers::BroadcastStream::new(self.local_events.subscribe()); + + let event_queue = event_queue.filter_map(|event| async move { + match event { + Ok(LocalEvents::NewGroup(g)) => Some(g), + Err(BroadcastStreamRecvError::Lagged(missed)) => { + tracing::warn!("Missed {missed} messages due to local event queue lagging"); + None + } + } + }); + let installation_key = self.installation_public_key(); let id_cursor = 0; + tracing::info!("Setting up conversation stream"); let subscription = self .api_client - .subscribe_welcome_messages(installation_key, Some(id_cursor as u64)) + .subscribe_welcome_messages(installation_key, Some(id_cursor)) .await?; let stream = subscription - .map(|welcome_result| async { - log::info!("Received conversation streaming payload"); - let welcome = welcome_result?; - self.process_streamed_welcome(welcome).await + .map(|welcome| async { + tracing::info!("Received conversation streaming payload"); + self.process_streamed_welcome(welcome?).await }) .filter_map(|res| async { match res.await { Ok(group) => Some(group), Err(err) => { - log::error!("Error processing stream entry: {:?}", err); + tracing::error!( + "Error processing stream entry for conversation: {:?}", + err + ); None } } }); - Ok(Box::pin(stream)) + Ok(futures::stream::select(stream, event_queue)) } + #[tracing::instrument(skip(self, group_id_to_info))] pub(crate) async fn stream_messages( - self: Arc, - group_id_to_info: HashMap, MessagesStreamInfo>, - ) -> Result + Send>>, ClientError> { + &self, + group_id_to_info: Arc, MessagesStreamInfo>>, + ) -> Result + '_, ClientError> + where + ApiClient: 'static, + { let filters: Vec = group_id_to_info .iter() .map(|(group_id, info)| GroupFilter::new(group_id.clone(), Some(info.cursor))) .collect(); + let messages_subscription = self.api_client.subscribe_group_messages(filters).await?; let stream = messages_subscription .map(move |res| { - let context = self.context.clone(); - let client = self.clone(); - - let group_id_to_info = group_id_to_info.clone(); + let group_info = group_id_to_info.clone(); async move { match res { Ok(envelope) => { - log::info!("Received message streaming payload"); + tracing::info!("Received message streaming payload"); let group_id = extract_group_id(&envelope)?; - let stream_info = group_id_to_info.get(&group_id).ok_or( + tracing::info!("Extracted group id {}", hex::encode(&group_id)); + let stream_info = group_info.get(&group_id).ok_or( ClientError::StreamInconsistency( "Received message for a non-subscribed group".to_string(), ), )?; - // TODO update cursor - MlsGroup::new(context, group_id, stream_info.convo_created_at_ns) - .process_stream_entry(envelope, client) - .await + let mls_group = MlsGroup::new( + self.context.clone(), + group_id, + stream_info.convo_created_at_ns, + ); + mls_group.process_stream_entry(envelope, self).await } Err(err) => Err(GroupError::Api(err)), } } }) - .filter_map(move |res| async { + .filter_map(|res| async { match res.await { Ok(Some(message)) => Some(message), Ok(None) => { - log::info!("Skipped message streaming payload"); + tracing::info!("Skipped message streaming payload"); None } Err(err) => { - log::error!("Error processing stream entry: {:?}", err); + tracing::error!("Error processing stream entry: {:?}", err); None } } }); - - Ok(Box::pin(stream)) + Ok(stream) } } impl Client where - ApiClient: XmtpApi, + ApiClient: XmtpApi + 'static, { pub fn stream_conversations_with_callback( client: Arc>, mut convo_callback: impl FnMut(MlsGroup) + Send + 'static, - mut on_close_callback: impl FnMut() + Send + 'static, - ) -> Result { - let (close_sender, close_receiver) = oneshot::channel::<()>(); - let is_closed = Arc::new(AtomicBool::new(false)); - let is_closed_clone = is_closed.clone(); - - tokio::spawn(async move { - let mut stream = client.stream_conversations().await.unwrap(); - let mut close_receiver = close_receiver; - loop { - tokio::select! { - item = stream.next() => { - match item { - Some(convo) => { convo_callback(convo) }, - None => break - } - } - _ = &mut close_receiver => { - on_close_callback(); - break; - } - } + ) -> StreamHandle> { + let (tx, rx) = oneshot::channel(); + + let handle = tokio::spawn(async move { + let stream = client.stream_conversations().await?; + futures::pin_mut!(stream); + let _ = tx.send(()); + while let Some(convo) = stream.next().await { + tracing::info!("Trigger conversation callback"); + convo_callback(convo) } - is_closed_clone.store(true, Ordering::Relaxed); - log::info!("closing stream"); + tracing::debug!("`stream_conversations` stream ended, dropping stream"); + Ok(()) }); - Ok(StreamCloser { - close_fn: Arc::new(Mutex::new(Some(close_sender))), - is_closed_atomic: is_closed, - }) + StreamHandle { + start: Some(rx), + handle, + } } pub(crate) fn stream_messages_with_callback( client: Arc>, group_id_to_info: HashMap, MessagesStreamInfo>, mut callback: impl FnMut(StoredGroupMessage) + Send + 'static, - ) -> Result { - let (close_sender, close_receiver) = oneshot::channel::<()>(); - let is_closed = Arc::new(AtomicBool::new(false)); - - let is_closed_clone = is_closed.clone(); - tokio::spawn(async move { - let mut stream = Self::stream_messages(client, group_id_to_info) - .await - .unwrap(); - let mut close_receiver = close_receiver; - loop { - tokio::select! { - item = stream.next() => { - match item { - Some(message) => callback(message), - None => break - } - } - _ = &mut close_receiver => { - break; - } - } + ) -> StreamHandle> { + let (tx, rx) = oneshot::channel(); + + let client = client.clone(); + let handle = tokio::spawn(async move { + let stream = Self::stream_messages(&client, group_id_to_info.into()).await?; + let _ = tx.send(()); + futures::pin_mut!(stream); + while let Some(message) = stream.next().await { + callback(message) } - is_closed_clone.store(true, Ordering::Relaxed); - log::info!("closing stream"); + tracing::debug!("`stream_messages` stream ended, dropping stream"); + Ok(()) }); - Ok(StreamCloser { - close_fn: Arc::new(Mutex::new(Some(close_sender))), - is_closed_atomic: is_closed, - }) - } - - pub async fn stream_all_messages_with_callback( - client: Arc>, - callback: impl FnMut(StoredGroupMessage) + Send + Sync + 'static, - ) -> Result { - client.sync_welcomes().await?; // TODO pipe cursor from welcomes sync into groups_stream - Self::stream_all_messages_with_callback_sync(client, callback) + StreamHandle { + start: Some(rx), + handle, + } } - /// Requires a sync welcomes before use - pub fn stream_all_messages_with_callback_sync( - client: Arc>, - callback: impl FnMut(StoredGroupMessage) + Send + Sync + 'static, - ) -> Result { - let callback = Arc::new(Mutex::new(callback)); + pub async fn stream_all_messages( + &self, + ) -> Result> + '_, ClientError> { + self.sync_welcomes().await?; - let mut group_id_to_info: HashMap, MessagesStreamInfo> = client + let mut group_id_to_info = self .store() .conn()? .find_groups(None, None, None, None)? .into_iter() - .map(|group| { - ( - group.id.clone(), - MessagesStreamInfo { - convo_created_at_ns: group.created_at_ns, - cursor: 0, - }, - ) - }) - .collect(); + .map(Into::into) + .collect::, MessagesStreamInfo>>(); + + let stream = async_stream::stream! { + let messages_stream = self + .stream_messages(Arc::new(group_id_to_info.clone())) + .await?; + futures::pin_mut!(messages_stream); - let callback_clone = callback.clone(); - let messages_stream_closer_mutex = - Arc::new(Mutex::new(Self::stream_messages_with_callback( - client.clone(), - group_id_to_info.clone(), - move |message| callback_clone.lock().unwrap()(message), // TODO fix unwrap - )?)); - let messages_stream_closer_mutex_clone = messages_stream_closer_mutex.clone(); - let groups_stream_closer = Self::stream_conversations_with_callback( - client.clone(), - move |convo| { - // TODO make sure key comparison works correctly - if group_id_to_info.contains_key(&convo.group_id) { - return; + tracing::info!("Setting up conversation stream in stream_all_messages"); + let convo_stream = self.stream_conversations().await?; + futures::pin_mut!(convo_stream); + + let mut extra_messages = Vec::new(); + + loop { + tokio::select! { + // biased enforces an order to select!. If a message and a group are both ready + // at the same time, `biased` mode will process the message before the new + // group. + biased; + + messages = futures::future::ready(&mut extra_messages), if !extra_messages.is_empty() => { + for message in messages.drain(0..) { + yield Ok(message); + } + }, + Some(message) = messages_stream.next() => { + // an error can only mean the receiver has been dropped or closed so we're + // safe to end the stream + yield Ok(message); + } + Some(new_group) = convo_stream.next() => { + tracing::info!("Received new conversation inside streamAllMessages"); + if group_id_to_info.contains_key(&new_group.group_id) { + continue; + } + for info in group_id_to_info.values_mut() { + info.cursor = 0; + } + group_id_to_info.insert( + new_group.group_id, + MessagesStreamInfo { + convo_created_at_ns: new_group.created_at_ns, + cursor: 1, // For the new group, stream all messages since the group was created + }, + ); + let new_messages_stream = match self.stream_messages(Arc::new(group_id_to_info.clone())).await { + Ok(stream) => stream, + Err(e) => { + tracing::error!("{}", e); + break; + } + }; + + tracing::debug!("switching streams"); + // attempt to drain all ready messages from existing stream + while let Some(Some(message)) = messages_stream.next().now_or_never() { + extra_messages.push(message); + } + messages_stream.set(new_messages_stream); + }, } - // Close existing message stream - // TODO remove unwrap - let mut messages_stream_closer = messages_stream_closer_mutex.lock().unwrap(); - messages_stream_closer.end(); - - // Set up new stream. For existing groups, stream new messages only by unsetting the cursor - for info in group_id_to_info.values_mut() { - info.cursor = 0; + } + }; + + Ok(stream) + } + + pub fn stream_all_messages_with_callback( + client: Arc>, + mut callback: impl FnMut(StoredGroupMessage) + Send + Sync + 'static, + ) -> StreamHandle> { + let (tx, rx) = oneshot::channel(); + + let handle = tokio::spawn(async move { + let stream = Self::stream_all_messages(&client).await?; + let _ = tx.send(()); + futures::pin_mut!(stream); + while let Some(message) = stream.next().await { + match message { + Ok(m) => callback(m), + Err(m) => tracing::error!("error during stream all messages {}", m), } - group_id_to_info.insert( - convo.group_id, - MessagesStreamInfo { - convo_created_at_ns: convo.created_at_ns, - cursor: 1, // For the new group, stream all messages since the group was created - }, - ); - - // Open new message stream - let callback_clone = callback.clone(); - *messages_stream_closer = Self::stream_messages_with_callback( - client.clone(), - group_id_to_info.clone(), - move |message| callback_clone.lock().unwrap()(message), // TODO fix unwrap - ) - .unwrap(); // TODO fix unwrap - }, - move || { - messages_stream_closer_mutex_clone.lock().unwrap().end(); - }, - )?; + } + tracing::debug!("`stream_all_messages` stream ended, dropping stream"); + Ok(()) + }); - Ok(groups_stream_closer) + StreamHandle { + start: Some(rx), + handle, + } } } #[cfg(test)] mod tests { + use crate::utils::test::{Delivery, TestClient}; use crate::{ builder::ClientBuilder, groups::GroupMetadataOptions, storage::group_message::StoredGroupMessage, Client, }; use futures::StreamExt; - use std::sync::{Arc, Mutex}; - use xmtp_api_grpc::grpc_api_helper::Client as GrpcClient; + use parking_lot::Mutex; + use std::sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }; use xmtp_cryptography::utils::generate_local_wallet; - #[tokio::test(flavor = "multi_thread", worker_threads = 10)] + #[tokio::test(flavor = "current_thread")] async fn test_stream_welcomes() { - let alice = ClientBuilder::new_test_client(&generate_local_wallet()).await; - let bob = ClientBuilder::new_test_client(&generate_local_wallet()).await; - + let alice = Arc::new(ClientBuilder::new_test_client(&generate_local_wallet()).await); + let bob = Arc::new(ClientBuilder::new_test_client(&generate_local_wallet()).await); let alice_bob_group = alice .create_group(None, GroupMetadataOptions::default()) .unwrap(); - let mut bob_stream = bob.stream_conversations().await.unwrap(); + // FIXME:insipx we run into an issue where the reqwest::post().send() request + // blocks the executor and we cannot progress the runtime if we dont `tokio::spawn` this. + // A solution might be to use `hyper` instead, and implement a custom connection pool with + // `deadpool`. This is a bit more work but shouldn't be too complicated since + // we're only using `post` requests. It would be nice for all streams to work + // w/o spawning a separate task. + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + let mut stream = tokio_stream::wrappers::UnboundedReceiverStream::new(rx); + let bob_ptr = bob.clone(); + tokio::spawn(async move { + let bob_stream = bob_ptr.stream_conversations().await.unwrap(); + futures::pin_mut!(bob_stream); + while let Some(item) = bob_stream.next().await { + let _ = tx.send(item); + } + }); + + let group_id = alice_bob_group.group_id.clone(); alice_bob_group .add_members_by_inbox_id(&alice, vec![bob.inbox_id()]) .await .unwrap(); - let bob_received_groups = bob_stream.next().await.unwrap(); - assert_eq!(bob_received_groups.group_id, alice_bob_group.group_id); + let bob_received_groups = stream.next().await.unwrap(); + assert_eq!(bob_received_groups.group_id, group_id); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 10)] + async fn test_stream_messages() { + let alice = Arc::new(ClientBuilder::new_test_client(&generate_local_wallet()).await); + let bob = ClientBuilder::new_test_client(&generate_local_wallet()).await; + + let alice_group = alice + .create_group(None, GroupMetadataOptions::default()) + .unwrap(); + + // let mut bob_stream = bob.stream_conversations().await.unwrap(); + alice_group + .add_members_by_inbox_id(&alice, vec![bob.inbox_id()]) + .await + .unwrap(); + let bob_group = bob.sync_welcomes().await.unwrap(); + let bob_group = bob_group.first().unwrap(); + + let notify = Delivery::new(None); + let notify_ptr = notify.clone(); + let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); + tokio::spawn(async move { + let stream = alice_group.stream(&alice).await.unwrap(); + futures::pin_mut!(stream); + while let Some(item) = stream.next().await { + let _ = tx.send(item); + notify_ptr.notify_one(); + } + }); + let mut stream = tokio_stream::wrappers::UnboundedReceiverStream::new(rx); + + bob_group.send_message(b"hello", &bob).await.unwrap(); + notify.wait_for_delivery().await.unwrap(); + let message = stream.next().await.unwrap(); + assert_eq!(message.decrypted_message_bytes, b"hello"); + + bob_group.send_message(b"hello2", &bob).await.unwrap(); + notify.wait_for_delivery().await.unwrap(); + let message = stream.next().await.unwrap(); + assert_eq!(message.decrypted_message_bytes, b"hello2"); + + // assert_eq!(bob_received_groups.group_id, alice_bob_group.group_id); } #[tokio::test(flavor = "multi_thread", worker_threads = 10)] @@ -388,47 +509,52 @@ mod tests { let messages: Arc>> = Arc::new(Mutex::new(Vec::new())); let messages_clone = messages.clone(); - let stream = Client::::stream_all_messages_with_callback( + + let notify = Delivery::new(None); + let notify_pointer = notify.clone(); + let mut handle = Client::::stream_all_messages_with_callback( Arc::new(caro), move |message| { - (*messages_clone.lock().unwrap()).push(message); + (*messages_clone.lock()).push(message); + notify_pointer.notify_one(); }, - ) - .await - .unwrap(); - - tokio::time::sleep(std::time::Duration::from_millis(50)).await; + ); + handle.wait_for_ready().await; alix_group .send_message("first".as_bytes(), &alix) .await .unwrap(); + notify + .wait_for_delivery() + .await + .expect("didn't get `first`"); bo_group .send_message("second".as_bytes(), &bo) .await .unwrap(); + notify.wait_for_delivery().await.unwrap(); alix_group .send_message("third".as_bytes(), &alix) .await .unwrap(); + notify.wait_for_delivery().await.unwrap(); bo_group .send_message("fourth".as_bytes(), &bo) .await .unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(200)).await; + notify.wait_for_delivery().await.unwrap(); - let messages = messages.lock().unwrap(); - assert_eq!(messages[0].decrypted_message_bytes, "first".as_bytes()); - assert_eq!(messages[1].decrypted_message_bytes, "second".as_bytes()); - assert_eq!(messages[2].decrypted_message_bytes, "third".as_bytes()); - assert_eq!(messages[3].decrypted_message_bytes, "fourth".as_bytes()); - - stream.end(); + let messages = messages.lock(); + assert_eq!(messages[0].decrypted_message_bytes, b"first"); + assert_eq!(messages[1].decrypted_message_bytes, b"second"); + assert_eq!(messages[2].decrypted_message_bytes, b"third"); + assert_eq!(messages[3].decrypted_message_bytes, b"fourth"); } #[tokio::test(flavor = "multi_thread", worker_threads = 10)] async fn test_stream_all_messages_changing_group_list() { - let alix = ClientBuilder::new_test_client(&generate_local_wallet()).await; + let alix = Arc::new(ClientBuilder::new_test_client(&generate_local_wallet()).await); let bo = ClientBuilder::new_test_client(&generate_local_wallet()).await; let caro = Arc::new(ClientBuilder::new_test_client(&generate_local_wallet()).await); @@ -440,27 +566,25 @@ mod tests { .await .unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(100)).await; - let messages: Arc>> = Arc::new(Mutex::new(Vec::new())); let messages_clone = messages.clone(); - let stream = - Client::::stream_all_messages_with_callback(caro.clone(), move |message| { - let text = String::from_utf8(message.decrypted_message_bytes.clone()) - .unwrap_or("".to_string()); - println!("Received: {}", text); - (*messages_clone.lock().unwrap()).push(message); - }) - .await - .unwrap(); - - tokio::time::sleep(std::time::Duration::from_millis(50)).await; + let delivery = Delivery::new(None); + let delivery_pointer = delivery.clone(); + let mut handle = + Client::::stream_all_messages_with_callback(caro.clone(), move |message| { + delivery_pointer.notify_one(); + (*messages_clone.lock()).push(message); + }); + handle.wait_for_ready().await; alix_group .send_message("first".as_bytes(), &alix) .await .unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(100)).await; + delivery + .wait_for_delivery() + .await + .expect("timed out waiting for `first`"); let bo_group = bo .create_group(None, GroupMetadataOptions::default()) @@ -469,19 +593,24 @@ mod tests { .add_members_by_inbox_id(&bo, vec![caro.inbox_id()]) .await .unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(300)).await; bo_group .send_message("second".as_bytes(), &bo) .await .unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(100)).await; + delivery + .wait_for_delivery() + .await + .expect("timed out waiting for `second`"); alix_group .send_message("third".as_bytes(), &alix) .await .unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(100)).await; + delivery + .wait_for_delivery() + .await + .expect("timed out waiting for `third`"); let alix_group_2 = alix .create_group(None, GroupMetadataOptions::default()) @@ -490,36 +619,156 @@ mod tests { .add_members_by_inbox_id(&alix, vec![caro.inbox_id()]) .await .unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(300)).await; alix_group .send_message("fourth".as_bytes(), &alix) .await .unwrap(); - tokio::time::sleep(std::time::Duration::from_millis(100)).await; + delivery + .wait_for_delivery() + .await + .expect("timed out waiting for `fourth`"); + alix_group_2 .send_message("fifth".as_bytes(), &alix) .await .unwrap(); - - tokio::time::sleep(std::time::Duration::from_millis(100)).await; + delivery + .wait_for_delivery() + .await + .expect("timed out waiting for `fifth`"); { - let messages = messages.lock().unwrap(); + let messages = messages.lock(); assert_eq!(messages.len(), 5); } - stream.end(); - tokio::time::sleep(std::time::Duration::from_millis(50)).await; - assert!(stream.is_closed()); + let a = handle.handle.abort_handle(); + a.abort(); + let _ = handle.handle.await; + assert!(a.is_finished()); alix_group - .send_message("first".as_bytes(), &alix) + .send_message("should not show up".as_bytes(), &alix) .await .unwrap(); tokio::time::sleep(std::time::Duration::from_millis(100)).await; - let messages = messages.lock().unwrap(); + let messages = messages.lock(); assert_eq!(messages.len(), 5); } + + #[ignore] + #[tokio::test(flavor = "multi_thread", worker_threads = 10)] + async fn test_stream_all_messages_does_not_lose_messages() { + let alix = Arc::new(ClientBuilder::new_test_client(&generate_local_wallet()).await); + let caro = Arc::new(ClientBuilder::new_test_client(&generate_local_wallet()).await); + + let alix_group = alix + .create_group(None, GroupMetadataOptions::default()) + .unwrap(); + alix_group + .add_members_by_inbox_id(&alix, vec![caro.inbox_id()]) + .await + .unwrap(); + + let messages: Arc>> = Arc::new(Mutex::new(Vec::new())); + let messages_clone = messages.clone(); + + let blocked = Arc::new(AtomicU64::new(55)); + + let blocked_pointer = blocked.clone(); + let mut handle = + Client::::stream_all_messages_with_callback(caro.clone(), move |message| { + (*messages_clone.lock()).push(message); + blocked_pointer.fetch_sub(1, Ordering::SeqCst); + }); + handle.wait_for_ready().await; + + let alix_group_pointer = alix_group.clone(); + let alix_pointer = alix.clone(); + tokio::spawn(async move { + for _ in 0..50 { + alix_group_pointer + .send_message(b"spam", &alix_pointer) + .await + .unwrap(); + tokio::time::sleep(std::time::Duration::from_micros(200)).await; + } + }); + + for _ in 0..5 { + let new_group = alix + .create_group(None, GroupMetadataOptions::default()) + .unwrap(); + new_group + .add_members_by_inbox_id(&alix, vec![caro.inbox_id()]) + .await + .unwrap(); + new_group + .send_message(b"spam from new group", &alix) + .await + .unwrap(); + } + + let _ = tokio::time::timeout(std::time::Duration::from_secs(60), async { + while blocked.load(Ordering::SeqCst) > 0 { + tokio::task::yield_now().await; + } + }) + .await; + + let missed_messages = blocked.load(Ordering::SeqCst); + if missed_messages > 0 { + println!("Missed {} Messages", missed_messages); + panic!("Test failed due to missed messages"); + } + } + + #[tokio::test(flavor = "multi_thread")] + async fn test_self_group_creation() { + let alix = Arc::new(ClientBuilder::new_test_client(&generate_local_wallet()).await); + let bo = Arc::new(ClientBuilder::new_test_client(&generate_local_wallet()).await); + + let groups = Arc::new(Mutex::new(Vec::new())); + let notify = Delivery::new(None); + let (notify_pointer, groups_pointer) = (notify.clone(), groups.clone()); + + let closer = + Client::::stream_conversations_with_callback(alix.clone(), move |g| { + let mut groups = groups_pointer.lock(); + groups.push(g); + notify_pointer.notify_one(); + }); + + alix.create_group(None, GroupMetadataOptions::default()) + .unwrap(); + + notify + .wait_for_delivery() + .await + .expect("Stream never received group"); + + { + let grps = groups.lock(); + assert_eq!(grps.len(), 1); + } + + let group = bo + .create_group(None, GroupMetadataOptions::default()) + .unwrap(); + group + .add_members_by_inbox_id(&bo, vec![alix.inbox_id()]) + .await + .unwrap(); + + notify.wait_for_delivery().await.unwrap(); + + { + let grps = groups.lock(); + assert_eq!(grps.len(), 2); + } + + closer.handle.abort(); + } } diff --git a/xmtp_mls/src/utils/bench.rs b/xmtp_mls/src/utils/bench.rs index 101f0ab6b..99f833cae 100644 --- a/xmtp_mls/src/utils/bench.rs +++ b/xmtp_mls/src/utils/bench.rs @@ -1,8 +1,9 @@ //! Utilities for xmtp_mls benchmarks //! Utilities mostly include pre-generating identities in order to save time when writing/testing //! benchmarks. -use crate::builder::ClientBuilder; -use ethers::signers::{LocalWallet, Signer}; +#![allow(clippy::unwrap_used)] + +use crate::{builder::ClientBuilder, Client}; use indicatif::{ProgressBar, ProgressStyle}; use once_cell::sync::OnceCell; use serde::{Deserialize, Serialize}; @@ -16,7 +17,8 @@ use tracing_subscriber::{ util::SubscriberInitExt, EnvFilter, }; -use xmtp_cryptography::utils::rng; +use xmtp_cryptography::utils::generate_local_wallet; +use xmtp_id::InboxOwner; use super::test::TestClient; @@ -124,13 +126,13 @@ impl Identity { } async fn create_identity(is_dev_network: bool) -> Identity { - let wallet = LocalWallet::new(&mut rng()); + let wallet = generate_local_wallet(); let client = if is_dev_network { ClientBuilder::new_dev_client(&wallet).await } else { ClientBuilder::new_test_client(&wallet).await }; - Identity::new(client.inbox_id(), format!("0x{:x}", wallet.address())) + Identity::new(client.inbox_id(), wallet.get_address()) } async fn create_identities(n: usize, is_dev_network: bool) -> Vec { @@ -174,12 +176,12 @@ async fn create_identities(n: usize, is_dev_network: bool) -> Vec { /// node still has those identities. pub async fn create_identities_if_dont_exist( identities: usize, - client: &TestClient, + client: &Client, is_dev_network: bool, ) -> Vec { match load_identities(is_dev_network) { Ok(identities) => { - log::info!( + tracing::info!( "Found generated identities at {}, checking for existence on backend...", file_path(is_dev_network) ); @@ -193,7 +195,7 @@ pub async fn create_identities_if_dont_exist( _ => (), } - log::info!( + tracing::info!( "Could not find any identitites to load, creating new identitites \n Beware, this fills $TMPDIR with ~10GBs of identities" ); diff --git a/xmtp_mls/src/utils/test.rs b/xmtp_mls/src/utils/test.rs old mode 100644 new mode 100755 index cffc96d67..529191155 --- a/xmtp_mls/src/utils/test.rs +++ b/xmtp_mls/src/utils/test.rs @@ -1,21 +1,35 @@ +#![allow(clippy::unwrap_used)] use std::env; use rand::{ distributions::{Alphanumeric, DistString}, - Rng, + Rng, RngCore, }; +use std::sync::Arc; +use tokio::{sync::Notify, time::error::Elapsed}; use xmtp_api_grpc::grpc_api_helper::Client as GrpcClient; -use xmtp_id::associations::{generate_inbox_id, RecoverableEcdsaSignature}; +use xmtp_id::associations::{ + generate_inbox_id, + test_utils::MockSmartContractSignatureVerifier, + unverified::{UnverifiedRecoverableEcdsaSignature, UnverifiedSignature}, +}; use crate::{ builder::ClientBuilder, identity::IdentityStrategy, - storage::{EncryptedMessageStore, StorageOption}, + storage::{EncryptedConnection, EncryptedMessageStore, EncryptionKey, StorageOption}, types::Address, - Client, InboxOwner, + Client, InboxOwner, XmtpApi, XmtpTestClient, }; -pub type TestClient = Client; +#[cfg(feature = "http-api")] +use xmtp_api_http::XmtpHttpApiClient; + +#[cfg(not(feature = "http-api"))] +pub type TestClient = GrpcClient; + +#[cfg(feature = "http-api")] +pub type TestClient = XmtpHttpApiClient; pub fn rand_string() -> String { Alphanumeric.sample_string(&mut rand::thread_rng(), 24) @@ -39,46 +53,76 @@ pub fn rand_time() -> i64 { rng.gen_range(0..1_000_000_000) } -/// Get a GRPC Client pointed at the local instance of `xmtp-node-go` -pub async fn get_local_grpc_client() -> GrpcClient { - GrpcClient::create("http://localhost:5556".to_string(), false) - .await - .unwrap() +#[cfg(feature = "http-api")] +impl XmtpTestClient for XmtpHttpApiClient { + async fn create_local() -> Self { + XmtpHttpApiClient::new("http://localhost:5555".into()).unwrap() + } + + async fn create_dev() -> Self { + XmtpHttpApiClient::new("https://grpc.dev.xmtp.network:443".into()).unwrap() + } } -pub async fn get_dev_grpc_client() -> GrpcClient { - GrpcClient::create("https://grpc.dev.xmtp.network:443".into(), true) - .await - .unwrap() +impl XmtpTestClient for GrpcClient { + async fn create_local() -> Self { + GrpcClient::create("http://localhost:5556".into(), false) + .await + .unwrap() + } + + async fn create_dev() -> Self { + GrpcClient::create("https://grpc.dev.xmtp.network:443".into(), false) + .await + .unwrap() + } } -impl ClientBuilder { - pub async fn local_grpc(self) -> Self { - self.api_client(get_local_grpc_client().await) +impl EncryptedMessageStore { + pub fn generate_enc_key() -> EncryptionKey { + let mut key = [0u8; 32]; + xmtp_cryptography::utils::rng().fill_bytes(&mut key[..]); + key } - pub async fn dev_grpc(self) -> Self { - self.api_client(get_dev_grpc_client().await) + pub fn remove_db_files>(path: P) { + let path = path.as_ref(); + std::fs::remove_file(path).unwrap(); + std::fs::remove_file(EncryptedConnection::salt_file(path).unwrap()).unwrap(); } +} +impl ClientBuilder { pub fn temp_store(self) -> Self { let tmpdb = tmp_path(); self.store( - EncryptedMessageStore::new_unencrypted(StorageOption::Persistent(tmpdb)).unwrap(), + EncryptedMessageStore::new( + StorageOption::Persistent(tmpdb), + EncryptedMessageStore::generate_enc_key(), + ) + .unwrap(), ) } - pub async fn new_test_client(owner: &impl InboxOwner) -> Client { + pub async fn local_client(mut self) -> Self { + let local_client = ::create_local().await; + self = self.api_client(local_client); + self + } + + pub async fn new_test_client(owner: &impl InboxOwner) -> Client { let nonce = 1; let inbox_id = generate_inbox_id(&owner.get_address(), &nonce); + let client = Self::new(IdentityStrategy::CreateIfNotFound( inbox_id, owner.get_address(), nonce, None, )) + .scw_signatuer_verifier(MockSmartContractSignatureVerifier::new(true)) .temp_store() - .local_grpc() + .local_client() .await .build() .await @@ -89,9 +133,11 @@ impl ClientBuilder { client } - pub async fn new_dev_client(owner: &impl InboxOwner) -> Client { + pub async fn new_dev_client(owner: &impl InboxOwner) -> Client { let nonce = 1; let inbox_id = generate_inbox_id(&owner.get_address(), &nonce); + let dev_client = ::create_dev().await; + let client = Self::new(IdentityStrategy::CreateIfNotFound( inbox_id, owner.get_address(), @@ -99,8 +145,7 @@ impl ClientBuilder { None, )) .temp_store() - .dev_grpc() - .await + .api_client(dev_client) .build() .await .unwrap(); @@ -111,7 +156,32 @@ impl ClientBuilder { } } -impl Client { +/// wrapper over a `Notify` with a 60-scond timeout for waiting +#[derive(Clone, Default)] +pub struct Delivery { + notify: Arc, + timeout: std::time::Duration, +} + +impl Delivery { + pub fn new(timeout: Option) -> Self { + let timeout = timeout.unwrap_or(std::time::Duration::from_secs(60)); + Self { + notify: Arc::new(Notify::new()), + timeout, + } + } + + pub async fn wait_for_delivery(&self) -> Result<(), Elapsed> { + tokio::time::timeout(self.timeout, async { self.notify.notified().await }).await + } + + pub fn notify_one(&self) { + self.notify.notify_one() + } +} + +impl Client { pub async fn is_registered(&self, address: &String) -> bool { let ids = self .api_client @@ -122,14 +192,17 @@ impl Client { } } -pub async fn register_client(client: &Client, owner: &impl InboxOwner) { +pub async fn register_client(client: &Client, owner: &impl InboxOwner) { let mut signature_request = client.context.signature_request().unwrap(); let signature_text = signature_request.signature_text(); + let unverified_signature = UnverifiedSignature::RecoverableEcdsa( + UnverifiedRecoverableEcdsaSignature::new(owner.sign(&signature_text).unwrap().into()), + ); signature_request - .add_signature(Box::new(RecoverableEcdsaSignature::new( - signature_text.clone(), - owner.sign(&signature_text).unwrap().into(), - ))) + .add_signature( + unverified_signature, + client.smart_contract_signature_verifier().as_ref(), + ) .await .unwrap(); diff --git a/xmtp_mls/src/verified_key_package.rs b/xmtp_mls/src/verified_key_package.rs deleted file mode 100644 index 83b7f89bd..000000000 --- a/xmtp_mls/src/verified_key_package.rs +++ /dev/null @@ -1,121 +0,0 @@ -use openmls::{ - credentials::{errors::BasicCredentialError, BasicCredential}, - prelude::{ - tls_codec::{Deserialize, Error as TlsCodecError}, - KeyPackage, KeyPackageIn, KeyPackageVerifyError, - }, -}; - -use openmls_rust_crypto::RustCrypto; -use thiserror::Error; - -use crate::{ - configuration::MLS_PROTOCOL_VERSION, - credential::{get_validated_account_address, AssociationError}, - identity::IdentityError, - types::Address, -}; - -#[derive(Debug, Error)] -pub enum KeyPackageVerificationError { - #[error("TLS Codec error: {0}")] - TlsError(#[from] TlsCodecError), - #[error("mls validation: {0}")] - MlsValidation(#[from] KeyPackageVerifyError), - #[error("identity: {0}")] - Identity(#[from] IdentityError), - #[error("invalid application id")] - InvalidApplicationId, - #[error("application id ({0}) does not match the credential address ({1}).")] - ApplicationIdCredentialMismatch(String, String), - #[error("invalid credential")] - InvalidCredential, - #[error(transparent)] - Association(#[from] AssociationError), - #[error("invalid lifetime")] - InvalidLifetime, - #[error("generic: {0}")] - Generic(String), - #[error("wrong credential type")] - WrongCredentialType(#[from] BasicCredentialError), -} - -#[derive(Debug, Clone, PartialEq)] -pub struct VerifiedKeyPackage { - pub inner: KeyPackage, - pub account_address: String, -} - -impl VerifiedKeyPackage { - pub fn new(inner: KeyPackage, account_address: String) -> Self { - Self { - inner, - account_address, - } - } - - /// Validates starting with a KeyPackage (which is already validated by OpenMLS) - pub fn from_key_package(kp: KeyPackage) -> Result { - let leaf_node = kp.leaf_node(); - let basic_credential = BasicCredential::try_from(leaf_node.credential().clone())?; - let pub_key_bytes = leaf_node.signature_key().as_slice(); - let account_address = - identity_to_account_address(basic_credential.identity(), pub_key_bytes)?; - let application_id = extract_application_id(&kp)?; - if !account_address.eq(&application_id) { - return Err( - KeyPackageVerificationError::ApplicationIdCredentialMismatch( - application_id, - account_address, - ), - ); - } - if !kp.life_time().is_valid() { - return Err(KeyPackageVerificationError::InvalidLifetime); - } - - Ok(Self::new(kp, account_address)) - } - - // Validates starting with a KeyPackageIn as bytes (which is not validated by OpenMLS) - pub fn from_bytes( - crypto_provider: &RustCrypto, - data: &[u8], - ) -> Result { - let kp_in: KeyPackageIn = KeyPackageIn::tls_deserialize_exact(data)?; - let kp = kp_in.validate(crypto_provider, MLS_PROTOCOL_VERSION)?; - - Self::from_key_package(kp) - } - - pub fn installation_id(&self) -> Vec { - self.inner.leaf_node().signature_key().as_slice().to_vec() - } - - pub fn hpke_init_key(&self) -> Vec { - self.inner.hpke_init_key().as_slice().to_vec() - } -} - -fn identity_to_account_address( - credential_bytes: &[u8], - installation_key_bytes: &[u8], -) -> Result { - Ok(get_validated_account_address( - credential_bytes, - installation_key_bytes, - )?) -} - -fn extract_application_id(kp: &KeyPackage) -> Result { - let application_id_bytes = kp - .leaf_node() - .extensions() - .application_id() - .ok_or_else(|| KeyPackageVerificationError::InvalidApplicationId)? - .as_slice() - .to_vec(); - - String::from_utf8(application_id_bytes) - .map_err(|_| KeyPackageVerificationError::InvalidApplicationId) -} diff --git a/xmtp_mls/src/verified_key_package_v2.rs b/xmtp_mls/src/verified_key_package_v2.rs index b3cfc63a2..5dd5ecef3 100644 --- a/xmtp_mls/src/verified_key_package_v2.rs +++ b/xmtp_mls/src/verified_key_package_v2.rs @@ -18,8 +18,6 @@ pub enum KeyPackageVerificationError { TlsError(#[from] TlsCodecError), #[error("mls validation: {0}")] MlsValidation(#[from] KeyPackageVerifyError), - #[error("invalid lifetime")] - InvalidLifetime, #[error("wrong credential type")] WrongCredentialType(#[from] BasicCredentialError), #[error(transparent)] @@ -75,10 +73,6 @@ impl TryFrom for VerifiedKeyPackageV2 { let pub_key_bytes = leaf_node.signature_key().as_slice().to_vec(); let credential = MlsCredential::decode(basic_credential.identity())?; - if !kp.life_time().is_valid() { - return Err(KeyPackageVerificationError::InvalidLifetime); - } - Ok(Self::new(kp, credential, pub_key_bytes)) } } diff --git a/xmtp_mls/src/xmtp_openmls_provider.rs b/xmtp_mls/src/xmtp_openmls_provider.rs index dcc93ee1f..b3e7b36a8 100644 --- a/xmtp_mls/src/xmtp_openmls_provider.rs +++ b/xmtp_mls/src/xmtp_openmls_provider.rs @@ -9,15 +9,6 @@ pub struct XmtpOpenMlsProvider { key_store: SqlKeyStore, } -impl Clone for XmtpOpenMlsProvider { - fn clone(&self) -> Self { - Self { - crypto: RustCrypto::default(), - key_store: self.key_store.clone(), - } - } -} - impl XmtpOpenMlsProvider { pub fn new(conn: DbConnection) -> Self { Self { @@ -26,10 +17,6 @@ impl XmtpOpenMlsProvider { } } - pub(crate) fn conn(&self) -> DbConnection { - self.key_store.conn() - } - pub(crate) fn conn_ref(&self) -> &DbConnection { self.key_store.conn_ref() } diff --git a/xmtp_proto/Cargo.toml b/xmtp_proto/Cargo.toml index 506479cea..9bfffc877 100644 --- a/xmtp_proto/Cargo.toml +++ b/xmtp_proto/Cargo.toml @@ -1,38 +1,40 @@ [package] -name = "xmtp_proto" -version = "0.1.0" edition = "2021" +name = "xmtp_proto" +version.workspace = true [dependencies] -async-trait = { workspace = true } futures = { workspace = true } -futures-core = { workspace = true } -pbjson = "0.6.0" -pbjson-types = "0.6.0" +pbjson-types.workspace = true +pbjson.workspace = true prost = { workspace = true, features = ["prost-derive"] } # Only necessary if using Protobuf well-known types: prost-types = { workspace = true } serde = { workspace = true } -tonic = { workspace = true, optional = true } -openmls_basic_credential = { workspace = true, optional = true } openmls = { workspace = true, optional = true } +trait-variant = "0.1.2" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +tonic = { workspace = true } + [features] +convert = ["openmls", "proto_full"] default = [] -convert = ["openmls_basic_credential", "openmls", "proto_full"] -grpc = ["tonic"] + # @@protoc_deletion_point(features) # This section is automatically generated by protoc-gen-prost-crate. # Changes in this area may be lost on regeneration. -proto_full = ["xmtp-identity","xmtp-identity-api-v1","xmtp-identity-associations","xmtp-keystore_api-v1","xmtp-message_api-v1","xmtp-message_contents","xmtp-mls-api-v1","xmtp-mls-database","xmtp-mls-message_contents","xmtp-mls_validation-v1"] +proto_full = ["xmtp-identity","xmtp-identity-api-v1","xmtp-identity-associations","xmtp-keystore_api-v1","xmtp-message_api-v1","xmtp-message_contents","xmtp-mls-api-v1","xmtp-mls-database","xmtp-mls-message_contents","xmtp-mls_validation-v1","xmtp-xmtpv4"] "xmtp-identity" = [] "xmtp-identity-api-v1" = ["xmtp-identity","xmtp-identity-associations"] "xmtp-identity-associations" = ["xmtp-identity","xmtp-message_contents"] "xmtp-keystore_api-v1" = ["xmtp-message_contents"] "xmtp-message_api-v1" = ["xmtp-message_contents"] "xmtp-message_contents" = [] -"xmtp-mls-api-v1" = ["xmtp-message_contents"] +"xmtp-mls-api-v1" = [] "xmtp-mls-database" = [] -"xmtp-mls-message_contents" = ["xmtp-message_contents"] +"xmtp-mls-message_contents" = [] "xmtp-mls_validation-v1" = ["xmtp-identity-associations"] +"xmtp-xmtpv4" = ["xmtp-identity-associations","xmtp-mls-api-v1"] ## @@protoc_insertion_point(features) \ No newline at end of file diff --git a/xmtp_proto/buf.gen.yaml b/xmtp_proto/buf.gen.yaml index aa5ea1dae..4ca89dd90 100644 --- a/xmtp_proto/buf.gen.yaml +++ b/xmtp_proto/buf.gen.yaml @@ -2,29 +2,31 @@ version: v1 managed: enabled: true plugins: - - plugin: buf.build/community/neoeinstein-prost:v0.3.1 + - plugin: buf.build/community/neoeinstein-prost:v0.4.0 out: src/gen opt: - compile_well_known_types - extern_path=.google.protobuf=::pbjson_types - file_descriptor_set - - plugin: buf.build/community/neoeinstein-prost-serde:v0.3.0 + - plugin: buf.build/community/neoeinstein-prost-serde:v0.3.1 out: src/gen - - plugin: buf.build/community/neoeinstein-tonic:v0.4.0 + - plugin: buf.build/community/neoeinstein-tonic:v0.4.1 out: src/gen opt: - no_server=false - compile_well_known_types - extern_path=.google.protobuf=::pbjson_types # Exclude it from non-tonic builds (so we can use the rest in Wasm) - - client_mod_attribute=xmtp.identity.api.v1=#[cfg(feature = "tonic")] - - client_mod_attribute=xmtp.message_api.v1=#[cfg(feature = "tonic")] - - client_mod_attribute=xmtp.mls.api.v1=#[cfg(feature = "tonic")] - - client_mod_attribute=xmtp.mls_validation.v1=#[cfg(feature = "tonic")] - - server_mod_attribute=xmtp.identity.api.v1=#[cfg(feature = "tonic")] - - server_mod_attribute=xmtp.mls_validation.v1=#[cfg(feature = "tonic")] - - server_mod_attribute=xmtp.message_api.v1=#[cfg(feature = "tonic")] - - server_mod_attribute=xmtp.mls.api.v1=#[cfg(feature = "tonic")] + - client_mod_attribute=xmtp.identity.api.v1=#[cfg(not(target_arch = "wasm32"))] + - client_mod_attribute=xmtp.message_api.v1=#[cfg(not(target_arch = "wasm32"))] + - client_mod_attribute=xmtp.mls.api.v1=#[cfg(not(target_arch = "wasm32"))] + - client_mod_attribute=xmtp.mls_validation.v1=#[cfg(not(target_arch = "wasm32"))] + - client_mod_attribute=xmtp.xmtpv4=#[cfg(not(target_arch = "wasm32"))] + - server_mod_attribute=xmtp.identity.api.v1=#[cfg(not(target_arch = "wasm32"))] + - server_mod_attribute=xmtp.mls_validation.v1=#[cfg(not(target_arch = "wasm32"))] + - server_mod_attribute=xmtp.message_api.v1=#[cfg(not(target_arch = "wasm32"))] + - server_mod_attribute=xmtp.mls.api.v1=#[cfg(not(target_arch = "wasm32"))] + - server_mod_attribute=xmtp.xmtpv4=#[cfg(not(target_arch = "wasm32"))] - name: prost-crate out: . strategy: all diff --git a/xmtp_proto/src/api_client.rs b/xmtp_proto/src/api_client.rs index 418da0a9c..60db8e4e9 100644 --- a/xmtp_proto/src/api_client.rs +++ b/xmtp_proto/src/api_client.rs @@ -1,6 +1,5 @@ -use std::{error::Error as StdError, fmt, pin::Pin}; +use std::{error::Error as StdError, fmt}; -use async_trait::async_trait; use futures::Stream; pub use super::xmtp::message_api::v1::{ @@ -13,12 +12,10 @@ use crate::xmtp::identity::api::v1::{ GetInboxIdsResponse, PublishIdentityUpdateRequest, PublishIdentityUpdateResponse, }; use crate::xmtp::mls::api::v1::{ - FetchKeyPackagesRequest, FetchKeyPackagesResponse, GetIdentityUpdatesRequest, - GetIdentityUpdatesResponse, GroupMessage, QueryGroupMessagesRequest, + FetchKeyPackagesRequest, FetchKeyPackagesResponse, GroupMessage, QueryGroupMessagesRequest, QueryGroupMessagesResponse, QueryWelcomeMessagesRequest, QueryWelcomeMessagesResponse, - RegisterInstallationRequest, RegisterInstallationResponse, SendGroupMessagesRequest, - SendWelcomeMessagesRequest, SubscribeGroupMessagesRequest, SubscribeWelcomeMessagesRequest, - UploadKeyPackageRequest, WelcomeMessage, + SendGroupMessagesRequest, SendWelcomeMessagesRequest, SubscribeGroupMessagesRequest, + SubscribeWelcomeMessagesRequest, UploadKeyPackageRequest, WelcomeMessage, }; #[derive(Debug)] @@ -33,6 +30,7 @@ pub enum ErrorKind { MlsError, IdentityError, SubscriptionUpdateError, + MetadataError, } type ErrorSource = Box; @@ -80,6 +78,7 @@ impl fmt::Display for Error { ErrorKind::IdentityError => "identity error", ErrorKind::MlsError => "mls error", ErrorKind::SubscriptionUpdateError => "subscription update error", + ErrorKind::MetadataError => "metadata error", })?; if self.source().is_some() { f.write_str(": ")?; @@ -103,22 +102,31 @@ pub trait XmtpApiSubscription { fn close_stream(&mut self); } -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +#[allow(async_fn_in_trait)] pub trait MutableApiSubscription: Stream> + Send { async fn update(&mut self, req: SubscribeRequest) -> Result<(), Error>; fn close(&self); } +pub trait ClientWithMetadata { + fn set_libxmtp_version(&mut self, version: String) -> Result<(), Error>; + fn set_app_version(&mut self, version: String) -> Result<(), Error>; +} + +/// Global Marker trait for WebAssembly +#[cfg(target_arch = "wasm32")] +pub trait Wasm {} +#[cfg(target_arch = "wasm32")] +impl Wasm for T {} + // Wasm futures don't have `Send` or `Sync` bounds. -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -pub trait XmtpApiClient: Send + Sync { +#[allow(async_fn_in_trait)] +#[cfg_attr(not(target_arch = "wasm32"), trait_variant::make(XmtpApiClient: Send))] +#[cfg_attr(target_arch = "wasm32", trait_variant::make(XmtpApiClient: Wasm))] +pub trait LocalXmtpApiClient { type Subscription: XmtpApiSubscription; type MutableSubscription: MutableApiSubscription; - fn set_app_version(&mut self, version: String); - async fn publish( &self, token: String, @@ -137,17 +145,11 @@ pub trait XmtpApiClient: Send + Sync { async fn batch_query(&self, request: BatchQueryRequest) -> Result; } -pub type GroupMessageStream = Pin> + Send>>; -pub type WelcomeMessageStream = Pin> + Send>>; - // Wasm futures don't have `Send` or `Sync` bounds. -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -pub trait XmtpMlsClient: Send + Sync + 'static { - async fn register_installation( - &self, - request: RegisterInstallationRequest, - ) -> Result; +#[allow(async_fn_in_trait)] +#[cfg_attr(not(target_arch = "wasm32"), trait_variant::make(XmtpMlsClient: Send))] +#[cfg_attr(target_arch = "wasm32", trait_variant::make(XmtpMlsClient: Wasm))] +pub trait LocalXmtpMlsClient { async fn upload_key_package(&self, request: UploadKeyPackageRequest) -> Result<(), Error>; async fn fetch_key_packages( &self, @@ -156,10 +158,6 @@ pub trait XmtpMlsClient: Send + Sync + 'static { async fn send_group_messages(&self, request: SendGroupMessagesRequest) -> Result<(), Error>; async fn send_welcome_messages(&self, request: SendWelcomeMessagesRequest) -> Result<(), Error>; - async fn get_identity_updates( - &self, - request: GetIdentityUpdatesRequest, - ) -> Result; async fn query_group_messages( &self, request: QueryGroupMessagesRequest, @@ -168,20 +166,57 @@ pub trait XmtpMlsClient: Send + Sync + 'static { &self, request: QueryWelcomeMessagesRequest, ) -> Result; +} + +#[allow(async_fn_in_trait)] +#[cfg_attr(target_arch = "wasm32", trait_variant::make(XmtpMlsStreams: Wasm))] +pub trait LocalXmtpMlsStreams { + type GroupMessageStream<'a>: Stream> + 'a + where + Self: 'a; + + type WelcomeMessageStream<'a>: Stream> + 'a + where + Self: 'a; + async fn subscribe_group_messages( &self, request: SubscribeGroupMessagesRequest, - ) -> Result; + ) -> Result, Error>; async fn subscribe_welcome_messages( &self, request: SubscribeWelcomeMessagesRequest, - ) -> Result; + ) -> Result, Error>; } -// Wasm futures don't have `Send` or `Sync` bounds. -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -pub trait XmtpIdentityClient: Send + Sync + 'static { +// we manually make a Local+Non-Local trait variant here b/c the +// macro breaks with GATs +#[allow(async_fn_in_trait)] +#[cfg(not(target_arch = "wasm32"))] +pub trait XmtpMlsStreams: Send { + type GroupMessageStream<'a>: Stream> + Send + 'a + where + Self: 'a; + + type WelcomeMessageStream<'a>: Stream> + Send + 'a + where + Self: 'a; + + fn subscribe_group_messages( + &self, + request: SubscribeGroupMessagesRequest, + ) -> impl futures::Future, Error>> + Send; + + fn subscribe_welcome_messages( + &self, + request: SubscribeWelcomeMessagesRequest, + ) -> impl futures::Future, Error>> + Send; +} + +#[allow(async_fn_in_trait)] +#[cfg_attr(not(target_arch = "wasm32"), trait_variant::make(XmtpIdentityClient: Send))] +#[cfg_attr(target_arch = "wasm32", trait_variant::make(XmtpIdentityClient: Wasm))] +pub trait LocalXmtpIdentityClient { async fn publish_identity_update( &self, request: PublishIdentityUpdateRequest, diff --git a/xmtp_proto/src/gen/mod.rs b/xmtp_proto/src/gen/mod.rs index e7556d3cb..47d0b137e 100644 --- a/xmtp_proto/src/gen/mod.rs +++ b/xmtp_proto/src/gen/mod.rs @@ -72,4 +72,10 @@ pub mod xmtp { // @@protoc_insertion_point(xmtp.mls_validation.v1) } } + #[cfg(feature = "xmtp-xmtpv4")] + // @@protoc_insertion_point(attribute:xmtp.xmtpv4) + pub mod xmtpv4 { + include!("xmtp.xmtpv4.rs"); + // @@protoc_insertion_point(xmtp.xmtpv4) + } } \ No newline at end of file diff --git a/xmtp_proto/src/gen/xmtp.identity.api.v1.rs b/xmtp_proto/src/gen/xmtp.identity.api.v1.rs index 5079157b9..0b77836b7 100644 --- a/xmtp_proto/src/gen/xmtp.identity.api.v1.rs +++ b/xmtp_proto/src/gen/xmtp.identity.api.v1.rs @@ -1,4 +1,5 @@ // @generated +// This file is @generated by prost-build. /// Publishes an identity update to the network #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -8,7 +9,7 @@ pub struct PublishIdentityUpdateRequest { } /// The response when an identity update is published #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct PublishIdentityUpdateResponse { } /// Get all updates for an identity since the specified time diff --git a/xmtp_proto/src/gen/xmtp.identity.api.v1.serde.rs b/xmtp_proto/src/gen/xmtp.identity.api.v1.serde.rs index 029e46b6a..ed2e88547 100644 --- a/xmtp_proto/src/gen/xmtp.identity.api.v1.serde.rs +++ b/xmtp_proto/src/gen/xmtp.identity.api.v1.serde.rs @@ -110,6 +110,7 @@ impl serde::Serialize for get_identity_updates_request::Request { } if self.sequence_id != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("sequenceId", ToString::to_string(&self.sequence_id).as_str())?; } struct_ser.end() @@ -314,10 +315,12 @@ impl serde::Serialize for get_identity_updates_response::IdentityUpdateLog { let mut struct_ser = serializer.serialize_struct("xmtp.identity.api.v1.GetIdentityUpdatesResponse.IdentityUpdateLog", len)?; if self.sequence_id != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("sequenceId", ToString::to_string(&self.sequence_id).as_str())?; } if self.server_timestamp_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("serverTimestampNs", ToString::to_string(&self.server_timestamp_ns).as_str())?; } if let Some(v) = self.update.as_ref() { diff --git a/xmtp_proto/src/gen/xmtp.identity.api.v1.tonic.rs b/xmtp_proto/src/gen/xmtp.identity.api.v1.tonic.rs index 90f53d301..52a54f8e5 100644 --- a/xmtp_proto/src/gen/xmtp.identity.api.v1.tonic.rs +++ b/xmtp_proto/src/gen/xmtp.identity.api.v1.tonic.rs @@ -1,6 +1,6 @@ // @generated /// Generated client implementations. -#[cfg(feature = "tonic")] +#[cfg(not(target_arch = "wasm32"))] pub mod identity_api_client { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] use tonic::codegen::*; @@ -175,7 +175,7 @@ pub mod identity_api_client { } } /// Generated server implementations. -#[cfg(feature = "tonic")] +#[cfg(not(target_arch = "wasm32"))] pub mod identity_api_server { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] use tonic::codegen::*; @@ -206,19 +206,17 @@ pub mod identity_api_server { } #[derive(Debug)] pub struct IdentityApiServer { - inner: _Inner, + inner: Arc, accept_compression_encodings: EnabledCompressionEncodings, send_compression_encodings: EnabledCompressionEncodings, max_decoding_message_size: Option, max_encoding_message_size: Option, } - struct _Inner(Arc); impl IdentityApiServer { pub fn new(inner: T) -> Self { Self::from_arc(Arc::new(inner)) } pub fn from_arc(inner: Arc) -> Self { - let inner = _Inner(inner); Self { inner, accept_compression_encodings: Default::default(), @@ -281,7 +279,6 @@ pub mod identity_api_server { Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { - let inner = self.inner.clone(); match req.uri().path() { "/xmtp.identity.api.v1.IdentityApi/PublishIdentityUpdate" => { #[allow(non_camel_case_types)] @@ -313,7 +310,6 @@ pub mod identity_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = PublishIdentityUpdateSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -360,7 +356,6 @@ pub mod identity_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = GetIdentityUpdatesSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -406,7 +401,6 @@ pub mod identity_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = GetInboxIdsSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -428,8 +422,11 @@ pub mod identity_api_server { Ok( http::Response::builder() .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") + .header("grpc-status", tonic::Code::Unimplemented as i32) + .header( + http::header::CONTENT_TYPE, + tonic::metadata::GRPC_CONTENT_TYPE, + ) .body(empty_body()) .unwrap(), ) @@ -450,16 +447,6 @@ pub mod identity_api_server { } } } - impl Clone for _Inner { - fn clone(&self) -> Self { - Self(Arc::clone(&self.0)) - } - } - impl std::fmt::Debug for _Inner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - } - } impl tonic::server::NamedService for IdentityApiServer { const NAME: &'static str = "xmtp.identity.api.v1.IdentityApi"; } diff --git a/xmtp_proto/src/gen/xmtp.identity.associations.rs b/xmtp_proto/src/gen/xmtp.identity.associations.rs index beb97ec02..f01f90fd9 100644 --- a/xmtp_proto/src/gen/xmtp.identity.associations.rs +++ b/xmtp_proto/src/gen/xmtp.identity.associations.rs @@ -1,4 +1,5 @@ // @generated +// This file is @generated by prost-build. /// RecoverableEcdsaSignature for EIP-191 and V2 signatures #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -108,6 +109,8 @@ pub struct Member { pub identifier: ::core::option::Option, #[prost(message, optional, tag="2")] pub added_by_entity: ::core::option::Option, + #[prost(uint64, optional, tag="3")] + pub client_timestamp_ns: ::core::option::Option, } /// The first entry of any XID log. The XID must be deterministically derivable /// from the address and nonce. @@ -444,7 +447,7 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[ 0x33, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x03, 0x06, 0x12, 0x03, 0x3b, 0x04, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x03, 0x01, 0x12, 0x03, 0x3b, 0x1d, 0x2e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x03, 0x03, 0x12, 0x03, 0x3b, 0x31, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, 0x0a, 0x88, 0x31, 0x0a, 0x27, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x74, 0x6f, 0x33, 0x0a, 0x9a, 0x32, 0x0a, 0x27, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1a, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, @@ -458,7 +461,7 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[ 0x6f, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x15, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x06, 0x0a, 0x04, - 0x6b, 0x69, 0x6e, 0x64, 0x22, 0xc5, 0x01, 0x0a, 0x06, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, + 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x92, 0x02, 0x0a, 0x06, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x4c, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, @@ -469,374 +472,383 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[ 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x64, 0x64, 0x65, 0x64, 0x42, 0x79, 0x45, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x61, 0x64, 0x64, - 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x22, 0xaf, 0x01, 0x0a, - 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x12, 0x27, 0x0a, 0x0f, - 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x61, 0x0a, 0x19, 0x69, - 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x13, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x11, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, + 0x10, 0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x22, 0xaf, 0x01, 0x0a, 0x0b, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x61, 0x0a, 0x19, 0x69, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, + 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, + 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x52, 0x17, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xae, 0x02, 0x0a, 0x0e, + 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x60, + 0x0a, 0x15, 0x6e, 0x65, 0x77, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, + 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, + 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x13, 0x6e, 0x65, 0x77, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x12, 0x61, 0x0a, 0x19, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x17, 0x65, 0x78, 0x69, 0x73, + 0x74, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x12, 0x57, 0x0a, 0x14, 0x6e, 0x65, 0x77, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x12, 0x6e, 0x65, 0x77, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xd0, 0x01, 0x0a, + 0x11, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x56, 0x0a, 0x10, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x5f, + 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, + 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x6d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x54, 0x6f, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x63, 0x0a, 0x1a, 0x72, 0x65, + 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x17, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xae, - 0x02, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x60, 0x0a, 0x15, 0x6e, 0x65, 0x77, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, - 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x13, - 0x6e, 0x65, 0x77, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x12, 0x61, 0x0a, 0x19, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, - 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x17, 0x65, - 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x57, 0x0a, 0x14, 0x6e, 0x65, 0x77, 0x5f, 0x6d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x18, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, + 0xbf, 0x01, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, + 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x65, 0x77, + 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6e, 0x65, 0x77, 0x52, 0x65, 0x63, 0x6f, + 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x74, 0x0a, 0x23, 0x65, + 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, + 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, + 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x22, 0xdc, 0x02, 0x0a, 0x0e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4c, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x69, + 0x6e, 0x62, 0x6f, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, + 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, + 0x62, 0x6f, 0x78, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, + 0x6f, 0x78, 0x12, 0x3e, 0x0a, 0x03, 0x61, 0x64, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, + 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x64, 0x64, + 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x03, 0x61, + 0x64, 0x64, 0x12, 0x47, 0x0a, 0x06, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x6b, 0x0a, 0x17, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, + 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, + 0x00, 0x52, 0x15, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, + 0x22, 0xa1, 0x01, 0x0a, 0x0e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x12, 0x44, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x12, 0x6e, 0x65, 0x77, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, - 0xd0, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x56, 0x0a, 0x10, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, - 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x73, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x62, + 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x6e, 0x62, + 0x6f, 0x78, 0x49, 0x64, 0x22, 0x85, 0x01, 0x0a, 0x09, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4d, + 0x61, 0x70, 0x12, 0x3e, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x6d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x54, 0x6f, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x63, 0x0a, - 0x1a, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x18, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, - 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x22, 0xbf, 0x01, 0x0a, 0x15, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, - 0x6f, 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x30, 0x0a, 0x14, - 0x6e, 0x65, 0x77, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6e, 0x65, 0x77, 0x52, - 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x74, - 0x0a, 0x23, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, - 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, - 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, - 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x52, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x63, 0x6f, - 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x22, 0xdc, 0x02, 0x0a, 0x0e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4c, 0x0a, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, + 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x38, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xc2, 0x01, 0x0a, + 0x10, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x12, 0x3f, 0x0a, 0x07, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, - 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x12, 0x3e, 0x0a, 0x03, 0x61, 0x64, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, - 0x52, 0x03, 0x61, 0x64, 0x64, 0x12, 0x47, 0x0a, 0x06, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x12, 0x6b, - 0x0a, 0x17, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, - 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x31, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, - 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x48, 0x00, 0x52, 0x15, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x6f, - 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x6b, - 0x69, 0x6e, 0x64, 0x22, 0xa1, 0x01, 0x0a, 0x0e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x44, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x13, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x19, 0x0a, 0x08, - 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x22, 0x85, 0x01, 0x0a, 0x09, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x3e, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x38, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, - 0xc2, 0x01, 0x0a, 0x10, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x12, - 0x3f, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, - 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, - 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x73, - 0x65, 0x65, 0x6e, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x04, - 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0e, 0x73, 0x65, 0x65, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x73, 0x22, 0xbc, 0x01, 0x0a, 0x14, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x66, 0x66, 0x12, 0x4d, 0x0a, - 0x0b, 0x6e, 0x65, 0x77, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x4d, 0x61, 0x70, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x29, 0x0a, + 0x10, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x65, 0x65, 0x6e, + 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0c, 0x52, 0x0e, 0x73, 0x65, 0x65, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x22, 0xbc, 0x01, 0x0a, 0x14, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x66, 0x66, 0x12, 0x4d, 0x0a, 0x0b, 0x6e, 0x65, + 0x77, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, + 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0a, 0x6e, + 0x65, 0x77, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x0f, 0x72, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, - 0x52, 0x0a, 0x6e, 0x65, 0x77, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x0f, - 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x73, 0x42, 0xef, 0x01, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, - 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x10, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x2f, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xa2, 0x02, 0x03, - 0x58, 0x49, 0x41, 0xaa, 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x2e, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0xca, 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x5c, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xe2, 0x02, 0x26, + 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, + 0x42, 0xef, 0x01, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x42, 0x10, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, + 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x61, 0x73, + 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x49, 0x41, + 0xaa, 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x2e, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xca, 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5c, 0x41, 0x73, - 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1c, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x49, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x3a, 0x3a, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x4a, 0xde, 0x1c, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x63, 0x01, - 0x0a, 0x3b, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x31, 0x20, 0x50, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, - 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, - 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x08, 0x0a, - 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, 0x23, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x05, - 0x00, 0x2f, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x07, 0x00, 0x48, 0x0a, 0x09, 0x0a, 0x02, - 0x08, 0x0b, 0x12, 0x03, 0x07, 0x00, 0x48, 0x0a, 0x33, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x0b, - 0x00, 0x10, 0x01, 0x1a, 0x27, 0x20, 0x54, 0x68, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, - 0x04, 0x00, 0x01, 0x12, 0x03, 0x0b, 0x08, 0x18, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x00, 0x08, 0x00, - 0x12, 0x04, 0x0c, 0x02, 0x0f, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x08, 0x00, 0x01, 0x12, - 0x03, 0x0c, 0x08, 0x0c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0d, 0x04, - 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x0d, 0x04, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0d, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0d, 0x15, 0x16, 0x0a, 0x0b, 0x0a, 0x04, 0x04, - 0x00, 0x02, 0x01, 0x12, 0x03, 0x0e, 0x04, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, - 0x05, 0x12, 0x03, 0x0e, 0x04, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, - 0x03, 0x0e, 0x0a, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x0e, - 0x24, 0x25, 0x0a, 0x50, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x13, 0x00, 0x16, 0x01, 0x1a, 0x44, - 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x74, - 0x68, 0x61, 0x74, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x69, - 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x74, - 0x68, 0x65, 0x6d, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x13, 0x08, 0x0e, - 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x14, 0x02, 0x22, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, 0x14, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x14, 0x13, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, - 0x00, 0x03, 0x12, 0x03, 0x14, 0x20, 0x21, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, 0x12, - 0x03, 0x15, 0x02, 0x30, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x04, 0x12, 0x03, 0x15, - 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x06, 0x12, 0x03, 0x15, 0x0b, 0x1b, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x15, 0x1c, 0x2b, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x15, 0x2e, 0x2f, 0x0a, 0xf8, 0x01, 0x0a, - 0x02, 0x04, 0x02, 0x12, 0x04, 0x1c, 0x00, 0x20, 0x01, 0x1a, 0xeb, 0x01, 0x20, 0x54, 0x68, 0x65, - 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x6f, 0x66, 0x20, - 0x61, 0x6e, 0x79, 0x20, 0x58, 0x49, 0x44, 0x20, 0x6c, 0x6f, 0x67, 0x2e, 0x20, 0x54, 0x68, 0x65, - 0x20, 0x58, 0x49, 0x44, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x64, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x64, - 0x65, 0x72, 0x69, 0x76, 0x61, 0x62, 0x6c, 0x65, 0x0a, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6e, - 0x6f, 0x6e, 0x63, 0x65, 0x2e, 0x0a, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x6f, 0x76, - 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x64, 0x65, 0x66, 0x61, - 0x75, 0x6c, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x69, 0x74, - 0x69, 0x61, 0x6c, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x75, 0x6e, 0x6c, 0x65, 0x73, 0x73, 0x0a, 0x20, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x73, 0x75, 0x62, 0x73, 0x65, 0x71, - 0x75, 0x65, 0x6e, 0x74, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x76, - 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x6c, 0x6f, 0x67, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, - 0x1c, 0x08, 0x13, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x1d, 0x02, 0x1d, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x05, 0x12, 0x03, 0x1d, 0x02, 0x08, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1d, 0x09, 0x18, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1d, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, - 0x02, 0x01, 0x12, 0x03, 0x1e, 0x02, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x05, - 0x12, 0x03, 0x1e, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x01, 0x12, 0x03, - 0x1e, 0x09, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, 0x03, 0x1e, 0x11, - 0x12, 0x0a, 0x2c, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x02, 0x12, 0x03, 0x1f, 0x02, 0x2a, 0x22, 0x1f, - 0x20, 0x4d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x06, 0x12, 0x03, 0x1f, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x02, 0x02, 0x02, 0x01, 0x12, 0x03, 0x1f, 0x0c, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x02, 0x02, 0x02, 0x03, 0x12, 0x03, 0x1f, 0x28, 0x29, 0x0a, 0x82, 0x02, 0x0a, 0x02, 0x04, 0x03, - 0x12, 0x04, 0x26, 0x00, 0x2a, 0x01, 0x1a, 0xf5, 0x01, 0x20, 0x41, 0x64, 0x64, 0x73, 0x20, 0x61, - 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, - 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x20, 0x2d, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, - 0x61, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x73, 0x75, 0x63, 0x68, 0x20, 0x61, 0x73, 0x20, 0x61, 0x0a, - 0x20, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x61, 0x6e, 0x20, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x63, 0x74, 0x69, - 0x6e, 0x67, 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x65, 0x68, 0x61, 0x6c, 0x66, 0x20, 0x6f, 0x66, 0x20, - 0x61, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x0a, 0x20, 0x41, 0x20, 0x6b, - 0x65, 0x79, 0x2d, 0x70, 0x61, 0x69, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x73, - 0x20, 0x62, 0x65, 0x65, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, - 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x20, 0x4d, - 0x55, 0x53, 0x54, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, - 0x74, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x0a, 0x20, 0x61, 0x73, 0x73, 0x6f, - 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, 0x64, 0x69, - 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x2e, 0x0a, 0x0a, 0x0a, - 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x03, 0x26, 0x08, 0x16, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, - 0x02, 0x00, 0x12, 0x03, 0x27, 0x02, 0x2d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x06, - 0x12, 0x03, 0x27, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x01, 0x12, 0x03, - 0x27, 0x13, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, 0x27, 0x2b, - 0x2c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x01, 0x12, 0x03, 0x28, 0x02, 0x2a, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x06, 0x12, 0x03, 0x28, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x03, 0x02, 0x01, 0x01, 0x12, 0x03, 0x28, 0x0c, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, - 0x02, 0x01, 0x03, 0x12, 0x03, 0x28, 0x28, 0x29, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x02, - 0x12, 0x03, 0x29, 0x02, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x06, 0x12, 0x03, - 0x29, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x01, 0x12, 0x03, 0x29, 0x0c, - 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x03, 0x12, 0x03, 0x29, 0x23, 0x24, 0x0a, - 0x5a, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x2d, 0x00, 0x30, 0x01, 0x1a, 0x4e, 0x20, 0x52, 0x65, - 0x76, 0x6f, 0x6b, 0x65, 0x73, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x66, - 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, - 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, - 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, - 0x04, 0x01, 0x12, 0x03, 0x2d, 0x08, 0x19, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, - 0x03, 0x2e, 0x02, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x06, 0x12, 0x03, 0x2e, - 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2e, 0x13, 0x23, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2e, 0x26, 0x27, 0x0a, 0x0b, - 0x0a, 0x04, 0x04, 0x04, 0x02, 0x01, 0x12, 0x03, 0x2f, 0x02, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x04, 0x02, 0x01, 0x06, 0x12, 0x03, 0x2f, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, - 0x01, 0x01, 0x12, 0x03, 0x2f, 0x0c, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x03, - 0x12, 0x03, 0x2f, 0x29, 0x2a, 0x0a, 0xd1, 0x01, 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04, 0x35, 0x00, - 0x38, 0x01, 0x1a, 0xc4, 0x01, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x74, 0x68, + 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xe2, 0x02, 0x26, 0x58, 0x6d, 0x74, + 0x70, 0x5c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5c, 0x41, 0x73, 0x73, 0x6f, 0x63, + 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0xea, 0x02, 0x1c, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x49, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x3a, 0x3a, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x4a, 0xa3, 0x1d, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x64, 0x01, 0x0a, 0x3b, 0x0a, + 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x31, 0x20, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x61, 0x73, 0x73, + 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, + 0x03, 0x03, 0x00, 0x23, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x05, 0x00, 0x2f, 0x0a, + 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x07, 0x00, 0x48, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, + 0x03, 0x07, 0x00, 0x48, 0x0a, 0x33, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x0b, 0x00, 0x10, 0x01, + 0x1a, 0x27, 0x20, 0x54, 0x68, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, + 0x66, 0x20, 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, + 0x12, 0x03, 0x0b, 0x08, 0x18, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x00, 0x08, 0x00, 0x12, 0x04, 0x0c, + 0x02, 0x0f, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x08, 0x00, 0x01, 0x12, 0x03, 0x0c, 0x08, + 0x0c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0d, 0x04, 0x17, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x0d, 0x04, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0d, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, + 0x02, 0x00, 0x03, 0x12, 0x03, 0x0d, 0x15, 0x16, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, + 0x12, 0x03, 0x0e, 0x04, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, + 0x0e, 0x04, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x0e, 0x0a, + 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x0e, 0x24, 0x25, 0x0a, + 0x50, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x13, 0x00, 0x17, 0x01, 0x1a, 0x44, 0x20, 0x73, 0x69, + 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, + 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x69, 0x6e, 0x64, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6d, + 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x13, 0x08, 0x0e, 0x0a, 0x0b, 0x0a, + 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x14, 0x02, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, + 0x02, 0x00, 0x06, 0x12, 0x03, 0x14, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, + 0x01, 0x12, 0x03, 0x14, 0x13, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, + 0x03, 0x14, 0x20, 0x21, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, 0x12, 0x03, 0x15, 0x02, + 0x30, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x04, 0x12, 0x03, 0x15, 0x02, 0x0a, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x06, 0x12, 0x03, 0x15, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x15, 0x1c, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x15, 0x2e, 0x2f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, + 0x02, 0x12, 0x03, 0x16, 0x02, 0x2a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x04, 0x12, + 0x03, 0x16, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x05, 0x12, 0x03, 0x16, + 0x0b, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x01, 0x12, 0x03, 0x16, 0x12, 0x25, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x03, 0x12, 0x03, 0x16, 0x28, 0x29, 0x0a, 0xf8, + 0x01, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x1d, 0x00, 0x21, 0x01, 0x1a, 0xeb, 0x01, 0x20, 0x54, + 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x6f, + 0x66, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x58, 0x49, 0x44, 0x20, 0x6c, 0x6f, 0x67, 0x2e, 0x20, 0x54, + 0x68, 0x65, 0x20, 0x58, 0x49, 0x44, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x64, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, + 0x20, 0x64, 0x65, 0x72, 0x69, 0x76, 0x61, 0x62, 0x6c, 0x65, 0x0a, 0x20, 0x66, 0x72, 0x6f, 0x6d, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x2e, 0x0a, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, + 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x75, 0x6e, 0x6c, 0x65, 0x73, 0x73, 0x0a, + 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x73, 0x75, 0x62, 0x73, + 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x69, 0x6e, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x6c, 0x6f, 0x67, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, + 0x12, 0x03, 0x1d, 0x08, 0x13, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x1e, + 0x02, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x05, 0x12, 0x03, 0x1e, 0x02, 0x08, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1e, 0x09, 0x18, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1e, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x02, 0x02, 0x01, 0x12, 0x03, 0x1f, 0x02, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, + 0x01, 0x05, 0x12, 0x03, 0x1f, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x01, + 0x12, 0x03, 0x1f, 0x09, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, 0x03, + 0x1f, 0x11, 0x12, 0x0a, 0x2c, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x02, 0x12, 0x03, 0x20, 0x02, 0x2a, + 0x22, 0x1f, 0x20, 0x4d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x06, 0x12, 0x03, 0x20, 0x02, 0x0b, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x01, 0x12, 0x03, 0x20, 0x0c, 0x25, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x02, 0x02, 0x02, 0x03, 0x12, 0x03, 0x20, 0x28, 0x29, 0x0a, 0x82, 0x02, 0x0a, 0x02, + 0x04, 0x03, 0x12, 0x04, 0x27, 0x00, 0x2b, 0x01, 0x1a, 0xf5, 0x01, 0x20, 0x41, 0x64, 0x64, 0x73, + 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x20, 0x2d, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, + 0x72, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x73, 0x75, 0x63, 0x68, 0x20, 0x61, 0x73, 0x20, + 0x61, 0x0a, 0x20, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x61, 0x6e, + 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x63, + 0x74, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x65, 0x68, 0x61, 0x6c, 0x66, 0x20, 0x6f, + 0x66, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x0a, 0x20, 0x41, + 0x20, 0x6b, 0x65, 0x79, 0x2d, 0x70, 0x61, 0x69, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, + 0x61, 0x73, 0x20, 0x62, 0x65, 0x65, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, + 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x72, 0x6f, 0x6c, 0x65, + 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x70, 0x65, 0x72, + 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x0a, 0x20, 0x61, 0x73, + 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, + 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x72, 0x6f, 0x6c, 0x65, 0x2e, 0x0a, + 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x03, 0x27, 0x08, 0x16, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x03, 0x02, 0x00, 0x12, 0x03, 0x28, 0x02, 0x2d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, + 0x00, 0x06, 0x12, 0x03, 0x28, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x01, + 0x12, 0x03, 0x28, 0x13, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, + 0x28, 0x2b, 0x2c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x01, 0x12, 0x03, 0x29, 0x02, 0x2a, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x06, 0x12, 0x03, 0x29, 0x02, 0x0b, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x12, 0x03, 0x29, 0x0c, 0x25, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x03, 0x02, 0x01, 0x03, 0x12, 0x03, 0x29, 0x28, 0x29, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, + 0x02, 0x02, 0x12, 0x03, 0x2a, 0x02, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x06, + 0x12, 0x03, 0x2a, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x01, 0x12, 0x03, + 0x2a, 0x0c, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x03, 0x12, 0x03, 0x2a, 0x23, + 0x24, 0x0a, 0x5a, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x2e, 0x00, 0x31, 0x01, 0x1a, 0x4e, 0x20, + 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x73, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x2e, 0x20, 0x54, - 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x64, 0x0a, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x58, 0x49, 0x44, 0x2e, 0x20, - 0x49, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x62, - 0x65, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x64, 0x64, - 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x72, - 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, - 0x63, 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x20, - 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x05, 0x01, - 0x12, 0x03, 0x35, 0x08, 0x1d, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, 0x12, 0x03, 0x36, - 0x02, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x05, 0x12, 0x03, 0x36, 0x02, 0x08, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x01, 0x12, 0x03, 0x36, 0x09, 0x1d, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x03, 0x12, 0x03, 0x36, 0x20, 0x21, 0x0a, 0x0b, 0x0a, 0x04, - 0x04, 0x05, 0x02, 0x01, 0x12, 0x03, 0x37, 0x02, 0x34, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, - 0x01, 0x06, 0x12, 0x03, 0x37, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x01, - 0x12, 0x03, 0x37, 0x0c, 0x2f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x03, 0x12, 0x03, - 0x37, 0x32, 0x33, 0x0a, 0x29, 0x0a, 0x02, 0x04, 0x06, 0x12, 0x04, 0x3b, 0x00, 0x42, 0x01, 0x1a, - 0x1d, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x0a, 0x0a, - 0x0a, 0x03, 0x04, 0x06, 0x01, 0x12, 0x03, 0x3b, 0x08, 0x16, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x06, - 0x08, 0x00, 0x12, 0x04, 0x3c, 0x02, 0x41, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x08, 0x00, - 0x01, 0x12, 0x03, 0x3c, 0x08, 0x0c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x00, 0x12, 0x03, - 0x3d, 0x04, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x06, 0x12, 0x03, 0x3d, 0x04, - 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x01, 0x12, 0x03, 0x3d, 0x10, 0x1c, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x03, 0x12, 0x03, 0x3d, 0x1f, 0x20, 0x0a, 0x0b, 0x0a, - 0x04, 0x04, 0x06, 0x02, 0x01, 0x12, 0x03, 0x3e, 0x04, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, - 0x02, 0x01, 0x06, 0x12, 0x03, 0x3e, 0x04, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x01, - 0x01, 0x12, 0x03, 0x3e, 0x13, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x01, 0x03, 0x12, - 0x03, 0x3e, 0x19, 0x1a, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x02, 0x12, 0x03, 0x3f, 0x04, - 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x02, 0x06, 0x12, 0x03, 0x3f, 0x04, 0x15, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x02, 0x01, 0x12, 0x03, 0x3f, 0x16, 0x1c, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x06, 0x02, 0x02, 0x03, 0x12, 0x03, 0x3f, 0x1f, 0x20, 0x0a, 0x0b, 0x0a, 0x04, 0x04, - 0x06, 0x02, 0x03, 0x12, 0x03, 0x40, 0x04, 0x36, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x03, - 0x06, 0x12, 0x03, 0x40, 0x04, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x03, 0x01, 0x12, - 0x03, 0x40, 0x1a, 0x31, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x03, 0x03, 0x12, 0x03, 0x40, - 0x34, 0x35, 0x0a, 0xd5, 0x03, 0x0a, 0x02, 0x04, 0x07, 0x12, 0x04, 0x4b, 0x00, 0x4f, 0x01, 0x1a, - 0xc8, 0x03, 0x20, 0x4f, 0x6e, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x69, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x65, 0x72, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, - 0x20, 0x74, 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x0a, 0x20, 0x45, 0x78, 0x61, 0x6d, - 0x70, 0x6c, 0x65, 0x3a, 0x20, 0x5b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x58, 0x69, 0x64, 0x2c, - 0x20, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, - 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5d, 0x0a, 0x20, 0x31, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, - 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x69, 0x73, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x64, 0x20, 0x62, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x6e, 0x61, 0x74, 0x69, - 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x20, 0x74, 0x65, 0x78, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x66, 0x20, 0x65, 0x61, 0x63, - 0x68, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, - 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, 0x73, 0x65, - 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x27, 0x5c, 0x6e, 0x5c, 0x6e, 0x5c, 0x6e, - 0x27, 0x2e, 0x0a, 0x20, 0x32, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, - 0x73, 0x69, 0x67, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, - 0x74, 0x65, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x0a, - 0x20, 0x33, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e, - 0x67, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, - 0x64, 0x64, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x61, 0x73, 0x73, - 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x20, 0x77, - 0x68, 0x65, 0x72, 0x65, 0x20, 0x72, 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x74, 0x2e, 0x0a, 0x20, - 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, - 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x61, - 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x07, - 0x01, 0x12, 0x03, 0x4b, 0x08, 0x16, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x00, 0x12, 0x03, - 0x4c, 0x02, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x04, 0x12, 0x03, 0x4c, 0x02, - 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x06, 0x12, 0x03, 0x4c, 0x0b, 0x19, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x01, 0x12, 0x03, 0x4c, 0x1a, 0x21, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x07, 0x02, 0x00, 0x03, 0x12, 0x03, 0x4c, 0x24, 0x25, 0x0a, 0x0b, 0x0a, 0x04, 0x04, - 0x07, 0x02, 0x01, 0x12, 0x03, 0x4d, 0x02, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, - 0x05, 0x12, 0x03, 0x4d, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x01, 0x12, - 0x03, 0x4d, 0x09, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x03, 0x12, 0x03, 0x4d, - 0x1f, 0x20, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x02, 0x12, 0x03, 0x4e, 0x02, 0x16, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x02, 0x05, 0x12, 0x03, 0x4e, 0x02, 0x08, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x07, 0x02, 0x02, 0x01, 0x12, 0x03, 0x4e, 0x09, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x07, 0x02, 0x02, 0x03, 0x12, 0x03, 0x4e, 0x14, 0x15, 0x0a, 0x35, 0x0a, 0x02, 0x04, 0x08, 0x12, - 0x04, 0x52, 0x00, 0x55, 0x01, 0x1a, 0x29, 0x20, 0x4d, 0x61, 0x70, 0x20, 0x6f, 0x66, 0x20, 0x6d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, 0x62, 0x65, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x6e, 0x67, - 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x0a, - 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x08, 0x01, 0x12, 0x03, 0x52, 0x08, 0x11, 0x0a, 0x0b, 0x0a, 0x04, - 0x04, 0x08, 0x02, 0x00, 0x12, 0x03, 0x53, 0x02, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, - 0x00, 0x06, 0x12, 0x03, 0x53, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x01, - 0x12, 0x03, 0x53, 0x13, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x03, 0x12, 0x03, - 0x53, 0x19, 0x1a, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x08, 0x02, 0x01, 0x12, 0x03, 0x54, 0x02, 0x13, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x01, 0x06, 0x12, 0x03, 0x54, 0x02, 0x08, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x08, 0x02, 0x01, 0x01, 0x12, 0x03, 0x54, 0x09, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x08, 0x02, 0x01, 0x03, 0x12, 0x03, 0x54, 0x11, 0x12, 0x0a, 0x51, 0x0a, 0x02, 0x04, 0x09, - 0x12, 0x04, 0x58, 0x00, 0x5d, 0x01, 0x1a, 0x45, 0x20, 0x41, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, - 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x66, 0x72, 0x6f, - 0x6d, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x60, 0x49, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x60, 0x0a, 0x0a, 0x0a, 0x0a, - 0x03, 0x04, 0x09, 0x01, 0x12, 0x03, 0x58, 0x08, 0x18, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x09, 0x02, - 0x00, 0x12, 0x03, 0x59, 0x02, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x05, 0x12, - 0x03, 0x59, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x01, 0x12, 0x03, 0x59, - 0x09, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x03, 0x12, 0x03, 0x59, 0x14, 0x15, - 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x09, 0x02, 0x01, 0x12, 0x03, 0x5a, 0x02, 0x21, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x09, 0x02, 0x01, 0x04, 0x12, 0x03, 0x5a, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x09, 0x02, 0x01, 0x06, 0x12, 0x03, 0x5a, 0x0b, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, - 0x01, 0x01, 0x12, 0x03, 0x5a, 0x15, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x01, 0x03, - 0x12, 0x03, 0x5a, 0x1f, 0x20, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x09, 0x02, 0x02, 0x12, 0x03, 0x5b, - 0x02, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x02, 0x05, 0x12, 0x03, 0x5b, 0x02, 0x08, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x02, 0x01, 0x12, 0x03, 0x5b, 0x09, 0x19, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x09, 0x02, 0x02, 0x03, 0x12, 0x03, 0x5b, 0x1c, 0x1d, 0x0a, 0x0b, 0x0a, 0x04, - 0x04, 0x09, 0x02, 0x03, 0x12, 0x03, 0x5c, 0x02, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, - 0x03, 0x04, 0x12, 0x03, 0x5c, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x03, 0x05, - 0x12, 0x03, 0x5c, 0x0b, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x03, 0x01, 0x12, 0x03, - 0x5c, 0x11, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x03, 0x03, 0x12, 0x03, 0x5c, 0x23, - 0x24, 0x0a, 0x3d, 0x0a, 0x02, 0x04, 0x0a, 0x12, 0x04, 0x60, 0x00, 0x63, 0x01, 0x1a, 0x31, 0x2f, - 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x64, 0x69, 0x66, 0x66, 0x20, 0x62, 0x65, 0x74, 0x77, - 0x65, 0x65, 0x6e, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x41, 0x73, - 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x0a, - 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x0a, 0x01, 0x12, 0x03, 0x60, 0x08, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, - 0x04, 0x0a, 0x02, 0x00, 0x12, 0x03, 0x61, 0x02, 0x2c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, - 0x00, 0x04, 0x12, 0x03, 0x61, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x06, - 0x12, 0x03, 0x61, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x01, 0x12, 0x03, - 0x61, 0x1c, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x03, 0x12, 0x03, 0x61, 0x2a, - 0x2b, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0a, 0x02, 0x01, 0x12, 0x03, 0x62, 0x02, 0x30, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x01, 0x04, 0x12, 0x03, 0x62, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x0a, 0x02, 0x01, 0x06, 0x12, 0x03, 0x62, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, - 0x02, 0x01, 0x01, 0x12, 0x03, 0x62, 0x1c, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x01, - 0x03, 0x12, 0x03, 0x62, 0x2e, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x73, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x72, 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, + 0x03, 0x04, 0x04, 0x01, 0x12, 0x03, 0x2e, 0x08, 0x19, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, + 0x00, 0x12, 0x03, 0x2f, 0x02, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x06, 0x12, + 0x03, 0x2f, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2f, + 0x13, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2f, 0x26, 0x27, + 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x01, 0x12, 0x03, 0x30, 0x02, 0x2b, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x04, 0x02, 0x01, 0x06, 0x12, 0x03, 0x30, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x04, 0x02, 0x01, 0x01, 0x12, 0x03, 0x30, 0x0c, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, + 0x01, 0x03, 0x12, 0x03, 0x30, 0x29, 0x2a, 0x0a, 0xd1, 0x01, 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04, + 0x36, 0x00, 0x39, 0x01, 0x1a, 0xc4, 0x01, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x6e, 0x20, 0x58, 0x49, 0x44, 0x2e, + 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x6d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x58, 0x49, 0x44, + 0x2e, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, + 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, + 0x64, 0x64, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x0a, + 0x20, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x72, 0x65, 0x76, 0x6f, 0x6b, + 0x65, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, + 0x05, 0x01, 0x12, 0x03, 0x36, 0x08, 0x1d, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, 0x12, + 0x03, 0x37, 0x02, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x05, 0x12, 0x03, 0x37, + 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x01, 0x12, 0x03, 0x37, 0x09, 0x1d, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x03, 0x12, 0x03, 0x37, 0x20, 0x21, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x05, 0x02, 0x01, 0x12, 0x03, 0x38, 0x02, 0x34, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x05, 0x02, 0x01, 0x06, 0x12, 0x03, 0x38, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, + 0x01, 0x01, 0x12, 0x03, 0x38, 0x0c, 0x2f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x03, + 0x12, 0x03, 0x38, 0x32, 0x33, 0x0a, 0x29, 0x0a, 0x02, 0x04, 0x06, 0x12, 0x04, 0x3c, 0x00, 0x43, + 0x01, 0x1a, 0x1d, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, + 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x06, 0x01, 0x12, 0x03, 0x3c, 0x08, 0x16, 0x0a, 0x0c, 0x0a, 0x04, + 0x04, 0x06, 0x08, 0x00, 0x12, 0x04, 0x3d, 0x02, 0x42, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, + 0x08, 0x00, 0x01, 0x12, 0x03, 0x3d, 0x08, 0x0c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x00, + 0x12, 0x03, 0x3e, 0x04, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x06, 0x12, 0x03, + 0x3e, 0x04, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x01, 0x12, 0x03, 0x3e, 0x10, + 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x03, 0x12, 0x03, 0x3e, 0x1f, 0x20, 0x0a, + 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x01, 0x12, 0x03, 0x3f, 0x04, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x06, 0x02, 0x01, 0x06, 0x12, 0x03, 0x3f, 0x04, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, + 0x02, 0x01, 0x01, 0x12, 0x03, 0x3f, 0x13, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x01, + 0x03, 0x12, 0x03, 0x3f, 0x19, 0x1a, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x02, 0x12, 0x03, + 0x40, 0x04, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x02, 0x06, 0x12, 0x03, 0x40, 0x04, + 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x02, 0x01, 0x12, 0x03, 0x40, 0x16, 0x1c, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x02, 0x03, 0x12, 0x03, 0x40, 0x1f, 0x20, 0x0a, 0x0b, 0x0a, + 0x04, 0x04, 0x06, 0x02, 0x03, 0x12, 0x03, 0x41, 0x04, 0x36, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, + 0x02, 0x03, 0x06, 0x12, 0x03, 0x41, 0x04, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x03, + 0x01, 0x12, 0x03, 0x41, 0x1a, 0x31, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x03, 0x03, 0x12, + 0x03, 0x41, 0x34, 0x35, 0x0a, 0xd5, 0x03, 0x0a, 0x02, 0x04, 0x07, 0x12, 0x04, 0x4c, 0x00, 0x50, + 0x01, 0x1a, 0xc8, 0x03, 0x20, 0x4f, 0x6e, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, + 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x65, 0x72, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, + 0x65, 0x64, 0x20, 0x74, 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x0a, 0x20, 0x45, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x3a, 0x20, 0x5b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x58, 0x69, + 0x64, 0x2c, 0x20, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2c, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5d, 0x0a, 0x20, 0x31, 0x2e, 0x20, 0x54, 0x68, + 0x65, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x69, 0x73, 0x20, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x6e, 0x61, + 0x74, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x66, 0x20, 0x65, + 0x61, 0x63, 0x68, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x74, 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, + 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2c, 0x20, 0x27, 0x5c, 0x6e, 0x5c, 0x6e, + 0x5c, 0x6e, 0x27, 0x2e, 0x0a, 0x20, 0x32, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x75, 0x73, 0x65, + 0x72, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6e, + 0x63, 0x61, 0x74, 0x65, 0x6e, 0x61, 0x74, 0x65, 0x64, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x2e, 0x0a, 0x20, 0x33, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x69, 0x6e, 0x67, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x69, 0x73, + 0x20, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x61, + 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x72, 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x74, 0x2e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x75, + 0x73, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, + 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x69, 0x6e, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, + 0x04, 0x07, 0x01, 0x12, 0x03, 0x4c, 0x08, 0x16, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x00, + 0x12, 0x03, 0x4d, 0x02, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x04, 0x12, 0x03, + 0x4d, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x06, 0x12, 0x03, 0x4d, 0x0b, + 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x01, 0x12, 0x03, 0x4d, 0x1a, 0x21, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x03, 0x12, 0x03, 0x4d, 0x24, 0x25, 0x0a, 0x0b, 0x0a, + 0x04, 0x04, 0x07, 0x02, 0x01, 0x12, 0x03, 0x4e, 0x02, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, + 0x02, 0x01, 0x05, 0x12, 0x03, 0x4e, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, + 0x01, 0x12, 0x03, 0x4e, 0x09, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x03, 0x12, + 0x03, 0x4e, 0x1f, 0x20, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x02, 0x12, 0x03, 0x4f, 0x02, + 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x02, 0x05, 0x12, 0x03, 0x4f, 0x02, 0x08, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x02, 0x01, 0x12, 0x03, 0x4f, 0x09, 0x11, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x07, 0x02, 0x02, 0x03, 0x12, 0x03, 0x4f, 0x14, 0x15, 0x0a, 0x35, 0x0a, 0x02, 0x04, + 0x08, 0x12, 0x04, 0x53, 0x00, 0x56, 0x01, 0x1a, 0x29, 0x20, 0x4d, 0x61, 0x70, 0x20, 0x6f, 0x66, + 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, 0x62, 0x65, 0x6c, 0x6f, 0x6e, 0x67, 0x69, + 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, + 0x64, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x08, 0x01, 0x12, 0x03, 0x53, 0x08, 0x11, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x08, 0x02, 0x00, 0x12, 0x03, 0x54, 0x02, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x08, 0x02, 0x00, 0x06, 0x12, 0x03, 0x54, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, + 0x00, 0x01, 0x12, 0x03, 0x54, 0x13, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x03, + 0x12, 0x03, 0x54, 0x19, 0x1a, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x08, 0x02, 0x01, 0x12, 0x03, 0x55, + 0x02, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x01, 0x06, 0x12, 0x03, 0x55, 0x02, 0x08, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x01, 0x01, 0x12, 0x03, 0x55, 0x09, 0x0e, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x08, 0x02, 0x01, 0x03, 0x12, 0x03, 0x55, 0x11, 0x12, 0x0a, 0x51, 0x0a, 0x02, + 0x04, 0x09, 0x12, 0x04, 0x59, 0x00, 0x5e, 0x01, 0x1a, 0x45, 0x20, 0x41, 0x20, 0x66, 0x69, 0x6e, + 0x61, 0x6c, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x66, + 0x72, 0x6f, 0x6d, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x60, 0x49, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x60, 0x0a, 0x0a, + 0x0a, 0x0a, 0x03, 0x04, 0x09, 0x01, 0x12, 0x03, 0x59, 0x08, 0x18, 0x0a, 0x0b, 0x0a, 0x04, 0x04, + 0x09, 0x02, 0x00, 0x12, 0x03, 0x5a, 0x02, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, + 0x05, 0x12, 0x03, 0x5a, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x01, 0x12, + 0x03, 0x5a, 0x09, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x03, 0x12, 0x03, 0x5a, + 0x14, 0x15, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x09, 0x02, 0x01, 0x12, 0x03, 0x5b, 0x02, 0x21, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x01, 0x04, 0x12, 0x03, 0x5b, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x09, 0x02, 0x01, 0x06, 0x12, 0x03, 0x5b, 0x0b, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x09, 0x02, 0x01, 0x01, 0x12, 0x03, 0x5b, 0x15, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, + 0x01, 0x03, 0x12, 0x03, 0x5b, 0x1f, 0x20, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x09, 0x02, 0x02, 0x12, + 0x03, 0x5c, 0x02, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x02, 0x05, 0x12, 0x03, 0x5c, + 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x02, 0x01, 0x12, 0x03, 0x5c, 0x09, 0x19, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x02, 0x03, 0x12, 0x03, 0x5c, 0x1c, 0x1d, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x09, 0x02, 0x03, 0x12, 0x03, 0x5d, 0x02, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x09, 0x02, 0x03, 0x04, 0x12, 0x03, 0x5d, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, + 0x03, 0x05, 0x12, 0x03, 0x5d, 0x0b, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x03, 0x01, + 0x12, 0x03, 0x5d, 0x11, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x03, 0x03, 0x12, 0x03, + 0x5d, 0x23, 0x24, 0x0a, 0x3d, 0x0a, 0x02, 0x04, 0x0a, 0x12, 0x04, 0x61, 0x00, 0x64, 0x01, 0x1a, + 0x31, 0x2f, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x64, 0x69, 0x66, 0x66, 0x20, 0x62, 0x65, + 0x74, 0x77, 0x65, 0x65, 0x6e, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x20, + 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x0a, 0x01, 0x12, 0x03, 0x61, 0x08, 0x1c, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x0a, 0x02, 0x00, 0x12, 0x03, 0x62, 0x02, 0x2c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x0a, 0x02, 0x00, 0x04, 0x12, 0x03, 0x62, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, + 0x00, 0x06, 0x12, 0x03, 0x62, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x01, + 0x12, 0x03, 0x62, 0x1c, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x03, 0x12, 0x03, + 0x62, 0x2a, 0x2b, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0a, 0x02, 0x01, 0x12, 0x03, 0x63, 0x02, 0x30, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x01, 0x04, 0x12, 0x03, 0x63, 0x02, 0x0a, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x01, 0x06, 0x12, 0x03, 0x63, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x0a, 0x02, 0x01, 0x01, 0x12, 0x03, 0x63, 0x1c, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, + 0x02, 0x01, 0x03, 0x12, 0x03, 0x63, 0x2e, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, ]; include!("xmtp.identity.associations.serde.rs"); // @@protoc_insertion_point(module) \ No newline at end of file diff --git a/xmtp_proto/src/gen/xmtp.identity.associations.serde.rs b/xmtp_proto/src/gen/xmtp.identity.associations.serde.rs index abe5f4671..696f4a122 100644 --- a/xmtp_proto/src/gen/xmtp.identity.associations.serde.rs +++ b/xmtp_proto/src/gen/xmtp.identity.associations.serde.rs @@ -518,6 +518,7 @@ impl serde::Serialize for CreateInbox { } 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())?; } if let Some(v) = self.initial_address_signature.as_ref() { @@ -785,6 +786,7 @@ impl serde::Serialize for IdentityUpdate { } if self.client_timestamp_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("clientTimestampNs", ToString::to_string(&self.client_timestamp_ns).as_str())?; } if !self.inbox_id.is_empty() { @@ -1015,6 +1017,9 @@ impl serde::Serialize for Member { if self.added_by_entity.is_some() { len += 1; } + if self.client_timestamp_ns.is_some() { + len += 1; + } let mut struct_ser = serializer.serialize_struct("xmtp.identity.associations.Member", len)?; if let Some(v) = self.identifier.as_ref() { struct_ser.serialize_field("identifier", v)?; @@ -1022,6 +1027,11 @@ impl serde::Serialize for Member { if let Some(v) = self.added_by_entity.as_ref() { struct_ser.serialize_field("addedByEntity", v)?; } + if let Some(v) = self.client_timestamp_ns.as_ref() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("clientTimestampNs", ToString::to_string(&v).as_str())?; + } struct_ser.end() } } @@ -1035,12 +1045,15 @@ impl<'de> serde::Deserialize<'de> for Member { "identifier", "added_by_entity", "addedByEntity", + "client_timestamp_ns", + "clientTimestampNs", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { Identifier, AddedByEntity, + ClientTimestampNs, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -1064,6 +1077,7 @@ impl<'de> serde::Deserialize<'de> for Member { match value { "identifier" => Ok(GeneratedField::Identifier), "addedByEntity" | "added_by_entity" => Ok(GeneratedField::AddedByEntity), + "clientTimestampNs" | "client_timestamp_ns" => Ok(GeneratedField::ClientTimestampNs), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -1085,6 +1099,7 @@ impl<'de> serde::Deserialize<'de> for Member { { let mut identifier__ = None; let mut added_by_entity__ = None; + let mut client_timestamp_ns__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Identifier => { @@ -1099,11 +1114,20 @@ impl<'de> serde::Deserialize<'de> for Member { } added_by_entity__ = map_.next_value()?; } + GeneratedField::ClientTimestampNs => { + if client_timestamp_ns__.is_some() { + return Err(serde::de::Error::duplicate_field("clientTimestampNs")); + } + client_timestamp_ns__ = + map_.next_value::<::std::option::Option<::pbjson::private::NumberDeserialize<_>>>()?.map(|x| x.0) + ; + } } } Ok(Member { identifier: identifier__, added_by_entity: added_by_entity__, + client_timestamp_ns: client_timestamp_ns__, }) } } @@ -1129,6 +1153,7 @@ impl serde::Serialize for MemberIdentifier { } member_identifier::Kind::InstallationPublicKey(v) => { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("installationPublicKey", pbjson::private::base64::encode(&v).as_str())?; } } @@ -1341,6 +1366,7 @@ impl serde::Serialize for RecoverableEcdsaSignature { let mut struct_ser = serializer.serialize_struct("xmtp.identity.associations.RecoverableEcdsaSignature", len)?; if !self.bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("bytes", pbjson::private::base64::encode(&self.bytes).as_str())?; } struct_ser.end() @@ -1438,10 +1464,12 @@ impl serde::Serialize for RecoverableEd25519Signature { let mut struct_ser = serializer.serialize_struct("xmtp.identity.associations.RecoverableEd25519Signature", len)?; if !self.bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("bytes", pbjson::private::base64::encode(&self.bytes).as_str())?; } if !self.public_key.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("publicKey", pbjson::private::base64::encode(&self.public_key).as_str())?; } struct_ser.end() @@ -1811,10 +1839,12 @@ impl serde::Serialize for SmartContractWalletSignature { } if self.block_number != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("blockNumber", ToString::to_string(&self.block_number).as_str())?; } if !self.signature.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("signature", pbjson::private::base64::encode(&self.signature).as_str())?; } if !self.chain_rpc_url.is_empty() { diff --git a/xmtp_proto/src/gen/xmtp.identity.rs b/xmtp_proto/src/gen/xmtp.identity.rs index b4e176f71..2fb0a5ab9 100644 --- a/xmtp_proto/src/gen/xmtp.identity.rs +++ b/xmtp_proto/src/gen/xmtp.identity.rs @@ -1,4 +1,5 @@ // @generated +// This file is @generated by prost-build. /// A credential that can be used in MLS leaf nodes #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/xmtp_proto/src/gen/xmtp.keystore_api.v1.rs b/xmtp_proto/src/gen/xmtp.keystore_api.v1.rs index 6aae2dd4d..83faab516 100644 --- a/xmtp_proto/src/gen/xmtp.keystore_api.v1.rs +++ b/xmtp_proto/src/gen/xmtp.keystore_api.v1.rs @@ -1,4 +1,5 @@ // @generated +// This file is @generated by prost-build. /// Wrapper class for errors from the Keystore API #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -322,7 +323,7 @@ pub mod save_invites_response { } /// CreateAuthTokenRequest is used to create an auth token for the XMTP API #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct CreateAuthTokenRequest { #[prost(uint64, optional, tag="1")] pub timestamp_ns: ::core::option::Option, @@ -337,7 +338,7 @@ pub struct SaveV1ConversationsRequest { } /// Placeholder response type for SaveV1Conversations #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct SaveV1ConversationsResponse { } /// Response for GetV2Conversations @@ -357,7 +358,7 @@ pub struct GetKeystoreStatusRequest { } /// Response to GetKeystoreStatusRequest #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct GetKeystoreStatusResponse { #[prost(enumeration="get_keystore_status_response::KeystoreStatus", tag="1")] pub status: i32, @@ -432,7 +433,7 @@ pub struct SignDigestRequest { /// Nested message and enum types in `SignDigestRequest`. pub mod sign_digest_request { #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Oneof)] +#[derive(Clone, Copy, PartialEq, ::prost::Oneof)] pub enum Signer { #[prost(bool, tag="2")] IdentityKey(bool), @@ -442,21 +443,21 @@ pub mod sign_digest_request { } /// GetRefreshJobRequest is used to get the last run time of a refresh job #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct GetRefreshJobRequest { #[prost(enumeration="JobType", tag="1")] pub job_type: i32, } /// GetRefreshJobResponse is used to return the last run time of a refresh job #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct GetRefreshJobResponse { #[prost(int64, tag="1")] pub last_run_ns: i64, } /// SetRefreshJobRequest is used to set the last run time of a refresh job #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct SetRefeshJobRequest { #[prost(enumeration="JobType", tag="1")] pub job_type: i32, @@ -465,7 +466,7 @@ pub struct SetRefeshJobRequest { } /// SetRefreshJobResponse is an empty response type #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct SetRefreshJobResponse { } /// A mapping of topics to their decrypted invitations @@ -522,6 +523,13 @@ pub mod get_conversation_hmac_keys_response { pub values: ::prost::alloc::vec::Vec, } } +/// A mapping of message hashes to their private preferences action +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PrivatePreferencesActionMap { + #[prost(map="string, message", tag="1")] + pub actions: ::std::collections::HashMap<::prost::alloc::string::String, super::super::message_contents::PrivatePreferencesAction>, +} /// Application-specific error codes for the Keystore API. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] @@ -587,7 +595,7 @@ impl JobType { } /// Encoded file descriptor set for the `xmtp.keystore_api.v1` package pub const FILE_DESCRIPTOR_SET: &[u8] = &[ - 0x0a, 0xba, 0x7c, 0x0a, 0x1e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, + 0x0a, 0xf1, 0x7f, 0x0a, 0x1e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x1a, 0x21, 0x6d, 0x65, 0x73, 0x73, 0x61, @@ -600,989 +608,1017 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[ 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x21, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5e, 0x0a, 0x0d, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x33, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, - 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x52, - 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0xac, 0x02, 0x0a, 0x10, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x1a, 0x2a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, + 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0x5e, 0x0a, 0x0d, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x33, 0x0a, 0x04, + 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, + 0x65, 0x22, 0xac, 0x02, 0x0a, 0x10, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x56, 0x31, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, + 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x56, 0x31, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x73, 0x1a, 0xcb, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, + 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x21, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x74, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x43, 0x0a, 0x09, 0x70, + 0x65, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, + 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x08, 0x70, 0x65, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x73, + 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, 0x79, + 0x74, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, + 0x22, 0xb0, 0x02, 0x0a, 0x0f, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, + 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, + 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x73, 0x1a, 0xce, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x50, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x36, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x12, 0x3b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x27, + 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x64, 0x65, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0xef, 0x01, 0x0a, 0x10, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x56, + 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x56, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x73, 0x1a, 0x8e, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x3b, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, + 0x74, 0x65, 0x78, 0x74, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x21, 0x0a, + 0x0c, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x70, 0x69, + 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x54, 0x6f, 0x70, 0x69, 0x63, 0x22, 0xed, 0x01, 0x0a, 0x10, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x56, 0x31, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x56, 0x31, 0x52, 0x65, 0x71, + 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x56, 0x31, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x08, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x1a, 0xcb, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x69, 0x70, 0x68, - 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, - 0x43, 0x0a, 0x09, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x08, 0x70, 0x65, 0x65, 0x72, - 0x4b, 0x65, 0x79, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x62, - 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x73, 0x65, - 0x6e, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x53, 0x65, - 0x6e, 0x64, 0x65, 0x72, 0x22, 0xb0, 0x02, 0x0a, 0x0f, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x6d, - 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x1a, 0xce, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x06, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x3b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x1a, 0x27, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1c, 0x0a, - 0x09, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x09, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x42, 0x0a, 0x0a, 0x08, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xef, 0x01, 0x0a, 0x10, 0x44, 0x65, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x56, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, 0x0a, 0x08, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, - 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x56, 0x32, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x08, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x1a, 0x8e, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x69, - 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, - 0x79, 0x74, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, - 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x22, 0xed, 0x01, 0x0a, 0x10, 0x45, 0x6e, - 0x63, 0x72, 0x79, 0x70, 0x74, 0x56, 0x31, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, - 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x56, - 0x31, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x1a, 0x8c, 0x01, 0x0a, 0x07, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, - 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x6d, 0x74, 0x70, - 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, - 0x65, 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, - 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, - 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0xf4, 0x02, 0x0a, 0x0f, 0x45, 0x6e, - 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, - 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x1a, 0x92, 0x02, 0x0a, 0x08, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, - 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x3b, 0x0a, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x6d, 0x74, 0x70, - 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, - 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x6b, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x12, 0x3f, 0x0a, 0x09, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x69, - 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x52, 0x09, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x6d, - 0x61, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, - 0x48, 0x6d, 0x61, 0x63, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0xcb, 0x01, 0x0a, 0x10, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x56, 0x32, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, - 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, - 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x56, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x73, 0x1a, 0x6b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, - 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, - 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x22, 0x87, - 0x01, 0x0a, 0x12, 0x53, 0x65, 0x6c, 0x66, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, - 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x1a, 0x8c, 0x01, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x44, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x09, 0x72, + 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0xf4, 0x02, 0x0a, 0x0f, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x72, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x09, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x1a, 0x92, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x06, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x3b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x1a, 0x6b, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x3f, + 0x0a, 0x09, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, + 0x74, 0x65, 0x78, 0x74, 0x52, 0x09, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x12, + 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x48, 0x6d, 0x61, 0x63, + 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xcb, 0x01, 0x0a, + 0x10, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x56, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x4a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x56, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x1a, 0x6b, 0x0a, + 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x22, 0x87, 0x01, 0x0a, 0x12, 0x53, 0x65, 0x6c, 0x66, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x73, 0x1a, 0x23, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, - 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xbc, 0x02, 0x0a, 0x13, 0x53, 0x65, 0x6c, - 0x66, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x50, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, + 0x74, 0x12, 0x4c, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x66, 0x45, - 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x73, 0x1a, 0xd2, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x54, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x3a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x1a, + 0x23, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xbc, 0x02, 0x0a, 0x13, 0x53, 0x65, 0x6c, 0x66, 0x45, 0x6e, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x09, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x32, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x66, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x06, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x3b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x1a, 0x27, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1c, 0x0a, - 0x09, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x09, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x42, 0x0a, 0x0a, 0x08, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x87, 0x01, 0x0a, 0x12, 0x53, 0x65, 0x6c, 0x66, - 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, - 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x30, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x66, 0x44, 0x65, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x1a, 0x23, 0x0a, 0x07, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x22, 0x4e, 0x0a, 0x2c, 0x47, 0x65, 0x74, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, - 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x49, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x22, 0x98, 0x02, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x76, 0x69, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x45, 0x0a, 0x07, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x6d, 0x74, - 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x73, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x31, 0x2e, - 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x12, 0x4a, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, - 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, - 0x65, 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x4e, 0x73, 0x12, 0x4f, 0x0a, 0x0d, 0x63, - 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x65, - 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0c, - 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x82, 0x01, 0x0a, - 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, - 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x22, 0xcf, 0x01, 0x0a, 0x12, 0x53, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x78, 0x6d, 0x74, - 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x08, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x1a, 0x6b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x70, - 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x22, 0xf0, 0x02, 0x0a, 0x13, 0x53, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x76, 0x69, - 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x09, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, - 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x1a, 0x86, 0x02, - 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x06, 0x72, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x78, 0x6d, 0x74, + 0x6e, 0x73, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x1a, 0xd2, + 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x06, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x78, 0x6d, + 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x66, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, + 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x12, 0x3b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x27, + 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x6e, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x87, 0x01, 0x0a, 0x12, 0x53, 0x65, 0x6c, 0x66, 0x44, 0x65, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x08, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6c, 0x66, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x08, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x1a, 0x23, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x4e, 0x0a, + 0x2c, 0x47, 0x65, 0x74, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, + 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x22, 0x98, 0x02, + 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x45, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x49, + 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x4a, 0x0a, 0x09, + 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x09, 0x72, + 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x4e, 0x73, 0x12, 0x4f, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x73, 0x65, + 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, + 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x74, 0x50, 0x72, + 0x6f, 0x6f, 0x66, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x73, + 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0x82, 0x01, 0x0a, 0x14, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x50, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, + 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xcf, 0x01, + 0x0a, 0x12, 0x53, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, + 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x61, + 0x76, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x73, 0x1a, 0x6b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, + 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x70, + 0x69, 0x63, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, + 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, + 0xf0, 0x02, 0x0a, 0x13, 0x53, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, - 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x12, 0x3b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x23, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x5b, 0x0a, - 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x50, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, - 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0c, 0x63, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x51, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x26, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x4e, 0x73, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x22, 0x70, 0x0a, 0x1a, 0x53, 0x61, 0x76, - 0x65, 0x56, 0x31, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x52, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, - 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0d, 0x63, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x1d, 0x0a, 0x1b, 0x53, - 0x61, 0x76, 0x65, 0x56, 0x31, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6e, 0x0a, 0x18, 0x47, 0x65, - 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0d, 0x63, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x41, 0x0a, 0x18, 0x47, 0x65, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x09, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x1a, 0x86, 0x02, 0x0a, 0x08, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, + 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x61, + 0x76, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x3b, 0x0a, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x6d, + 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x5b, 0x0a, 0x07, 0x53, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x12, 0x50, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x51, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0c, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, + 0x73, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x22, 0x70, 0x0a, 0x1a, 0x53, 0x61, 0x76, 0x65, 0x56, 0x31, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x52, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x1d, 0x0a, 0x1b, 0x53, 0x61, 0x76, 0x65, 0x56, + 0x31, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6e, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, + 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x41, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xea, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xea, 0x01, - 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3e, 0x2e, 0x78, 0x6d, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, + 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, + 0x75, 0x0a, 0x0e, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x1f, 0x0a, 0x1b, 0x4b, 0x45, 0x59, 0x53, 0x54, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x54, + 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x4b, 0x45, 0x59, 0x53, 0x54, 0x4f, 0x52, 0x45, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x49, + 0x5a, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x4b, 0x45, 0x59, 0x53, 0x54, 0x4f, 0x52, + 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, + 0x49, 0x5a, 0x45, 0x44, 0x10, 0x02, 0x22, 0x5c, 0x0a, 0x13, 0x49, 0x6e, 0x69, 0x74, 0x4b, 0x65, + 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, + 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x78, 0x6d, 0x74, 0x70, + 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, + 0x6c, 0x65, 0x56, 0x31, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x42, 0x08, 0x0a, 0x06, 0x62, 0x75, + 0x6e, 0x64, 0x6c, 0x65, 0x22, 0x51, 0x0a, 0x14, 0x49, 0x6e, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4b, 0x65, 0x79, - 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x22, 0x75, 0x0a, 0x0e, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x1b, 0x4b, 0x45, 0x59, 0x53, 0x54, 0x4f, 0x52, - 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, - 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x4b, 0x45, 0x59, 0x53, 0x54, 0x4f, - 0x52, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x49, 0x4e, 0x49, 0x54, - 0x49, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x4b, 0x45, 0x59, - 0x53, 0x54, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x4e, 0x49, - 0x54, 0x49, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x02, 0x22, 0x5c, 0x0a, 0x13, 0x49, 0x6e, - 0x69, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x3b, 0x0a, 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, - 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x56, 0x31, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x42, 0x08, - 0x0a, 0x06, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x22, 0x51, 0x0a, 0x14, 0x49, 0x6e, 0x69, 0x74, - 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x39, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x23, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x7f, 0x0a, 0x11, 0x53, - 0x69, 0x67, 0x6e, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0c, 0x69, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, - 0x52, 0x0b, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, - 0x0c, 0x70, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x0b, 0x70, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x42, 0x08, 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x22, 0x50, 0x0a, 0x14, - 0x47, 0x65, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, - 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, - 0x62, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x54, 0x79, 0x70, 0x65, 0x22, 0x37, - 0x0a, 0x15, 0x47, 0x65, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, - 0x72, 0x75, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6c, 0x61, - 0x73, 0x74, 0x52, 0x75, 0x6e, 0x4e, 0x73, 0x22, 0x6f, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x52, 0x65, - 0x66, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, - 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x1d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x54, 0x79, 0x70, 0x65, 0x52, - 0x07, 0x6a, 0x6f, 0x62, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, - 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6c, - 0x61, 0x73, 0x74, 0x52, 0x75, 0x6e, 0x4e, 0x73, 0x22, 0x17, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x52, - 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0xc8, 0x02, 0x0a, 0x08, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x4d, 0x61, 0x70, 0x12, 0x42, - 0x0a, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, - 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x4d, 0x61, 0x70, 0x2e, 0x54, - 0x6f, 0x70, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x74, 0x6f, 0x70, 0x69, - 0x63, 0x73, 0x1a, 0x92, 0x01, 0x0a, 0x09, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, - 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x4e, 0x73, 0x12, - 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x43, 0x0a, 0x0a, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x49, - 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x31, 0x52, 0x0a, 0x69, 0x6e, 0x76, - 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x63, 0x0a, 0x0b, 0x54, 0x6f, 0x70, 0x69, 0x63, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, - 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x54, - 0x6f, 0x70, 0x69, 0x63, 0x4d, 0x61, 0x70, 0x2e, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x44, 0x61, 0x74, - 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x38, 0x0a, 0x1e, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, - 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, - 0x0a, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, - 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x22, 0xd5, 0x03, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, - 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x09, 0x68, 0x6d, - 0x61, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x08, 0x68, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x1a, 0x6c, 0x0a, 0x0b, - 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, 0x42, 0x0a, 0x1e, 0x74, - 0x68, 0x69, 0x72, 0x74, 0x79, 0x5f, 0x64, 0x61, 0x79, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, - 0x73, 0x5f, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x1a, 0x74, 0x68, 0x69, 0x72, 0x74, 0x79, 0x44, 0x61, 0x79, 0x50, 0x65, - 0x72, 0x69, 0x6f, 0x64, 0x73, 0x53, 0x69, 0x6e, 0x63, 0x65, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, - 0x19, 0x0a, 0x08, 0x68, 0x6d, 0x61, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x07, 0x68, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x1a, 0x65, 0x0a, 0x08, 0x48, 0x6d, - 0x61, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x59, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, - 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x6d, 0x61, - 0x63, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x6d, - 0x61, 0x63, 0x4b, 0x65, 0x79, 0x44, 0x61, 0x74, 0x61, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x73, 0x1a, 0x7b, 0x0a, 0x0d, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x45, 0x6e, 0x74, + 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x7f, 0x0a, 0x11, 0x53, 0x69, 0x67, 0x6e, 0x44, + 0x69, 0x67, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x69, + 0x67, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0c, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0b, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0c, 0x70, 0x72, 0x65, + 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, + 0x00, 0x52, 0x0b, 0x70, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x42, 0x08, + 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x22, 0x50, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x38, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x54, 0x79, 0x70, 0x65, 0x22, 0x37, 0x0a, 0x15, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x5f, + 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x52, 0x75, + 0x6e, 0x4e, 0x73, 0x22, 0x6f, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x52, 0x65, 0x66, 0x65, 0x73, 0x68, + 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x08, 0x6a, 0x6f, + 0x62, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x4a, 0x6f, 0x62, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x6a, 0x6f, 0x62, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x72, 0x75, 0x6e, + 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x52, + 0x75, 0x6e, 0x4e, 0x73, 0x22, 0x17, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, + 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc8, 0x02, + 0x0a, 0x08, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x4d, 0x61, 0x70, 0x12, 0x42, 0x0a, 0x06, 0x74, 0x6f, + 0x70, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x4d, 0x61, 0x70, 0x2e, 0x54, 0x6f, 0x70, 0x69, 0x63, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x1a, 0x92, + 0x01, 0x0a, 0x09, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x4e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x70, + 0x65, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x43, + 0x0a, 0x0a, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x31, 0x52, 0x0a, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x1a, 0x63, 0x0a, 0x0b, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x54, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, - 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x6d, 0x61, 0x63, 0x4b, - 0x65, 0x79, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x2a, 0x68, - 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x45, - 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x45, 0x52, 0x52, 0x4f, 0x52, - 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x49, 0x4e, - 0x50, 0x55, 0x54, 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, - 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x49, 0x4e, 0x47, 0x5f, - 0x50, 0x52, 0x45, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x2a, 0x70, 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x17, 0x0a, - 0x13, 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x46, 0x52, 0x45, 0x53, - 0x48, 0x5f, 0x56, 0x31, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x52, 0x45, 0x46, 0x52, 0x45, 0x53, 0x48, 0x5f, 0x56, 0x32, 0x10, 0x02, 0x12, - 0x19, 0x0a, 0x15, 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x46, 0x52, - 0x45, 0x53, 0x48, 0x5f, 0x50, 0x50, 0x50, 0x50, 0x10, 0x03, 0x42, 0xc4, 0x01, 0x0a, 0x18, 0x63, - 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0d, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, - 0x70, 0x69, 0x2f, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x58, 0x4b, 0x58, 0xaa, 0x02, 0x13, 0x58, 0x6d, - 0x74, 0x70, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x41, 0x70, 0x69, 0x2e, 0x56, - 0x31, 0xca, 0x02, 0x13, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x1f, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4b, - 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, - 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x58, 0x6d, 0x74, 0x70, - 0x3a, 0x3a, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, - 0x31, 0x4a, 0x81, 0x4c, 0x0a, 0x07, 0x12, 0x05, 0x01, 0x00, 0xbb, 0x02, 0x01, 0x0a, 0x2f, 0x0a, - 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x25, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, - 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x0a, 0x0a, 0x08, - 0x0a, 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, 0x1d, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, - 0x05, 0x00, 0x2b, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x01, 0x12, 0x03, 0x06, 0x00, 0x37, 0x0a, 0x09, - 0x0a, 0x02, 0x03, 0x02, 0x12, 0x03, 0x07, 0x00, 0x2b, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x03, 0x12, - 0x03, 0x08, 0x00, 0x2c, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x04, 0x12, 0x03, 0x09, 0x00, 0x2b, 0x0a, - 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x0b, 0x00, 0x42, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, - 0x03, 0x0b, 0x00, 0x42, 0x0a, 0x44, 0x0a, 0x02, 0x05, 0x00, 0x12, 0x04, 0x0f, 0x00, 0x13, 0x01, - 0x1a, 0x38, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x73, - 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x63, 0x6f, - 0x64, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x20, 0x41, 0x50, 0x49, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x00, - 0x01, 0x12, 0x03, 0x0f, 0x05, 0x0e, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x00, 0x12, 0x03, - 0x10, 0x02, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x10, 0x02, - 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x10, 0x1b, 0x1c, 0x0a, - 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x01, 0x12, 0x03, 0x11, 0x02, 0x1f, 0x0a, 0x0c, 0x0a, 0x05, - 0x05, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x11, 0x02, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, - 0x02, 0x01, 0x02, 0x12, 0x03, 0x11, 0x1d, 0x1e, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x02, - 0x12, 0x03, 0x12, 0x02, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, - 0x12, 0x02, 0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x12, 0x22, - 0x23, 0x0a, 0x3c, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x16, 0x00, 0x19, 0x01, 0x1a, 0x30, 0x20, - 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x66, 0x6f, - 0x72, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x41, 0x50, 0x49, 0x0a, 0x0a, - 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x16, 0x08, 0x15, 0x0a, 0x0b, 0x0a, 0x04, 0x04, - 0x00, 0x02, 0x00, 0x12, 0x03, 0x17, 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, - 0x05, 0x12, 0x03, 0x17, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, - 0x03, 0x17, 0x09, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x17, - 0x13, 0x14, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x18, 0x02, 0x15, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, 0x18, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x18, 0x0c, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x18, 0x13, 0x14, 0x0a, 0x42, 0x0a, 0x02, 0x04, 0x01, 0x12, - 0x04, 0x1c, 0x00, 0x26, 0x01, 0x1a, 0x36, 0x20, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x20, - 0x61, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x73, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x58, 0x33, 0x44, 0x48, 0x20, 0x6b, - 0x65, 0x79, 0x20, 0x61, 0x67, 0x72, 0x65, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0a, 0x0a, - 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x1c, 0x08, 0x18, 0x0a, 0x2b, 0x0a, 0x04, 0x04, 0x01, 0x03, - 0x00, 0x12, 0x04, 0x1e, 0x02, 0x23, 0x03, 0x1a, 0x1d, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, - 0x6c, 0x65, 0x20, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x03, 0x00, 0x01, 0x12, - 0x03, 0x1e, 0x0a, 0x11, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, - 0x1f, 0x04, 0x31, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, - 0x1f, 0x04, 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, - 0x1f, 0x25, 0x2c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, - 0x1f, 0x2f, 0x30, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x20, - 0x04, 0x38, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, 0x20, - 0x04, 0x29, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x20, - 0x2a, 0x33, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x20, - 0x36, 0x37, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x12, 0x03, 0x21, 0x04, - 0x1b, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x21, 0x04, - 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x21, 0x0a, - 0x16, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x21, 0x19, - 0x1a, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x03, 0x12, 0x03, 0x22, 0x04, 0x17, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x03, 0x05, 0x12, 0x03, 0x22, 0x04, 0x08, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x22, 0x09, 0x12, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x03, 0x03, 0x12, 0x03, 0x22, 0x15, 0x16, - 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x25, 0x02, 0x20, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x01, 0x02, 0x00, 0x04, 0x12, 0x03, 0x25, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, 0x25, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, - 0x00, 0x01, 0x12, 0x03, 0x25, 0x13, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, - 0x12, 0x03, 0x25, 0x1e, 0x1f, 0x0a, 0x42, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x29, 0x00, 0x38, - 0x01, 0x1a, 0x36, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x79, 0x70, - 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x20, 0x56, 0x31, 0x20, 0x61, 0x6e, - 0x64, 0x20, 0x56, 0x32, 0x20, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, - 0x12, 0x03, 0x29, 0x08, 0x17, 0x0a, 0x2c, 0x0a, 0x04, 0x04, 0x02, 0x03, 0x00, 0x12, 0x04, 0x2b, - 0x02, 0x35, 0x03, 0x1a, 0x1e, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x64, - 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x03, 0x00, 0x01, 0x12, 0x03, 0x2b, 0x0a, - 0x12, 0x0a, 0x35, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x03, 0x00, 0x12, 0x04, 0x2d, 0x04, 0x2f, - 0x05, 0x1a, 0x25, 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, - 0x03, 0x00, 0x01, 0x12, 0x03, 0x2d, 0x0c, 0x13, 0x0a, 0x0f, 0x0a, 0x08, 0x04, 0x02, 0x03, 0x00, - 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x2e, 0x06, 0x1a, 0x0a, 0x10, 0x0a, 0x09, 0x04, 0x02, 0x03, - 0x00, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x2e, 0x06, 0x0b, 0x0a, 0x10, 0x0a, 0x09, 0x04, - 0x02, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2e, 0x0c, 0x15, 0x0a, 0x10, 0x0a, - 0x09, 0x04, 0x02, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2e, 0x18, 0x19, 0x0a, - 0x0e, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x08, 0x00, 0x12, 0x04, 0x31, 0x04, 0x34, 0x05, 0x0a, - 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x08, 0x00, 0x01, 0x12, 0x03, 0x31, 0x0a, 0x12, 0x0a, - 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x32, 0x06, 0x19, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x32, 0x06, 0x0d, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x32, 0x0e, 0x14, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x32, 0x17, 0x18, 0x0a, 0x0d, - 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x33, 0x06, 0x1e, 0x0a, 0x0e, 0x0a, - 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, 0x33, 0x06, 0x13, 0x0a, 0x0e, 0x0a, - 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x33, 0x14, 0x19, 0x0a, 0x0e, 0x0a, - 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x33, 0x1c, 0x1d, 0x0a, 0x0b, 0x0a, - 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x37, 0x02, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, - 0x02, 0x00, 0x04, 0x12, 0x03, 0x37, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, - 0x06, 0x12, 0x03, 0x37, 0x0b, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, - 0x03, 0x37, 0x14, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x37, - 0x20, 0x21, 0x0a, 0x4a, 0x0a, 0x02, 0x04, 0x03, 0x12, 0x04, 0x3b, 0x00, 0x44, 0x01, 0x1a, 0x3e, - 0x20, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, - 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x20, 0x75, 0x73, 0x69, - 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, - 0x74, 0x65, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x0a, 0x0a, 0x0a, - 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x03, 0x3b, 0x08, 0x18, 0x0a, 0x2b, 0x0a, 0x04, 0x04, 0x03, - 0x03, 0x00, 0x12, 0x04, 0x3d, 0x02, 0x41, 0x03, 0x1a, 0x1d, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, - 0x67, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x03, 0x00, 0x01, - 0x12, 0x03, 0x3d, 0x0a, 0x11, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x12, - 0x03, 0x3e, 0x04, 0x31, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, - 0x03, 0x3e, 0x04, 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, - 0x03, 0x3e, 0x25, 0x2c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, - 0x03, 0x3e, 0x2f, 0x30, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, - 0x3f, 0x04, 0x1b, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, - 0x3f, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, - 0x3f, 0x0a, 0x16, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, - 0x3f, 0x19, 0x1a, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x03, 0x00, 0x02, 0x02, 0x12, 0x03, 0x40, - 0x04, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x40, - 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x40, - 0x0b, 0x18, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x40, - 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x00, 0x12, 0x03, 0x43, 0x02, 0x20, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x04, 0x12, 0x03, 0x43, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x03, 0x02, 0x00, 0x06, 0x12, 0x03, 0x43, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x03, 0x02, 0x00, 0x01, 0x12, 0x03, 0x43, 0x13, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, - 0x00, 0x03, 0x12, 0x03, 0x43, 0x1e, 0x1f, 0x0a, 0x42, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x47, - 0x00, 0x50, 0x01, 0x1a, 0x36, 0x20, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x20, 0x61, 0x20, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x70, 0x69, 0x63, + 0x4d, 0x61, 0x70, 0x2e, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x38, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x6d, 0x61, 0x63, 0x4b, + 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x6f, + 0x70, 0x69, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x6f, 0x70, 0x69, + 0x63, 0x73, 0x22, 0xd5, 0x03, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x09, 0x68, 0x6d, 0x61, 0x63, 0x5f, 0x6b, + 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x78, 0x6d, 0x74, 0x70, + 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, + 0x68, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x1a, 0x6c, 0x0a, 0x0b, 0x48, 0x6d, 0x61, 0x63, + 0x4b, 0x65, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, 0x42, 0x0a, 0x1e, 0x74, 0x68, 0x69, 0x72, 0x74, + 0x79, 0x5f, 0x64, 0x61, 0x79, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x5f, 0x73, 0x69, + 0x6e, 0x63, 0x65, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x1a, 0x74, 0x68, 0x69, 0x72, 0x74, 0x79, 0x44, 0x61, 0x79, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, + 0x73, 0x53, 0x69, 0x6e, 0x63, 0x65, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x68, + 0x6d, 0x61, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x68, + 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x1a, 0x65, 0x0a, 0x08, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, + 0x79, 0x73, 0x12, 0x59, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, + 0x79, 0x44, 0x61, 0x74, 0x61, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x1a, 0x7b, 0x0a, + 0x0d, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x54, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x3e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe4, 0x01, 0x0a, 0x1b, 0x50, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, + 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x70, 0x12, 0x58, 0x0a, 0x07, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x78, 0x6d, + 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x63, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x70, 0x2e, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x6b, 0x0a, 0x0c, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x45, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x72, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x2a, 0x68, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1a, + 0x0a, 0x16, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, + 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x45, 0x52, + 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, + 0x5f, 0x49, 0x4e, 0x50, 0x55, 0x54, 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x45, 0x52, 0x52, 0x4f, + 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x5f, 0x4d, 0x41, 0x54, 0x43, 0x48, 0x49, + 0x4e, 0x47, 0x5f, 0x50, 0x52, 0x45, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x2a, 0x70, 0x0a, 0x07, 0x4a, + 0x6f, 0x62, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x17, 0x0a, 0x13, 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x46, + 0x52, 0x45, 0x53, 0x48, 0x5f, 0x56, 0x31, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x4a, 0x4f, 0x42, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x46, 0x52, 0x45, 0x53, 0x48, 0x5f, 0x56, 0x32, + 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, + 0x45, 0x46, 0x52, 0x45, 0x53, 0x48, 0x5f, 0x50, 0x50, 0x50, 0x50, 0x10, 0x03, 0x42, 0xc4, 0x01, + 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0d, 0x4b, 0x65, 0x79, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x58, 0x4b, 0x58, 0xaa, 0x02, + 0x13, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x41, 0x70, + 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x13, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4b, 0x65, 0x79, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x1f, 0x58, 0x6d, 0x74, + 0x70, 0x5c, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, + 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x58, + 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x41, 0x70, 0x69, + 0x3a, 0x3a, 0x56, 0x31, 0x4a, 0xa5, 0x4d, 0x0a, 0x07, 0x12, 0x05, 0x01, 0x00, 0xc1, 0x02, 0x01, + 0x0a, 0x2f, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x25, 0x20, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x65, 0x6e, 0x63, + 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, 0x1d, 0x0a, 0x09, 0x0a, 0x02, 0x03, + 0x00, 0x12, 0x03, 0x05, 0x00, 0x2b, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x01, 0x12, 0x03, 0x06, 0x00, + 0x37, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x02, 0x12, 0x03, 0x07, 0x00, 0x2b, 0x0a, 0x09, 0x0a, 0x02, + 0x03, 0x03, 0x12, 0x03, 0x08, 0x00, 0x2c, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x04, 0x12, 0x03, 0x09, + 0x00, 0x34, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x05, 0x12, 0x03, 0x0a, 0x00, 0x2b, 0x0a, 0x08, 0x0a, + 0x01, 0x08, 0x12, 0x03, 0x0c, 0x00, 0x42, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x0c, + 0x00, 0x42, 0x0a, 0x44, 0x0a, 0x02, 0x05, 0x00, 0x12, 0x04, 0x10, 0x00, 0x14, 0x01, 0x1a, 0x38, + 0x20, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x63, 0x6f, 0x64, 0x65, + 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x20, 0x41, 0x50, 0x49, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x00, 0x01, 0x12, + 0x03, 0x10, 0x05, 0x0e, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x00, 0x12, 0x03, 0x11, 0x02, + 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x11, 0x02, 0x18, 0x0a, + 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x11, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, + 0x04, 0x05, 0x00, 0x02, 0x01, 0x12, 0x03, 0x12, 0x02, 0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, + 0x02, 0x01, 0x01, 0x12, 0x03, 0x12, 0x02, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, + 0x02, 0x12, 0x03, 0x12, 0x1d, 0x1e, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x02, 0x12, 0x03, + 0x13, 0x02, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x13, 0x02, + 0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x13, 0x22, 0x23, 0x0a, + 0x3c, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x17, 0x00, 0x1a, 0x01, 0x1a, 0x30, 0x20, 0x57, 0x72, + 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x41, 0x50, 0x49, 0x0a, 0x0a, 0x0a, 0x0a, + 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x17, 0x08, 0x15, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, + 0x00, 0x12, 0x03, 0x18, 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x05, 0x12, + 0x03, 0x18, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x18, + 0x09, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x18, 0x13, 0x14, + 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x19, 0x02, 0x15, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, 0x19, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x19, 0x0c, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, + 0x01, 0x03, 0x12, 0x03, 0x19, 0x13, 0x14, 0x0a, 0x42, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x1d, + 0x00, 0x27, 0x01, 0x1a, 0x36, 0x20, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x58, 0x33, 0x44, 0x48, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x61, 0x67, 0x72, 0x65, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, - 0x04, 0x01, 0x12, 0x03, 0x47, 0x08, 0x18, 0x0a, 0x2b, 0x0a, 0x04, 0x04, 0x04, 0x03, 0x00, 0x12, - 0x04, 0x49, 0x02, 0x4d, 0x03, 0x1a, 0x1d, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, - 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x03, 0x00, 0x01, 0x12, 0x03, 0x49, - 0x0a, 0x11, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x4a, 0x04, - 0x38, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x4a, 0x04, - 0x29, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x4a, 0x2a, - 0x33, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x4a, 0x36, - 0x37, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x4b, 0x04, 0x16, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x4b, 0x04, 0x09, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x4b, 0x0a, 0x11, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x4b, 0x14, 0x15, - 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x03, 0x00, 0x02, 0x02, 0x12, 0x03, 0x4c, 0x04, 0x1b, 0x0a, - 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x4c, 0x04, 0x09, 0x0a, - 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x4c, 0x0a, 0x16, 0x0a, - 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x4c, 0x19, 0x1a, 0x0a, - 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, 0x03, 0x4f, 0x02, 0x20, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x04, 0x02, 0x00, 0x04, 0x12, 0x03, 0x4f, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, - 0x02, 0x00, 0x06, 0x12, 0x03, 0x4f, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, - 0x01, 0x12, 0x03, 0x4f, 0x13, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, - 0x03, 0x4f, 0x1e, 0x1f, 0x0a, 0x42, 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04, 0x53, 0x00, 0x63, 0x01, - 0x1a, 0x36, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, - 0x20, 0x66, 0x6f, 0x72, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x20, 0x56, 0x31, 0x20, 0x61, 0x6e, 0x64, - 0x20, 0x56, 0x32, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x05, 0x01, 0x12, - 0x03, 0x53, 0x08, 0x17, 0x0a, 0x2c, 0x0a, 0x04, 0x04, 0x05, 0x03, 0x00, 0x12, 0x04, 0x55, 0x02, - 0x60, 0x03, 0x1a, 0x1e, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x65, 0x6e, - 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x03, 0x00, 0x01, 0x12, 0x03, 0x55, 0x0a, 0x12, - 0x0a, 0x35, 0x0a, 0x06, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x12, 0x04, 0x57, 0x04, 0x5a, 0x05, - 0x1a, 0x25, 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x72, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x03, - 0x00, 0x01, 0x12, 0x03, 0x57, 0x0c, 0x13, 0x0a, 0x0f, 0x0a, 0x08, 0x04, 0x05, 0x03, 0x00, 0x03, - 0x00, 0x02, 0x00, 0x12, 0x03, 0x58, 0x06, 0x35, 0x0a, 0x10, 0x0a, 0x09, 0x04, 0x05, 0x03, 0x00, - 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x58, 0x06, 0x26, 0x0a, 0x10, 0x0a, 0x09, 0x04, 0x05, - 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x58, 0x27, 0x30, 0x0a, 0x10, 0x0a, 0x09, - 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x58, 0x33, 0x34, 0x0a, 0x0f, - 0x0a, 0x08, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x59, 0x06, 0x1c, 0x0a, - 0x10, 0x0a, 0x09, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x59, 0x06, - 0x0b, 0x0a, 0x10, 0x0a, 0x09, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, - 0x59, 0x0c, 0x17, 0x0a, 0x10, 0x0a, 0x09, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x02, 0x01, 0x03, - 0x12, 0x03, 0x59, 0x1a, 0x1b, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x05, 0x03, 0x00, 0x08, 0x00, 0x12, - 0x04, 0x5c, 0x04, 0x5f, 0x05, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x08, 0x00, 0x01, - 0x12, 0x03, 0x5c, 0x0a, 0x12, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x05, 0x03, 0x00, 0x02, 0x00, 0x12, - 0x03, 0x5d, 0x06, 0x19, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, - 0x03, 0x5d, 0x06, 0x0d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, - 0x03, 0x5d, 0x0e, 0x14, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, - 0x03, 0x5d, 0x17, 0x18, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x05, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, - 0x5e, 0x06, 0x1e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, - 0x5e, 0x06, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, - 0x5e, 0x14, 0x19, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, - 0x5e, 0x1c, 0x1d, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, 0x12, 0x03, 0x62, 0x02, 0x22, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x04, 0x12, 0x03, 0x62, 0x02, 0x0a, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x06, 0x12, 0x03, 0x62, 0x0b, 0x13, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x05, 0x02, 0x00, 0x01, 0x12, 0x03, 0x62, 0x14, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, - 0x02, 0x00, 0x03, 0x12, 0x03, 0x62, 0x20, 0x21, 0x0a, 0x4a, 0x0a, 0x02, 0x04, 0x06, 0x12, 0x04, - 0x66, 0x00, 0x6f, 0x01, 0x1a, 0x3e, 0x20, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x20, 0x61, - 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x73, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x70, 0x70, - 0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x20, 0x6b, - 0x65, 0x79, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x06, 0x01, 0x12, 0x03, 0x66, 0x08, 0x18, - 0x0a, 0x2b, 0x0a, 0x04, 0x04, 0x06, 0x03, 0x00, 0x12, 0x04, 0x68, 0x02, 0x6c, 0x03, 0x1a, 0x1d, - 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x06, 0x03, 0x00, 0x01, 0x12, 0x03, 0x68, 0x0a, 0x11, 0x0a, 0x0d, 0x0a, 0x06, 0x04, - 0x06, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x69, 0x04, 0x16, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, - 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x69, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, - 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x69, 0x0a, 0x11, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, - 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x69, 0x14, 0x15, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x06, - 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x6a, 0x04, 0x1b, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, - 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x6a, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, - 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x6a, 0x0a, 0x16, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, - 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x6a, 0x19, 0x1a, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x06, 0x03, - 0x00, 0x02, 0x02, 0x12, 0x03, 0x6b, 0x04, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, - 0x02, 0x02, 0x05, 0x12, 0x03, 0x6b, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, - 0x02, 0x02, 0x01, 0x12, 0x03, 0x6b, 0x0b, 0x18, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, - 0x02, 0x02, 0x03, 0x12, 0x03, 0x6b, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x00, - 0x12, 0x03, 0x6e, 0x02, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x04, 0x12, 0x03, - 0x6e, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x06, 0x12, 0x03, 0x6e, 0x0b, - 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x01, 0x12, 0x03, 0x6e, 0x13, 0x1b, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x03, 0x12, 0x03, 0x6e, 0x1e, 0x1f, 0x0a, 0x2c, 0x0a, - 0x02, 0x04, 0x07, 0x12, 0x04, 0x72, 0x00, 0x79, 0x01, 0x1a, 0x20, 0x20, 0x45, 0x6e, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, 0x6f, - 0x72, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x6c, 0x66, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, - 0x07, 0x01, 0x12, 0x03, 0x72, 0x08, 0x1a, 0x0a, 0x1c, 0x0a, 0x04, 0x04, 0x07, 0x03, 0x00, 0x12, - 0x04, 0x74, 0x02, 0x76, 0x03, 0x1a, 0x0e, 0x20, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x03, 0x00, 0x01, 0x12, 0x03, - 0x74, 0x0a, 0x11, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x75, - 0x04, 0x16, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x75, - 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x75, - 0x0a, 0x11, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x75, - 0x14, 0x15, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x00, 0x12, 0x03, 0x78, 0x02, 0x20, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x04, 0x12, 0x03, 0x78, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x07, 0x02, 0x00, 0x06, 0x12, 0x03, 0x78, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x07, 0x02, 0x00, 0x01, 0x12, 0x03, 0x78, 0x13, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, - 0x00, 0x03, 0x12, 0x03, 0x78, 0x1e, 0x1f, 0x0a, 0x33, 0x0a, 0x02, 0x04, 0x08, 0x12, 0x05, 0x7c, - 0x00, 0x8b, 0x01, 0x01, 0x1a, 0x26, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, - 0x74, 0x79, 0x70, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x53, 0x65, 0x6c, 0x66, 0x45, 0x6e, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, - 0x04, 0x08, 0x01, 0x12, 0x03, 0x7c, 0x08, 0x1b, 0x0a, 0x1e, 0x0a, 0x04, 0x04, 0x08, 0x03, 0x00, - 0x12, 0x05, 0x7e, 0x02, 0x88, 0x01, 0x03, 0x1a, 0x0f, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x03, 0x00, - 0x01, 0x12, 0x03, 0x7e, 0x0a, 0x12, 0x0a, 0x24, 0x0a, 0x06, 0x04, 0x08, 0x03, 0x00, 0x03, 0x00, - 0x12, 0x06, 0x80, 0x01, 0x04, 0x82, 0x01, 0x05, 0x1a, 0x12, 0x20, 0x53, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x0a, 0x0a, 0x0f, 0x0a, 0x07, - 0x04, 0x08, 0x03, 0x00, 0x03, 0x00, 0x01, 0x12, 0x04, 0x80, 0x01, 0x0c, 0x13, 0x0a, 0x10, 0x0a, - 0x08, 0x04, 0x08, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0x81, 0x01, 0x06, 0x1a, 0x0a, - 0x11, 0x0a, 0x09, 0x04, 0x08, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0x81, 0x01, - 0x06, 0x0b, 0x0a, 0x11, 0x0a, 0x09, 0x04, 0x08, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, - 0x04, 0x81, 0x01, 0x0c, 0x15, 0x0a, 0x11, 0x0a, 0x09, 0x04, 0x08, 0x03, 0x00, 0x03, 0x00, 0x02, - 0x00, 0x03, 0x12, 0x04, 0x81, 0x01, 0x18, 0x19, 0x0a, 0x10, 0x0a, 0x06, 0x04, 0x08, 0x03, 0x00, - 0x08, 0x00, 0x12, 0x06, 0x84, 0x01, 0x04, 0x87, 0x01, 0x05, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x08, - 0x03, 0x00, 0x08, 0x00, 0x01, 0x12, 0x04, 0x84, 0x01, 0x0a, 0x12, 0x0a, 0x0e, 0x0a, 0x06, 0x04, - 0x08, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0x85, 0x01, 0x06, 0x19, 0x0a, 0x0f, 0x0a, 0x07, 0x04, - 0x08, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x04, 0x85, 0x01, 0x06, 0x0d, 0x0a, 0x0f, 0x0a, 0x07, - 0x04, 0x08, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0x85, 0x01, 0x0e, 0x14, 0x0a, 0x0f, 0x0a, - 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0x85, 0x01, 0x17, 0x18, 0x0a, 0x0e, - 0x0a, 0x06, 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, 0x86, 0x01, 0x06, 0x1e, 0x0a, 0x0f, - 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x06, 0x12, 0x04, 0x86, 0x01, 0x06, 0x13, 0x0a, - 0x0f, 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0x86, 0x01, 0x14, 0x19, - 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, 0x86, 0x01, 0x1c, - 0x1d, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x08, 0x02, 0x00, 0x12, 0x04, 0x8a, 0x01, 0x02, 0x22, 0x0a, - 0x0d, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x04, 0x12, 0x04, 0x8a, 0x01, 0x02, 0x0a, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x06, 0x12, 0x04, 0x8a, 0x01, 0x0b, 0x13, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x08, 0x02, 0x00, 0x01, 0x12, 0x04, 0x8a, 0x01, 0x14, 0x1d, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x08, 0x02, 0x00, 0x03, 0x12, 0x04, 0x8a, 0x01, 0x20, 0x21, 0x0a, 0x22, 0x0a, 0x02, 0x04, - 0x09, 0x12, 0x06, 0x8e, 0x01, 0x00, 0x95, 0x01, 0x01, 0x1a, 0x14, 0x20, 0x53, 0x65, 0x6c, 0x66, - 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, - 0x0b, 0x0a, 0x03, 0x04, 0x09, 0x01, 0x12, 0x04, 0x8e, 0x01, 0x08, 0x1a, 0x0a, 0x1e, 0x0a, 0x04, - 0x04, 0x09, 0x03, 0x00, 0x12, 0x06, 0x90, 0x01, 0x02, 0x92, 0x01, 0x03, 0x1a, 0x0e, 0x20, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x09, 0x03, 0x00, 0x01, 0x12, 0x04, 0x90, 0x01, 0x0a, 0x11, 0x0a, 0x0e, 0x0a, 0x06, 0x04, - 0x09, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0x91, 0x01, 0x04, 0x16, 0x0a, 0x0f, 0x0a, 0x07, 0x04, - 0x09, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0x91, 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, - 0x04, 0x09, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0x91, 0x01, 0x0a, 0x11, 0x0a, 0x0f, 0x0a, - 0x07, 0x04, 0x09, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0x91, 0x01, 0x14, 0x15, 0x0a, 0x0c, - 0x0a, 0x04, 0x04, 0x09, 0x02, 0x00, 0x12, 0x04, 0x94, 0x01, 0x02, 0x20, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x09, 0x02, 0x00, 0x04, 0x12, 0x04, 0x94, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, - 0x09, 0x02, 0x00, 0x06, 0x12, 0x04, 0x94, 0x01, 0x0b, 0x12, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x09, - 0x02, 0x00, 0x01, 0x12, 0x04, 0x94, 0x01, 0x13, 0x1b, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x09, 0x02, - 0x00, 0x03, 0x12, 0x04, 0x94, 0x01, 0x1e, 0x1f, 0x0a, 0x3c, 0x0a, 0x02, 0x04, 0x0a, 0x12, 0x06, - 0x98, 0x01, 0x00, 0x9a, 0x01, 0x01, 0x1a, 0x2e, 0x20, 0x47, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, - 0x6e, 0x63, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0a, 0x01, 0x12, 0x04, 0x98, - 0x01, 0x08, 0x34, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0a, 0x02, 0x00, 0x12, 0x04, 0x99, 0x01, 0x02, - 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x05, 0x12, 0x04, 0x99, 0x01, 0x02, 0x08, - 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x01, 0x12, 0x04, 0x99, 0x01, 0x09, 0x13, 0x0a, - 0x0d, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x03, 0x12, 0x04, 0x99, 0x01, 0x16, 0x17, 0x0a, 0x5d, - 0x0a, 0x02, 0x04, 0x0b, 0x12, 0x06, 0x9d, 0x01, 0x00, 0xa2, 0x01, 0x01, 0x1a, 0x4f, 0x20, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x69, 0x6e, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x0a, 0x0a, 0x0b, 0x0a, - 0x03, 0x04, 0x0b, 0x01, 0x12, 0x04, 0x9d, 0x01, 0x08, 0x1b, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0b, - 0x02, 0x00, 0x12, 0x04, 0x9e, 0x01, 0x02, 0x39, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, - 0x06, 0x12, 0x04, 0x9e, 0x01, 0x02, 0x2c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x01, - 0x12, 0x04, 0x9e, 0x01, 0x2d, 0x34, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x03, 0x12, - 0x04, 0x9e, 0x01, 0x37, 0x38, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x01, 0x12, 0x04, 0x9f, - 0x01, 0x02, 0x3c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x01, 0x06, 0x12, 0x04, 0x9f, 0x01, - 0x02, 0x2d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x01, 0x01, 0x12, 0x04, 0x9f, 0x01, 0x2e, - 0x37, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x01, 0x03, 0x12, 0x04, 0x9f, 0x01, 0x3a, 0x3b, - 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x02, 0x12, 0x04, 0xa0, 0x01, 0x02, 0x18, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x02, 0x05, 0x12, 0x04, 0xa0, 0x01, 0x02, 0x08, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x0b, 0x02, 0x02, 0x01, 0x12, 0x04, 0xa0, 0x01, 0x09, 0x13, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x0b, 0x02, 0x02, 0x03, 0x12, 0x04, 0xa0, 0x01, 0x16, 0x17, 0x0a, 0x0c, 0x0a, 0x04, 0x04, - 0x0b, 0x02, 0x03, 0x12, 0x04, 0xa1, 0x01, 0x02, 0x3e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, - 0x03, 0x06, 0x12, 0x04, 0xa1, 0x01, 0x02, 0x2b, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x03, - 0x01, 0x12, 0x04, 0xa1, 0x01, 0x2c, 0x39, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x03, 0x03, - 0x12, 0x04, 0xa1, 0x01, 0x3c, 0x3d, 0x0a, 0x31, 0x0a, 0x02, 0x04, 0x0c, 0x12, 0x06, 0xa5, 0x01, - 0x00, 0xa8, 0x01, 0x01, 0x1a, 0x23, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, - 0x74, 0x6f, 0x20, 0x61, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0c, 0x01, - 0x12, 0x04, 0xa5, 0x01, 0x08, 0x1c, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0c, 0x02, 0x00, 0x12, 0x04, - 0xa6, 0x01, 0x02, 0x3f, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x06, 0x12, 0x04, 0xa6, - 0x01, 0x02, 0x2d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x01, 0x12, 0x04, 0xa6, 0x01, - 0x2e, 0x3a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x03, 0x12, 0x04, 0xa6, 0x01, 0x3d, - 0x3e, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0c, 0x02, 0x01, 0x12, 0x04, 0xa7, 0x01, 0x02, 0x14, 0x0a, - 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x01, 0x05, 0x12, 0x04, 0xa7, 0x01, 0x02, 0x07, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x01, 0x01, 0x12, 0x04, 0xa7, 0x01, 0x08, 0x0f, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x0c, 0x02, 0x01, 0x03, 0x12, 0x04, 0xa7, 0x01, 0x12, 0x13, 0x0a, 0x4a, 0x0a, 0x02, - 0x04, 0x0d, 0x12, 0x06, 0xab, 0x01, 0x00, 0xb4, 0x01, 0x01, 0x1a, 0x3c, 0x20, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x62, - 0x61, 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x20, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4b, - 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0d, 0x01, 0x12, - 0x04, 0xab, 0x01, 0x08, 0x1a, 0x0a, 0x2e, 0x0a, 0x04, 0x04, 0x0d, 0x03, 0x00, 0x12, 0x06, 0xad, - 0x01, 0x02, 0xb1, 0x01, 0x03, 0x1a, 0x1e, 0x20, 0x4d, 0x69, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x20, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x20, 0x73, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0d, 0x03, 0x00, 0x01, 0x12, 0x04, - 0xad, 0x01, 0x0a, 0x11, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0d, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, - 0xae, 0x01, 0x04, 0x1d, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, - 0x04, 0xae, 0x01, 0x04, 0x0a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, 0x03, 0x00, 0x02, 0x00, 0x01, - 0x12, 0x04, 0xae, 0x01, 0x0b, 0x18, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, 0x03, 0x00, 0x02, 0x00, - 0x03, 0x12, 0x04, 0xae, 0x01, 0x1b, 0x1c, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0d, 0x03, 0x00, 0x02, - 0x01, 0x12, 0x04, 0xaf, 0x01, 0x04, 0x1c, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, 0x03, 0x00, 0x02, - 0x01, 0x05, 0x12, 0x04, 0xaf, 0x01, 0x04, 0x0a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, 0x03, 0x00, - 0x02, 0x01, 0x01, 0x12, 0x04, 0xaf, 0x01, 0x0b, 0x17, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, 0x03, - 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, 0xaf, 0x01, 0x1a, 0x1b, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0d, - 0x03, 0x00, 0x02, 0x02, 0x12, 0x04, 0xb0, 0x01, 0x04, 0x16, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, - 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, 0x04, 0xb0, 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, - 0x0d, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x04, 0xb0, 0x01, 0x0a, 0x11, 0x0a, 0x0f, 0x0a, 0x07, - 0x04, 0x0d, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, 0x04, 0xb0, 0x01, 0x14, 0x15, 0x0a, 0x0c, 0x0a, - 0x04, 0x04, 0x0d, 0x02, 0x00, 0x12, 0x04, 0xb3, 0x01, 0x02, 0x20, 0x0a, 0x0d, 0x0a, 0x05, 0x04, - 0x0d, 0x02, 0x00, 0x04, 0x12, 0x04, 0xb3, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0d, - 0x02, 0x00, 0x06, 0x12, 0x04, 0xb3, 0x01, 0x0b, 0x12, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0d, 0x02, - 0x00, 0x01, 0x12, 0x04, 0xb3, 0x01, 0x13, 0x1b, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x00, - 0x03, 0x12, 0x04, 0xb3, 0x01, 0x1e, 0x1f, 0x0a, 0x30, 0x0a, 0x02, 0x04, 0x0e, 0x12, 0x06, 0xb7, - 0x01, 0x00, 0xc6, 0x01, 0x01, 0x1a, 0x22, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x53, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0e, 0x01, - 0x12, 0x04, 0xb7, 0x01, 0x08, 0x1b, 0x0a, 0x23, 0x0a, 0x04, 0x04, 0x0e, 0x03, 0x00, 0x12, 0x06, - 0xb9, 0x01, 0x02, 0xc3, 0x01, 0x03, 0x1a, 0x13, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, - 0x65, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, - 0x0e, 0x03, 0x00, 0x01, 0x12, 0x04, 0xb9, 0x01, 0x0a, 0x12, 0x0a, 0x37, 0x0a, 0x06, 0x04, 0x0e, - 0x03, 0x00, 0x03, 0x00, 0x12, 0x06, 0xbb, 0x01, 0x04, 0xbd, 0x01, 0x05, 0x1a, 0x25, 0x20, 0x57, - 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x66, 0x6f, - 0x72, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x0a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x03, 0x00, 0x01, 0x12, 0x04, - 0xbb, 0x01, 0x0c, 0x13, 0x0a, 0x10, 0x0a, 0x08, 0x04, 0x0e, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, - 0x12, 0x04, 0xbc, 0x01, 0x06, 0x43, 0x0a, 0x11, 0x0a, 0x09, 0x04, 0x0e, 0x03, 0x00, 0x03, 0x00, - 0x02, 0x00, 0x06, 0x12, 0x04, 0xbc, 0x01, 0x06, 0x31, 0x0a, 0x11, 0x0a, 0x09, 0x04, 0x0e, 0x03, - 0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xbc, 0x01, 0x32, 0x3e, 0x0a, 0x11, 0x0a, 0x09, - 0x04, 0x0e, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0xbc, 0x01, 0x41, 0x42, 0x0a, - 0x10, 0x0a, 0x06, 0x04, 0x0e, 0x03, 0x00, 0x08, 0x00, 0x12, 0x06, 0xbf, 0x01, 0x04, 0xc2, 0x01, - 0x05, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x08, 0x00, 0x01, 0x12, 0x04, 0xbf, 0x01, - 0x0a, 0x12, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0xc0, 0x01, - 0x06, 0x19, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x04, 0xc0, - 0x01, 0x06, 0x0d, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, - 0xc0, 0x01, 0x0e, 0x14, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, - 0x04, 0xc0, 0x01, 0x17, 0x18, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x01, 0x12, - 0x04, 0xc1, 0x01, 0x06, 0x1e, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x01, 0x06, - 0x12, 0x04, 0xc1, 0x01, 0x06, 0x13, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x01, - 0x01, 0x12, 0x04, 0xc1, 0x01, 0x14, 0x19, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, - 0x01, 0x03, 0x12, 0x04, 0xc1, 0x01, 0x1c, 0x1d, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0e, 0x02, 0x00, - 0x12, 0x04, 0xc5, 0x01, 0x02, 0x22, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x04, 0x12, - 0x04, 0xc5, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x06, 0x12, 0x04, - 0xc5, 0x01, 0x0b, 0x13, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x01, 0x12, 0x04, 0xc5, - 0x01, 0x14, 0x1d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x03, 0x12, 0x04, 0xc5, 0x01, - 0x20, 0x21, 0x0a, 0x57, 0x0a, 0x02, 0x04, 0x0f, 0x12, 0x06, 0xc9, 0x01, 0x00, 0xcb, 0x01, 0x01, - 0x1a, 0x49, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, - 0x64, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, - 0x75, 0x74, 0x68, 0x20, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x58, 0x4d, 0x54, 0x50, 0x20, 0x41, 0x50, 0x49, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, - 0x0f, 0x01, 0x12, 0x04, 0xc9, 0x01, 0x08, 0x1e, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0f, 0x02, 0x00, - 0x12, 0x04, 0xca, 0x01, 0x02, 0x23, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x00, 0x04, 0x12, - 0x04, 0xca, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x00, 0x05, 0x12, 0x04, - 0xca, 0x01, 0x0b, 0x11, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x00, 0x01, 0x12, 0x04, 0xca, - 0x01, 0x12, 0x1e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x00, 0x03, 0x12, 0x04, 0xca, 0x01, - 0x21, 0x22, 0x0a, 0x70, 0x0a, 0x02, 0x04, 0x10, 0x12, 0x06, 0xcf, 0x01, 0x00, 0xd1, 0x01, 0x01, - 0x1a, 0x62, 0x20, 0x53, 0x61, 0x76, 0x65, 0x56, 0x31, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x69, 0x73, - 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, - 0x62, 0x61, 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x62, - 0x75, 0x69, 0x6c, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, - 0x6e, 0x63, 0x65, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x10, 0x01, 0x12, 0x04, 0xcf, 0x01, 0x08, - 0x22, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x10, 0x02, 0x00, 0x12, 0x04, 0xd0, 0x01, 0x02, 0x49, 0x0a, - 0x0d, 0x0a, 0x05, 0x04, 0x10, 0x02, 0x00, 0x04, 0x12, 0x04, 0xd0, 0x01, 0x02, 0x0a, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x10, 0x02, 0x00, 0x06, 0x12, 0x04, 0xd0, 0x01, 0x0b, 0x36, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x10, 0x02, 0x00, 0x01, 0x12, 0x04, 0xd0, 0x01, 0x37, 0x44, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x10, 0x02, 0x00, 0x03, 0x12, 0x04, 0xd0, 0x01, 0x47, 0x48, 0x0a, 0x3f, 0x0a, 0x02, 0x04, - 0x11, 0x12, 0x04, 0xd4, 0x01, 0x00, 0x26, 0x1a, 0x33, 0x20, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, - 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, - 0x79, 0x70, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x53, 0x61, 0x76, 0x65, 0x56, 0x31, 0x43, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, - 0x04, 0x11, 0x01, 0x12, 0x04, 0xd4, 0x01, 0x08, 0x23, 0x0a, 0x2f, 0x0a, 0x02, 0x04, 0x12, 0x12, - 0x06, 0xd7, 0x01, 0x00, 0xd9, 0x01, 0x01, 0x1a, 0x21, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x47, 0x65, 0x74, 0x56, 0x32, 0x43, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x12, - 0x01, 0x12, 0x04, 0xd7, 0x01, 0x08, 0x20, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x12, 0x02, 0x00, 0x12, - 0x04, 0xd8, 0x01, 0x02, 0x49, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x12, 0x02, 0x00, 0x04, 0x12, 0x04, - 0xd8, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x12, 0x02, 0x00, 0x06, 0x12, 0x04, 0xd8, - 0x01, 0x0b, 0x36, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x12, 0x02, 0x00, 0x01, 0x12, 0x04, 0xd8, 0x01, - 0x37, 0x44, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x12, 0x02, 0x00, 0x03, 0x12, 0x04, 0xd8, 0x01, 0x47, - 0x48, 0x0a, 0x93, 0x01, 0x0a, 0x02, 0x04, 0x13, 0x12, 0x06, 0xdd, 0x01, 0x00, 0xdf, 0x01, 0x01, - 0x1a, 0x84, 0x01, 0x20, 0x55, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x68, 0x65, 0x63, - 0x6b, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x68, 0x61, 0x73, 0x20, 0x62, 0x65, 0x65, 0x6e, 0x20, 0x73, 0x65, 0x74, 0x75, 0x70, 0x20, - 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x0a, 0x20, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x4f, 0x6e, - 0x6c, 0x79, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x4d, 0x4d, 0x20, 0x53, - 0x6e, 0x61, 0x70, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x63, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x13, 0x01, 0x12, 0x04, - 0xdd, 0x01, 0x08, 0x20, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x13, 0x02, 0x00, 0x12, 0x04, 0xde, 0x01, - 0x02, 0x1c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x13, 0x02, 0x00, 0x05, 0x12, 0x04, 0xde, 0x01, 0x02, - 0x08, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x13, 0x02, 0x00, 0x01, 0x12, 0x04, 0xde, 0x01, 0x09, 0x17, - 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x13, 0x02, 0x00, 0x03, 0x12, 0x04, 0xde, 0x01, 0x1a, 0x1b, 0x0a, - 0x34, 0x0a, 0x02, 0x04, 0x14, 0x12, 0x06, 0xe2, 0x01, 0x00, 0xeb, 0x01, 0x01, 0x1a, 0x26, 0x20, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x47, 0x65, 0x74, 0x4b, - 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x14, 0x01, 0x12, 0x04, 0xe2, 0x01, - 0x08, 0x21, 0x0a, 0x49, 0x0a, 0x04, 0x04, 0x14, 0x04, 0x00, 0x12, 0x06, 0xe4, 0x01, 0x02, 0xe8, - 0x01, 0x03, 0x1a, 0x39, 0x20, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x0a, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x14, 0x04, 0x00, 0x01, 0x12, 0x04, 0xe4, 0x01, 0x07, 0x15, 0x0a, 0x0e, 0x0a, 0x06, - 0x04, 0x14, 0x04, 0x00, 0x02, 0x00, 0x12, 0x04, 0xe5, 0x01, 0x04, 0x24, 0x0a, 0x0f, 0x0a, 0x07, - 0x04, 0x14, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xe5, 0x01, 0x04, 0x1f, 0x0a, 0x0f, 0x0a, - 0x07, 0x04, 0x14, 0x04, 0x00, 0x02, 0x00, 0x02, 0x12, 0x04, 0xe5, 0x01, 0x22, 0x23, 0x0a, 0x0e, - 0x0a, 0x06, 0x04, 0x14, 0x04, 0x00, 0x02, 0x01, 0x12, 0x04, 0xe6, 0x01, 0x04, 0x26, 0x0a, 0x0f, - 0x0a, 0x07, 0x04, 0x14, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0xe6, 0x01, 0x04, 0x21, 0x0a, - 0x0f, 0x0a, 0x07, 0x04, 0x14, 0x04, 0x00, 0x02, 0x01, 0x02, 0x12, 0x04, 0xe6, 0x01, 0x24, 0x25, - 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x14, 0x04, 0x00, 0x02, 0x02, 0x12, 0x04, 0xe7, 0x01, 0x04, 0x24, - 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x14, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, 0x04, 0xe7, 0x01, 0x04, - 0x1f, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x14, 0x04, 0x00, 0x02, 0x02, 0x02, 0x12, 0x04, 0xe7, 0x01, - 0x22, 0x23, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x14, 0x02, 0x00, 0x12, 0x04, 0xea, 0x01, 0x02, 0x1c, - 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x14, 0x02, 0x00, 0x06, 0x12, 0x04, 0xea, 0x01, 0x02, 0x10, 0x0a, - 0x0d, 0x0a, 0x05, 0x04, 0x14, 0x02, 0x00, 0x01, 0x12, 0x04, 0xea, 0x01, 0x11, 0x17, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x14, 0x02, 0x00, 0x03, 0x12, 0x04, 0xea, 0x01, 0x1a, 0x1b, 0x0a, 0x64, 0x0a, - 0x02, 0x04, 0x15, 0x12, 0x06, 0xef, 0x01, 0x00, 0xf3, 0x01, 0x01, 0x1a, 0x56, 0x20, 0x55, 0x73, - 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x77, 0x69, - 0x74, 0x68, 0x20, 0x61, 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x6b, 0x65, 0x79, - 0x20, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, - 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x15, 0x01, 0x12, 0x04, 0xef, 0x01, 0x08, 0x1b, - 0x0a, 0x0e, 0x0a, 0x04, 0x04, 0x15, 0x08, 0x00, 0x12, 0x06, 0xf0, 0x01, 0x02, 0xf2, 0x01, 0x03, - 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x15, 0x08, 0x00, 0x01, 0x12, 0x04, 0xf0, 0x01, 0x08, 0x0e, 0x0a, - 0x0c, 0x0a, 0x04, 0x04, 0x15, 0x02, 0x00, 0x12, 0x04, 0xf1, 0x01, 0x04, 0x34, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x15, 0x02, 0x00, 0x06, 0x12, 0x04, 0xf1, 0x01, 0x04, 0x2c, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x15, 0x02, 0x00, 0x01, 0x12, 0x04, 0xf1, 0x01, 0x2d, 0x2f, 0x0a, 0x0d, 0x0a, 0x05, 0x04, - 0x15, 0x02, 0x00, 0x03, 0x12, 0x04, 0xf1, 0x01, 0x32, 0x33, 0x0a, 0x42, 0x0a, 0x02, 0x04, 0x16, - 0x12, 0x06, 0xf6, 0x01, 0x00, 0xf8, 0x01, 0x01, 0x1a, 0x34, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x0a, 0x0a, 0x0b, - 0x0a, 0x03, 0x04, 0x16, 0x01, 0x12, 0x04, 0xf6, 0x01, 0x08, 0x1c, 0x0a, 0x0c, 0x0a, 0x04, 0x04, - 0x16, 0x02, 0x00, 0x12, 0x04, 0xf7, 0x01, 0x02, 0x1a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x16, 0x02, - 0x00, 0x06, 0x12, 0x04, 0xf7, 0x01, 0x02, 0x0f, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x16, 0x02, 0x00, - 0x01, 0x12, 0x04, 0xf7, 0x01, 0x10, 0x15, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x16, 0x02, 0x00, 0x03, - 0x12, 0x04, 0xf7, 0x01, 0x18, 0x19, 0x0a, 0x64, 0x0a, 0x02, 0x04, 0x17, 0x12, 0x06, 0xfc, 0x01, - 0x00, 0x82, 0x02, 0x01, 0x1a, 0x56, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x44, 0x69, 0x67, 0x65, 0x73, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, - 0x20, 0x74, 0x6f, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x20, 0x61, 0x20, 0x64, 0x69, 0x67, 0x65, 0x73, - 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x0a, 0x20, - 0x6f, 0x72, 0x20, 0x61, 0x20, 0x70, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, - 0x04, 0x17, 0x01, 0x12, 0x04, 0xfc, 0x01, 0x08, 0x19, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x17, 0x02, - 0x00, 0x12, 0x04, 0xfd, 0x01, 0x02, 0x13, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x17, 0x02, 0x00, 0x05, - 0x12, 0x04, 0xfd, 0x01, 0x02, 0x07, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x17, 0x02, 0x00, 0x01, 0x12, - 0x04, 0xfd, 0x01, 0x08, 0x0e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x17, 0x02, 0x00, 0x03, 0x12, 0x04, - 0xfd, 0x01, 0x11, 0x12, 0x0a, 0x0e, 0x0a, 0x04, 0x04, 0x17, 0x08, 0x00, 0x12, 0x06, 0xfe, 0x01, - 0x02, 0x81, 0x02, 0x03, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x17, 0x08, 0x00, 0x01, 0x12, 0x04, 0xfe, - 0x01, 0x08, 0x0e, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x17, 0x02, 0x01, 0x12, 0x04, 0xff, 0x01, 0x04, - 0x1a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x17, 0x02, 0x01, 0x05, 0x12, 0x04, 0xff, 0x01, 0x04, 0x08, - 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x17, 0x02, 0x01, 0x01, 0x12, 0x04, 0xff, 0x01, 0x09, 0x15, 0x0a, - 0x0d, 0x0a, 0x05, 0x04, 0x17, 0x02, 0x01, 0x03, 0x12, 0x04, 0xff, 0x01, 0x18, 0x19, 0x0a, 0x0c, - 0x0a, 0x04, 0x04, 0x17, 0x02, 0x02, 0x12, 0x04, 0x80, 0x02, 0x04, 0x1c, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x17, 0x02, 0x02, 0x05, 0x12, 0x04, 0x80, 0x02, 0x04, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, - 0x17, 0x02, 0x02, 0x01, 0x12, 0x04, 0x80, 0x02, 0x0b, 0x17, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x17, - 0x02, 0x02, 0x03, 0x12, 0x04, 0x80, 0x02, 0x1a, 0x1b, 0x0a, 0x58, 0x0a, 0x02, 0x05, 0x01, 0x12, - 0x06, 0x85, 0x02, 0x00, 0x8a, 0x02, 0x01, 0x1a, 0x4a, 0x20, 0x4a, 0x6f, 0x62, 0x54, 0x79, 0x70, - 0x65, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66, - 0x20, 0x6a, 0x6f, 0x62, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x20, - 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x20, - 0x6f, 0x6e, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x05, 0x01, 0x01, 0x12, 0x04, 0x85, 0x02, 0x05, 0x0c, - 0x0a, 0x0c, 0x0a, 0x04, 0x05, 0x01, 0x02, 0x00, 0x12, 0x04, 0x86, 0x02, 0x02, 0x1b, 0x0a, 0x0d, - 0x0a, 0x05, 0x05, 0x01, 0x02, 0x00, 0x01, 0x12, 0x04, 0x86, 0x02, 0x02, 0x16, 0x0a, 0x0d, 0x0a, - 0x05, 0x05, 0x01, 0x02, 0x00, 0x02, 0x12, 0x04, 0x86, 0x02, 0x19, 0x1a, 0x0a, 0x0c, 0x0a, 0x04, - 0x05, 0x01, 0x02, 0x01, 0x12, 0x04, 0x87, 0x02, 0x02, 0x1a, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, - 0x02, 0x01, 0x01, 0x12, 0x04, 0x87, 0x02, 0x02, 0x15, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, - 0x01, 0x02, 0x12, 0x04, 0x87, 0x02, 0x18, 0x19, 0x0a, 0x0c, 0x0a, 0x04, 0x05, 0x01, 0x02, 0x02, - 0x12, 0x04, 0x88, 0x02, 0x02, 0x1a, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x02, 0x01, 0x12, - 0x04, 0x88, 0x02, 0x02, 0x15, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x02, 0x02, 0x12, 0x04, - 0x88, 0x02, 0x18, 0x19, 0x0a, 0x0c, 0x0a, 0x04, 0x05, 0x01, 0x02, 0x03, 0x12, 0x04, 0x89, 0x02, - 0x02, 0x1c, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x03, 0x01, 0x12, 0x04, 0x89, 0x02, 0x02, - 0x17, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x03, 0x02, 0x12, 0x04, 0x89, 0x02, 0x1a, 0x1b, - 0x0a, 0x56, 0x0a, 0x02, 0x04, 0x18, 0x12, 0x06, 0x8d, 0x02, 0x00, 0x8f, 0x02, 0x01, 0x1a, 0x48, - 0x20, 0x47, 0x65, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, - 0x20, 0x67, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x72, 0x75, - 0x6e, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x72, 0x65, 0x66, 0x72, - 0x65, 0x73, 0x68, 0x20, 0x6a, 0x6f, 0x62, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x18, 0x01, 0x12, - 0x04, 0x8d, 0x02, 0x08, 0x1c, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x18, 0x02, 0x00, 0x12, 0x04, 0x8e, - 0x02, 0x02, 0x17, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x18, 0x02, 0x00, 0x06, 0x12, 0x04, 0x8e, 0x02, - 0x02, 0x09, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x18, 0x02, 0x00, 0x01, 0x12, 0x04, 0x8e, 0x02, 0x0a, - 0x12, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x18, 0x02, 0x00, 0x03, 0x12, 0x04, 0x8e, 0x02, 0x15, 0x16, - 0x0a, 0x5a, 0x0a, 0x02, 0x04, 0x19, 0x12, 0x06, 0x92, 0x02, 0x00, 0x94, 0x02, 0x01, 0x1a, 0x4c, - 0x20, 0x47, 0x65, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, - 0x6f, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, - 0x74, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, - 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x20, 0x6a, 0x6f, 0x62, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, - 0x04, 0x19, 0x01, 0x12, 0x04, 0x92, 0x02, 0x08, 0x1d, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x19, 0x02, - 0x00, 0x12, 0x04, 0x93, 0x02, 0x02, 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x19, 0x02, 0x00, 0x05, - 0x12, 0x04, 0x93, 0x02, 0x02, 0x07, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x19, 0x02, 0x00, 0x01, 0x12, - 0x04, 0x93, 0x02, 0x08, 0x13, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x19, 0x02, 0x00, 0x03, 0x12, 0x04, - 0x93, 0x02, 0x16, 0x17, 0x0a, 0x56, 0x0a, 0x02, 0x04, 0x1a, 0x12, 0x06, 0x97, 0x02, 0x00, 0x9a, - 0x02, 0x01, 0x1a, 0x48, 0x20, 0x53, 0x65, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, - 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, - 0x64, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, - 0x74, 0x20, 0x72, 0x75, 0x6e, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, - 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x20, 0x6a, 0x6f, 0x62, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, - 0x04, 0x1a, 0x01, 0x12, 0x04, 0x97, 0x02, 0x08, 0x1b, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x1a, 0x02, - 0x00, 0x12, 0x04, 0x98, 0x02, 0x02, 0x17, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1a, 0x02, 0x00, 0x06, - 0x12, 0x04, 0x98, 0x02, 0x02, 0x09, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1a, 0x02, 0x00, 0x01, 0x12, - 0x04, 0x98, 0x02, 0x0a, 0x12, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1a, 0x02, 0x00, 0x03, 0x12, 0x04, - 0x98, 0x02, 0x15, 0x16, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x1a, 0x02, 0x01, 0x12, 0x04, 0x99, 0x02, - 0x02, 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1a, 0x02, 0x01, 0x05, 0x12, 0x04, 0x99, 0x02, 0x02, - 0x07, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1a, 0x02, 0x01, 0x01, 0x12, 0x04, 0x99, 0x02, 0x08, 0x13, - 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1a, 0x02, 0x01, 0x03, 0x12, 0x04, 0x99, 0x02, 0x16, 0x17, 0x0a, - 0x3d, 0x0a, 0x02, 0x04, 0x1b, 0x12, 0x04, 0x9d, 0x02, 0x00, 0x20, 0x1a, 0x31, 0x20, 0x53, 0x65, - 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, - 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0b, - 0x0a, 0x03, 0x04, 0x1b, 0x01, 0x12, 0x04, 0x9d, 0x02, 0x08, 0x1d, 0x0a, 0x42, 0x0a, 0x02, 0x04, - 0x1c, 0x12, 0x06, 0xa0, 0x02, 0x00, 0xa8, 0x02, 0x01, 0x1a, 0x34, 0x20, 0x41, 0x20, 0x6d, 0x61, - 0x70, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x20, - 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x65, 0x64, 0x20, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, - 0x0b, 0x0a, 0x03, 0x04, 0x1c, 0x01, 0x12, 0x04, 0xa0, 0x02, 0x08, 0x10, 0x0a, 0x51, 0x0a, 0x04, - 0x04, 0x1c, 0x03, 0x00, 0x12, 0x06, 0xa2, 0x02, 0x02, 0xa6, 0x02, 0x03, 0x1a, 0x41, 0x20, 0x54, - 0x6f, 0x70, 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x20, 0x77, 0x72, 0x61, 0x70, 0x73, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6e, - 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, - 0x69, 0x74, 0x20, 0x77, 0x61, 0x73, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x0a, 0x0a, - 0x0d, 0x0a, 0x05, 0x04, 0x1c, 0x03, 0x00, 0x01, 0x12, 0x04, 0xa2, 0x02, 0x0a, 0x13, 0x0a, 0x0e, - 0x0a, 0x06, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0xa3, 0x02, 0x04, 0x1a, 0x0a, 0x0f, - 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0xa3, 0x02, 0x04, 0x0a, 0x0a, - 0x0f, 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xa3, 0x02, 0x0b, 0x15, - 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0xa3, 0x02, 0x18, - 0x19, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, 0xa4, 0x02, 0x04, - 0x1c, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x04, 0xa4, 0x02, - 0x04, 0x0a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0xa4, - 0x02, 0x0b, 0x17, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, - 0xa4, 0x02, 0x1a, 0x1b, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x02, 0x12, 0x04, - 0xa5, 0x02, 0x04, 0x36, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x02, 0x06, 0x12, - 0x04, 0xa5, 0x02, 0x04, 0x26, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x02, 0x01, - 0x12, 0x04, 0xa5, 0x02, 0x27, 0x31, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x02, - 0x03, 0x12, 0x04, 0xa5, 0x02, 0x34, 0x35, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x1c, 0x02, 0x00, 0x12, - 0x04, 0xa7, 0x02, 0x02, 0x24, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1c, 0x02, 0x00, 0x06, 0x12, 0x04, - 0xa7, 0x02, 0x02, 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1c, 0x02, 0x00, 0x01, 0x12, 0x04, 0xa7, - 0x02, 0x19, 0x1f, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1c, 0x02, 0x00, 0x03, 0x12, 0x04, 0xa7, 0x02, - 0x22, 0x23, 0x0a, 0x4f, 0x0a, 0x02, 0x04, 0x1d, 0x12, 0x06, 0xab, 0x02, 0x00, 0xad, 0x02, 0x01, - 0x1a, 0x41, 0x20, 0x55, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x67, 0x65, 0x74, 0x20, 0x61, - 0x20, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x63, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x20, - 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x6b, 0x65, - 0x79, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x1d, 0x01, 0x12, 0x04, 0xab, 0x02, 0x08, 0x26, - 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x1d, 0x02, 0x00, 0x12, 0x04, 0xac, 0x02, 0x02, 0x1d, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x1d, 0x02, 0x00, 0x04, 0x12, 0x04, 0xac, 0x02, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x1d, 0x02, 0x00, 0x05, 0x12, 0x04, 0xac, 0x02, 0x0b, 0x11, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x1d, 0x02, 0x00, 0x01, 0x12, 0x04, 0xac, 0x02, 0x12, 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, - 0x1d, 0x02, 0x00, 0x03, 0x12, 0x04, 0xac, 0x02, 0x1b, 0x1c, 0x0a, 0x36, 0x0a, 0x02, 0x04, 0x1e, - 0x12, 0x06, 0xb0, 0x02, 0x00, 0xbb, 0x02, 0x01, 0x1a, 0x28, 0x20, 0x41, 0x20, 0x6d, 0x61, 0x70, - 0x70, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x20, 0x74, - 0x6f, 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x6b, 0x65, 0x79, - 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x1e, 0x01, 0x12, 0x04, 0xb0, 0x02, 0x08, 0x27, 0x0a, - 0x5d, 0x0a, 0x04, 0x04, 0x1e, 0x03, 0x00, 0x12, 0x06, 0xb2, 0x02, 0x02, 0xb5, 0x02, 0x03, 0x1a, - 0x4d, 0x20, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x44, 0x61, 0x74, 0x61, 0x20, 0x77, 0x72, - 0x61, 0x70, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x6b, 0x65, 0x79, - 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, - 0x6f, 0x66, 0x20, 0x33, 0x30, 0x20, 0x64, 0x61, 0x79, 0x20, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, - 0x73, 0x20, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x20, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x0a, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x1e, 0x03, 0x00, 0x01, 0x12, 0x04, 0xb2, 0x02, 0x0a, 0x15, 0x0a, 0x0e, 0x0a, - 0x06, 0x04, 0x1e, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0xb3, 0x02, 0x04, 0x2d, 0x0a, 0x0f, 0x0a, - 0x07, 0x04, 0x1e, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0xb3, 0x02, 0x04, 0x09, 0x0a, 0x0f, - 0x0a, 0x07, 0x04, 0x1e, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xb3, 0x02, 0x0a, 0x28, 0x0a, - 0x0f, 0x0a, 0x07, 0x04, 0x1e, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0xb3, 0x02, 0x2b, 0x2c, - 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x1e, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, 0xb4, 0x02, 0x04, 0x17, - 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1e, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x04, 0xb4, 0x02, 0x04, - 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1e, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0xb4, 0x02, - 0x0a, 0x12, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1e, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, 0xb4, - 0x02, 0x15, 0x16, 0x0a, 0x42, 0x0a, 0x04, 0x04, 0x1e, 0x03, 0x01, 0x12, 0x06, 0xb7, 0x02, 0x02, - 0xb9, 0x02, 0x03, 0x1a, 0x32, 0x20, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x20, 0x72, - 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, - 0x6c, 0x65, 0x20, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x44, 0x61, 0x74, 0x61, 0x20, 0x6f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1e, 0x03, 0x01, 0x01, - 0x12, 0x04, 0xb7, 0x02, 0x0a, 0x12, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x1e, 0x03, 0x01, 0x02, 0x00, - 0x12, 0x04, 0xb8, 0x02, 0x04, 0x24, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1e, 0x03, 0x01, 0x02, 0x00, - 0x04, 0x12, 0x04, 0xb8, 0x02, 0x04, 0x0c, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1e, 0x03, 0x01, 0x02, - 0x00, 0x06, 0x12, 0x04, 0xb8, 0x02, 0x0d, 0x18, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1e, 0x03, 0x01, - 0x02, 0x00, 0x01, 0x12, 0x04, 0xb8, 0x02, 0x19, 0x1f, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1e, 0x03, - 0x01, 0x02, 0x00, 0x03, 0x12, 0x04, 0xb8, 0x02, 0x22, 0x23, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x1e, - 0x02, 0x00, 0x12, 0x04, 0xba, 0x02, 0x02, 0x26, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1e, 0x02, 0x00, - 0x06, 0x12, 0x04, 0xba, 0x02, 0x02, 0x17, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1e, 0x02, 0x00, 0x01, - 0x12, 0x04, 0xba, 0x02, 0x18, 0x21, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1e, 0x02, 0x00, 0x03, 0x12, - 0x04, 0xba, 0x02, 0x24, 0x25, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x01, 0x01, 0x12, 0x03, 0x1d, 0x08, 0x18, 0x0a, 0x2b, 0x0a, 0x04, 0x04, 0x01, 0x03, 0x00, 0x12, + 0x04, 0x1f, 0x02, 0x24, 0x03, 0x1a, 0x1d, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, + 0x20, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x03, 0x00, 0x01, 0x12, 0x03, 0x1f, + 0x0a, 0x11, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x20, 0x04, + 0x31, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x20, 0x04, + 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x20, 0x25, + 0x2c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x20, 0x2f, + 0x30, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x21, 0x04, 0x38, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, 0x21, 0x04, 0x29, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x21, 0x2a, 0x33, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x21, 0x36, 0x37, + 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x12, 0x03, 0x22, 0x04, 0x1b, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x22, 0x04, 0x09, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x22, 0x0a, 0x16, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x22, 0x19, 0x1a, 0x0a, + 0x0d, 0x0a, 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x03, 0x12, 0x03, 0x23, 0x04, 0x17, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x03, 0x05, 0x12, 0x03, 0x23, 0x04, 0x08, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x23, 0x09, 0x12, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x03, 0x03, 0x12, 0x03, 0x23, 0x15, 0x16, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x26, 0x02, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x01, 0x02, 0x00, 0x04, 0x12, 0x03, 0x26, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, + 0x00, 0x06, 0x12, 0x03, 0x26, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, + 0x12, 0x03, 0x26, 0x13, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, + 0x26, 0x1e, 0x1f, 0x0a, 0x42, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x2a, 0x00, 0x39, 0x01, 0x1a, + 0x36, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x20, 0x56, 0x31, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x56, 0x32, 0x20, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, + 0x2a, 0x08, 0x17, 0x0a, 0x2c, 0x0a, 0x04, 0x04, 0x02, 0x03, 0x00, 0x12, 0x04, 0x2c, 0x02, 0x36, + 0x03, 0x1a, 0x1e, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x03, 0x00, 0x01, 0x12, 0x03, 0x2c, 0x0a, 0x12, 0x0a, + 0x35, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x03, 0x00, 0x12, 0x04, 0x2e, 0x04, 0x30, 0x05, 0x1a, + 0x25, 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x72, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x03, 0x00, + 0x01, 0x12, 0x03, 0x2e, 0x0c, 0x13, 0x0a, 0x0f, 0x0a, 0x08, 0x04, 0x02, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x12, 0x03, 0x2f, 0x06, 0x1a, 0x0a, 0x10, 0x0a, 0x09, 0x04, 0x02, 0x03, 0x00, 0x03, + 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x2f, 0x06, 0x0b, 0x0a, 0x10, 0x0a, 0x09, 0x04, 0x02, 0x03, + 0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2f, 0x0c, 0x15, 0x0a, 0x10, 0x0a, 0x09, 0x04, + 0x02, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2f, 0x18, 0x19, 0x0a, 0x0e, 0x0a, + 0x06, 0x04, 0x02, 0x03, 0x00, 0x08, 0x00, 0x12, 0x04, 0x32, 0x04, 0x35, 0x05, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x02, 0x03, 0x00, 0x08, 0x00, 0x01, 0x12, 0x03, 0x32, 0x0a, 0x12, 0x0a, 0x0d, 0x0a, + 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x33, 0x06, 0x19, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x33, 0x06, 0x0d, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x33, 0x0e, 0x14, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x33, 0x17, 0x18, 0x0a, 0x0d, 0x0a, 0x06, + 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x34, 0x06, 0x1e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x02, 0x03, 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, 0x34, 0x06, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x02, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x34, 0x14, 0x19, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x02, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x34, 0x1c, 0x1d, 0x0a, 0x0b, 0x0a, 0x04, 0x04, + 0x02, 0x02, 0x00, 0x12, 0x03, 0x38, 0x02, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, + 0x04, 0x12, 0x03, 0x38, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x06, 0x12, + 0x03, 0x38, 0x0b, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x38, + 0x14, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x38, 0x20, 0x21, + 0x0a, 0x4a, 0x0a, 0x02, 0x04, 0x03, 0x12, 0x04, 0x3c, 0x00, 0x45, 0x01, 0x1a, 0x3e, 0x20, 0x44, + 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x20, 0x6f, + 0x66, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, + 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, + 0x04, 0x03, 0x01, 0x12, 0x03, 0x3c, 0x08, 0x18, 0x0a, 0x2b, 0x0a, 0x04, 0x04, 0x03, 0x03, 0x00, + 0x12, 0x04, 0x3e, 0x02, 0x42, 0x03, 0x1a, 0x1d, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, + 0x65, 0x20, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x03, 0x00, 0x01, 0x12, 0x03, + 0x3e, 0x0a, 0x11, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x3f, + 0x04, 0x31, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x3f, + 0x04, 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x3f, + 0x25, 0x2c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x3f, + 0x2f, 0x30, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x40, 0x04, + 0x1b, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x40, 0x04, + 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x40, 0x0a, + 0x16, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x40, 0x19, + 0x1a, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x03, 0x00, 0x02, 0x02, 0x12, 0x03, 0x41, 0x04, 0x1d, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x41, 0x04, 0x0a, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x41, 0x0b, 0x18, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x41, 0x1b, 0x1c, + 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x00, 0x12, 0x03, 0x44, 0x02, 0x20, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x03, 0x02, 0x00, 0x04, 0x12, 0x03, 0x44, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x03, 0x02, 0x00, 0x06, 0x12, 0x03, 0x44, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, + 0x00, 0x01, 0x12, 0x03, 0x44, 0x13, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, + 0x12, 0x03, 0x44, 0x1e, 0x1f, 0x0a, 0x42, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x48, 0x00, 0x51, + 0x01, 0x1a, 0x36, 0x20, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x20, 0x61, 0x20, 0x62, 0x61, + 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x20, + 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x58, 0x33, 0x44, 0x48, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x61, + 0x67, 0x72, 0x65, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x04, 0x01, + 0x12, 0x03, 0x48, 0x08, 0x18, 0x0a, 0x2b, 0x0a, 0x04, 0x04, 0x04, 0x03, 0x00, 0x12, 0x04, 0x4a, + 0x02, 0x4e, 0x03, 0x1a, 0x1d, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x65, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x03, 0x00, 0x01, 0x12, 0x03, 0x4a, 0x0a, 0x11, + 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x4b, 0x04, 0x38, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x4b, 0x04, 0x29, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x4b, 0x2a, 0x33, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x4b, 0x36, 0x37, 0x0a, + 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x4c, 0x04, 0x16, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x4c, 0x04, 0x09, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x4c, 0x0a, 0x11, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x4c, 0x14, 0x15, 0x0a, 0x0d, + 0x0a, 0x06, 0x04, 0x04, 0x03, 0x00, 0x02, 0x02, 0x12, 0x03, 0x4d, 0x04, 0x1b, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x4d, 0x04, 0x09, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x4d, 0x0a, 0x16, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x4d, 0x19, 0x1a, 0x0a, 0x0b, 0x0a, + 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, 0x03, 0x50, 0x02, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, + 0x02, 0x00, 0x04, 0x12, 0x03, 0x50, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, + 0x06, 0x12, 0x03, 0x50, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, + 0x03, 0x50, 0x13, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x50, + 0x1e, 0x1f, 0x0a, 0x42, 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04, 0x54, 0x00, 0x64, 0x01, 0x1a, 0x36, + 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x20, 0x56, 0x31, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x56, + 0x32, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x05, 0x01, 0x12, 0x03, 0x54, + 0x08, 0x17, 0x0a, 0x2c, 0x0a, 0x04, 0x04, 0x05, 0x03, 0x00, 0x12, 0x04, 0x56, 0x02, 0x61, 0x03, + 0x1a, 0x1e, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x65, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x0a, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x03, 0x00, 0x01, 0x12, 0x03, 0x56, 0x0a, 0x12, 0x0a, 0x35, + 0x0a, 0x06, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x12, 0x04, 0x58, 0x04, 0x5b, 0x05, 0x1a, 0x25, + 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x72, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x01, + 0x12, 0x03, 0x58, 0x0c, 0x13, 0x0a, 0x0f, 0x0a, 0x08, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x02, + 0x00, 0x12, 0x03, 0x59, 0x06, 0x35, 0x0a, 0x10, 0x0a, 0x09, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x06, 0x12, 0x03, 0x59, 0x06, 0x26, 0x0a, 0x10, 0x0a, 0x09, 0x04, 0x05, 0x03, 0x00, + 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x59, 0x27, 0x30, 0x0a, 0x10, 0x0a, 0x09, 0x04, 0x05, + 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x59, 0x33, 0x34, 0x0a, 0x0f, 0x0a, 0x08, + 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x5a, 0x06, 0x1c, 0x0a, 0x10, 0x0a, + 0x09, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x5a, 0x06, 0x0b, 0x0a, + 0x10, 0x0a, 0x09, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x5a, 0x0c, + 0x17, 0x0a, 0x10, 0x0a, 0x09, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, + 0x5a, 0x1a, 0x1b, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x05, 0x03, 0x00, 0x08, 0x00, 0x12, 0x04, 0x5d, + 0x04, 0x60, 0x05, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x08, 0x00, 0x01, 0x12, 0x03, + 0x5d, 0x0a, 0x12, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x05, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x5e, + 0x06, 0x19, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x5e, + 0x06, 0x0d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x5e, + 0x0e, 0x14, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x5e, + 0x17, 0x18, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x05, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x5f, 0x06, + 0x1e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, 0x5f, 0x06, + 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x5f, 0x14, + 0x19, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x5f, 0x1c, + 0x1d, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, 0x12, 0x03, 0x63, 0x02, 0x22, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x04, 0x12, 0x03, 0x63, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x05, 0x02, 0x00, 0x06, 0x12, 0x03, 0x63, 0x0b, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, + 0x02, 0x00, 0x01, 0x12, 0x03, 0x63, 0x14, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, + 0x03, 0x12, 0x03, 0x63, 0x20, 0x21, 0x0a, 0x4a, 0x0a, 0x02, 0x04, 0x06, 0x12, 0x04, 0x67, 0x00, + 0x70, 0x01, 0x1a, 0x3e, 0x20, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x20, 0x61, 0x20, 0x62, + 0x61, 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, + 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x70, 0x70, 0x72, 0x6f, + 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x20, 0x6b, 0x65, 0x79, + 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x06, 0x01, 0x12, 0x03, 0x67, 0x08, 0x18, 0x0a, 0x2b, + 0x0a, 0x04, 0x04, 0x06, 0x03, 0x00, 0x12, 0x04, 0x69, 0x02, 0x6d, 0x03, 0x1a, 0x1d, 0x20, 0x41, + 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x06, 0x03, 0x00, 0x01, 0x12, 0x03, 0x69, 0x0a, 0x11, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x06, 0x03, + 0x00, 0x02, 0x00, 0x12, 0x03, 0x6a, 0x04, 0x16, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, + 0x02, 0x00, 0x05, 0x12, 0x03, 0x6a, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, + 0x02, 0x00, 0x01, 0x12, 0x03, 0x6a, 0x0a, 0x11, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, + 0x02, 0x00, 0x03, 0x12, 0x03, 0x6a, 0x14, 0x15, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x06, 0x03, 0x00, + 0x02, 0x01, 0x12, 0x03, 0x6b, 0x04, 0x1b, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, + 0x01, 0x05, 0x12, 0x03, 0x6b, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, + 0x01, 0x01, 0x12, 0x03, 0x6b, 0x0a, 0x16, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, + 0x01, 0x03, 0x12, 0x03, 0x6b, 0x19, 0x1a, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x06, 0x03, 0x00, 0x02, + 0x02, 0x12, 0x03, 0x6c, 0x04, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, 0x02, + 0x05, 0x12, 0x03, 0x6c, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, 0x02, + 0x01, 0x12, 0x03, 0x6c, 0x0b, 0x18, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, 0x02, + 0x03, 0x12, 0x03, 0x6c, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x00, 0x12, 0x03, + 0x6f, 0x02, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x04, 0x12, 0x03, 0x6f, 0x02, + 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x06, 0x12, 0x03, 0x6f, 0x0b, 0x12, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x01, 0x12, 0x03, 0x6f, 0x13, 0x1b, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x06, 0x02, 0x00, 0x03, 0x12, 0x03, 0x6f, 0x1e, 0x1f, 0x0a, 0x2c, 0x0a, 0x02, 0x04, + 0x07, 0x12, 0x04, 0x73, 0x00, 0x7a, 0x01, 0x1a, 0x20, 0x20, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x79, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x6c, 0x66, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x07, 0x01, + 0x12, 0x03, 0x73, 0x08, 0x1a, 0x0a, 0x1c, 0x0a, 0x04, 0x04, 0x07, 0x03, 0x00, 0x12, 0x04, 0x75, + 0x02, 0x77, 0x03, 0x1a, 0x0e, 0x20, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x03, 0x00, 0x01, 0x12, 0x03, 0x75, 0x0a, + 0x11, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x76, 0x04, 0x16, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x76, 0x04, 0x09, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x76, 0x0a, 0x11, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x76, 0x14, 0x15, + 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x00, 0x12, 0x03, 0x79, 0x02, 0x20, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x07, 0x02, 0x00, 0x04, 0x12, 0x03, 0x79, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x07, 0x02, 0x00, 0x06, 0x12, 0x03, 0x79, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, + 0x00, 0x01, 0x12, 0x03, 0x79, 0x13, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x03, + 0x12, 0x03, 0x79, 0x1e, 0x1f, 0x0a, 0x33, 0x0a, 0x02, 0x04, 0x08, 0x12, 0x05, 0x7d, 0x00, 0x8c, + 0x01, 0x01, 0x1a, 0x26, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x53, 0x65, 0x6c, 0x66, 0x45, 0x6e, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x08, + 0x01, 0x12, 0x03, 0x7d, 0x08, 0x1b, 0x0a, 0x1e, 0x0a, 0x04, 0x04, 0x08, 0x03, 0x00, 0x12, 0x05, + 0x7f, 0x02, 0x89, 0x01, 0x03, 0x1a, 0x0f, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x03, 0x00, 0x01, 0x12, + 0x03, 0x7f, 0x0a, 0x12, 0x0a, 0x24, 0x0a, 0x06, 0x04, 0x08, 0x03, 0x00, 0x03, 0x00, 0x12, 0x06, + 0x81, 0x01, 0x04, 0x83, 0x01, 0x05, 0x1a, 0x12, 0x20, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x0a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x08, + 0x03, 0x00, 0x03, 0x00, 0x01, 0x12, 0x04, 0x81, 0x01, 0x0c, 0x13, 0x0a, 0x10, 0x0a, 0x08, 0x04, + 0x08, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0x82, 0x01, 0x06, 0x1a, 0x0a, 0x11, 0x0a, + 0x09, 0x04, 0x08, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0x82, 0x01, 0x06, 0x0b, + 0x0a, 0x11, 0x0a, 0x09, 0x04, 0x08, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0x82, + 0x01, 0x0c, 0x15, 0x0a, 0x11, 0x0a, 0x09, 0x04, 0x08, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, + 0x12, 0x04, 0x82, 0x01, 0x18, 0x19, 0x0a, 0x10, 0x0a, 0x06, 0x04, 0x08, 0x03, 0x00, 0x08, 0x00, + 0x12, 0x06, 0x85, 0x01, 0x04, 0x88, 0x01, 0x05, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, + 0x08, 0x00, 0x01, 0x12, 0x04, 0x85, 0x01, 0x0a, 0x12, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x08, 0x03, + 0x00, 0x02, 0x00, 0x12, 0x04, 0x86, 0x01, 0x06, 0x19, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x08, 0x03, + 0x00, 0x02, 0x00, 0x06, 0x12, 0x04, 0x86, 0x01, 0x06, 0x0d, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x08, + 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0x86, 0x01, 0x0e, 0x14, 0x0a, 0x0f, 0x0a, 0x07, 0x04, + 0x08, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0x86, 0x01, 0x17, 0x18, 0x0a, 0x0e, 0x0a, 0x06, + 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, 0x87, 0x01, 0x06, 0x1e, 0x0a, 0x0f, 0x0a, 0x07, + 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x06, 0x12, 0x04, 0x87, 0x01, 0x06, 0x13, 0x0a, 0x0f, 0x0a, + 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0x87, 0x01, 0x14, 0x19, 0x0a, 0x0f, + 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, 0x87, 0x01, 0x1c, 0x1d, 0x0a, + 0x0c, 0x0a, 0x04, 0x04, 0x08, 0x02, 0x00, 0x12, 0x04, 0x8b, 0x01, 0x02, 0x22, 0x0a, 0x0d, 0x0a, + 0x05, 0x04, 0x08, 0x02, 0x00, 0x04, 0x12, 0x04, 0x8b, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, + 0x04, 0x08, 0x02, 0x00, 0x06, 0x12, 0x04, 0x8b, 0x01, 0x0b, 0x13, 0x0a, 0x0d, 0x0a, 0x05, 0x04, + 0x08, 0x02, 0x00, 0x01, 0x12, 0x04, 0x8b, 0x01, 0x14, 0x1d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x08, + 0x02, 0x00, 0x03, 0x12, 0x04, 0x8b, 0x01, 0x20, 0x21, 0x0a, 0x22, 0x0a, 0x02, 0x04, 0x09, 0x12, + 0x06, 0x8f, 0x01, 0x00, 0x96, 0x01, 0x01, 0x1a, 0x14, 0x20, 0x53, 0x65, 0x6c, 0x66, 0x44, 0x65, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0b, 0x0a, + 0x03, 0x04, 0x09, 0x01, 0x12, 0x04, 0x8f, 0x01, 0x08, 0x1a, 0x0a, 0x1e, 0x0a, 0x04, 0x04, 0x09, + 0x03, 0x00, 0x12, 0x06, 0x91, 0x01, 0x02, 0x93, 0x01, 0x03, 0x1a, 0x0e, 0x20, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x09, + 0x03, 0x00, 0x01, 0x12, 0x04, 0x91, 0x01, 0x0a, 0x11, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x09, 0x03, + 0x00, 0x02, 0x00, 0x12, 0x04, 0x92, 0x01, 0x04, 0x16, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x09, 0x03, + 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0x92, 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x09, + 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0x92, 0x01, 0x0a, 0x11, 0x0a, 0x0f, 0x0a, 0x07, 0x04, + 0x09, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0x92, 0x01, 0x14, 0x15, 0x0a, 0x0c, 0x0a, 0x04, + 0x04, 0x09, 0x02, 0x00, 0x12, 0x04, 0x95, 0x01, 0x02, 0x20, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x09, + 0x02, 0x00, 0x04, 0x12, 0x04, 0x95, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x09, 0x02, + 0x00, 0x06, 0x12, 0x04, 0x95, 0x01, 0x0b, 0x12, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, + 0x01, 0x12, 0x04, 0x95, 0x01, 0x13, 0x1b, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x03, + 0x12, 0x04, 0x95, 0x01, 0x1e, 0x1f, 0x0a, 0x3c, 0x0a, 0x02, 0x04, 0x0a, 0x12, 0x06, 0x99, 0x01, + 0x00, 0x9b, 0x01, 0x01, 0x1a, 0x2e, 0x20, 0x47, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x73, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0a, 0x01, 0x12, 0x04, 0x99, 0x01, 0x08, + 0x34, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0a, 0x02, 0x00, 0x12, 0x04, 0x9a, 0x01, 0x02, 0x18, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x05, 0x12, 0x04, 0x9a, 0x01, 0x02, 0x08, 0x0a, 0x0d, + 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x01, 0x12, 0x04, 0x9a, 0x01, 0x09, 0x13, 0x0a, 0x0d, 0x0a, + 0x05, 0x04, 0x0a, 0x02, 0x00, 0x03, 0x12, 0x04, 0x9a, 0x01, 0x16, 0x17, 0x0a, 0x5d, 0x0a, 0x02, + 0x04, 0x0b, 0x12, 0x06, 0x9e, 0x01, 0x00, 0xa3, 0x01, 0x01, 0x1a, 0x4f, 0x20, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, + 0x6e, 0x20, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x74, 0x6f, 0x70, 0x69, 0x63, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, + 0x0b, 0x01, 0x12, 0x04, 0x9e, 0x01, 0x08, 0x1b, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x00, + 0x12, 0x04, 0x9f, 0x01, 0x02, 0x39, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x06, 0x12, + 0x04, 0x9f, 0x01, 0x02, 0x2c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x01, 0x12, 0x04, + 0x9f, 0x01, 0x2d, 0x34, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x03, 0x12, 0x04, 0x9f, + 0x01, 0x37, 0x38, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x01, 0x12, 0x04, 0xa0, 0x01, 0x02, + 0x3c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x01, 0x06, 0x12, 0x04, 0xa0, 0x01, 0x02, 0x2d, + 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x01, 0x01, 0x12, 0x04, 0xa0, 0x01, 0x2e, 0x37, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x01, 0x03, 0x12, 0x04, 0xa0, 0x01, 0x3a, 0x3b, 0x0a, 0x0c, + 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x02, 0x12, 0x04, 0xa1, 0x01, 0x02, 0x18, 0x0a, 0x0d, 0x0a, 0x05, + 0x04, 0x0b, 0x02, 0x02, 0x05, 0x12, 0x04, 0xa1, 0x01, 0x02, 0x08, 0x0a, 0x0d, 0x0a, 0x05, 0x04, + 0x0b, 0x02, 0x02, 0x01, 0x12, 0x04, 0xa1, 0x01, 0x09, 0x13, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, + 0x02, 0x02, 0x03, 0x12, 0x04, 0xa1, 0x01, 0x16, 0x17, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0b, 0x02, + 0x03, 0x12, 0x04, 0xa2, 0x01, 0x02, 0x3e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x03, 0x06, + 0x12, 0x04, 0xa2, 0x01, 0x02, 0x2b, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x03, 0x01, 0x12, + 0x04, 0xa2, 0x01, 0x2c, 0x39, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x03, 0x03, 0x12, 0x04, + 0xa2, 0x01, 0x3c, 0x3d, 0x0a, 0x31, 0x0a, 0x02, 0x04, 0x0c, 0x12, 0x06, 0xa6, 0x01, 0x00, 0xa9, + 0x01, 0x01, 0x1a, 0x23, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x6f, + 0x20, 0x61, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0c, 0x01, 0x12, 0x04, + 0xa6, 0x01, 0x08, 0x1c, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0c, 0x02, 0x00, 0x12, 0x04, 0xa7, 0x01, + 0x02, 0x3f, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x06, 0x12, 0x04, 0xa7, 0x01, 0x02, + 0x2d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x01, 0x12, 0x04, 0xa7, 0x01, 0x2e, 0x3a, + 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x03, 0x12, 0x04, 0xa7, 0x01, 0x3d, 0x3e, 0x0a, + 0x0c, 0x0a, 0x04, 0x04, 0x0c, 0x02, 0x01, 0x12, 0x04, 0xa8, 0x01, 0x02, 0x14, 0x0a, 0x0d, 0x0a, + 0x05, 0x04, 0x0c, 0x02, 0x01, 0x05, 0x12, 0x04, 0xa8, 0x01, 0x02, 0x07, 0x0a, 0x0d, 0x0a, 0x05, + 0x04, 0x0c, 0x02, 0x01, 0x01, 0x12, 0x04, 0xa8, 0x01, 0x08, 0x0f, 0x0a, 0x0d, 0x0a, 0x05, 0x04, + 0x0c, 0x02, 0x01, 0x03, 0x12, 0x04, 0xa8, 0x01, 0x12, 0x13, 0x0a, 0x4a, 0x0a, 0x02, 0x04, 0x0d, + 0x12, 0x06, 0xac, 0x01, 0x00, 0xb5, 0x01, 0x01, 0x1a, 0x3c, 0x20, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, + 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x20, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4b, 0x65, 0x79, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0d, 0x01, 0x12, 0x04, 0xac, + 0x01, 0x08, 0x1a, 0x0a, 0x2e, 0x0a, 0x04, 0x04, 0x0d, 0x03, 0x00, 0x12, 0x06, 0xae, 0x01, 0x02, + 0xb2, 0x01, 0x03, 0x1a, 0x1e, 0x20, 0x4d, 0x69, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x20, 0x78, 0x6d, + 0x74, 0x70, 0x2e, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x20, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0d, 0x03, 0x00, 0x01, 0x12, 0x04, 0xae, 0x01, + 0x0a, 0x11, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0d, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0xaf, 0x01, + 0x04, 0x1d, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0xaf, + 0x01, 0x04, 0x0a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, + 0xaf, 0x01, 0x0b, 0x18, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, + 0x04, 0xaf, 0x01, 0x1b, 0x1c, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0d, 0x03, 0x00, 0x02, 0x01, 0x12, + 0x04, 0xb0, 0x01, 0x04, 0x1c, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, 0x03, 0x00, 0x02, 0x01, 0x05, + 0x12, 0x04, 0xb0, 0x01, 0x04, 0x0a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, 0x03, 0x00, 0x02, 0x01, + 0x01, 0x12, 0x04, 0xb0, 0x01, 0x0b, 0x17, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, 0x03, 0x00, 0x02, + 0x01, 0x03, 0x12, 0x04, 0xb0, 0x01, 0x1a, 0x1b, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0d, 0x03, 0x00, + 0x02, 0x02, 0x12, 0x04, 0xb1, 0x01, 0x04, 0x16, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, 0x03, 0x00, + 0x02, 0x02, 0x05, 0x12, 0x04, 0xb1, 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, 0x03, + 0x00, 0x02, 0x02, 0x01, 0x12, 0x04, 0xb1, 0x01, 0x0a, 0x11, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0d, + 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, 0x04, 0xb1, 0x01, 0x14, 0x15, 0x0a, 0x0c, 0x0a, 0x04, 0x04, + 0x0d, 0x02, 0x00, 0x12, 0x04, 0xb4, 0x01, 0x02, 0x20, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0d, 0x02, + 0x00, 0x04, 0x12, 0x04, 0xb4, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x00, + 0x06, 0x12, 0x04, 0xb4, 0x01, 0x0b, 0x12, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x00, 0x01, + 0x12, 0x04, 0xb4, 0x01, 0x13, 0x1b, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x00, 0x03, 0x12, + 0x04, 0xb4, 0x01, 0x1e, 0x1f, 0x0a, 0x30, 0x0a, 0x02, 0x04, 0x0e, 0x12, 0x06, 0xb8, 0x01, 0x00, + 0xc7, 0x01, 0x01, 0x1a, 0x22, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, + 0x6f, 0x20, 0x61, 0x20, 0x53, 0x61, 0x76, 0x65, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0e, 0x01, 0x12, 0x04, + 0xb8, 0x01, 0x08, 0x1b, 0x0a, 0x23, 0x0a, 0x04, 0x04, 0x0e, 0x03, 0x00, 0x12, 0x06, 0xba, 0x01, + 0x02, 0xc4, 0x01, 0x03, 0x1a, 0x13, 0x20, 0x41, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x03, + 0x00, 0x01, 0x12, 0x04, 0xba, 0x01, 0x0a, 0x12, 0x0a, 0x37, 0x0a, 0x06, 0x04, 0x0e, 0x03, 0x00, + 0x03, 0x00, 0x12, 0x06, 0xbc, 0x01, 0x04, 0xbe, 0x01, 0x05, 0x1a, 0x25, 0x20, 0x57, 0x72, 0x61, + 0x70, 0x70, 0x65, 0x72, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x0a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x03, 0x00, 0x01, 0x12, 0x04, 0xbc, 0x01, + 0x0c, 0x13, 0x0a, 0x10, 0x0a, 0x08, 0x04, 0x0e, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, + 0xbd, 0x01, 0x06, 0x43, 0x0a, 0x11, 0x0a, 0x09, 0x04, 0x0e, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, + 0x06, 0x12, 0x04, 0xbd, 0x01, 0x06, 0x31, 0x0a, 0x11, 0x0a, 0x09, 0x04, 0x0e, 0x03, 0x00, 0x03, + 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xbd, 0x01, 0x32, 0x3e, 0x0a, 0x11, 0x0a, 0x09, 0x04, 0x0e, + 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0xbd, 0x01, 0x41, 0x42, 0x0a, 0x10, 0x0a, + 0x06, 0x04, 0x0e, 0x03, 0x00, 0x08, 0x00, 0x12, 0x06, 0xc0, 0x01, 0x04, 0xc3, 0x01, 0x05, 0x0a, + 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x08, 0x00, 0x01, 0x12, 0x04, 0xc0, 0x01, 0x0a, 0x12, + 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0xc1, 0x01, 0x06, 0x19, + 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x04, 0xc1, 0x01, 0x06, + 0x0d, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xc1, 0x01, + 0x0e, 0x14, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0xc1, + 0x01, 0x17, 0x18, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, 0xc2, + 0x01, 0x06, 0x1e, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x01, 0x06, 0x12, 0x04, + 0xc2, 0x01, 0x06, 0x13, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, + 0x04, 0xc2, 0x01, 0x14, 0x19, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x01, 0x03, + 0x12, 0x04, 0xc2, 0x01, 0x1c, 0x1d, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0e, 0x02, 0x00, 0x12, 0x04, + 0xc6, 0x01, 0x02, 0x22, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x04, 0x12, 0x04, 0xc6, + 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x06, 0x12, 0x04, 0xc6, 0x01, + 0x0b, 0x13, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x01, 0x12, 0x04, 0xc6, 0x01, 0x14, + 0x1d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x03, 0x12, 0x04, 0xc6, 0x01, 0x20, 0x21, + 0x0a, 0x57, 0x0a, 0x02, 0x04, 0x0f, 0x12, 0x06, 0xca, 0x01, 0x00, 0xcc, 0x01, 0x01, 0x1a, 0x49, + 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, + 0x74, 0x6f, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x75, 0x74, + 0x68, 0x20, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x58, 0x4d, 0x54, 0x50, 0x20, 0x41, 0x50, 0x49, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0f, 0x01, + 0x12, 0x04, 0xca, 0x01, 0x08, 0x1e, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0f, 0x02, 0x00, 0x12, 0x04, + 0xcb, 0x01, 0x02, 0x23, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x00, 0x04, 0x12, 0x04, 0xcb, + 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x00, 0x05, 0x12, 0x04, 0xcb, 0x01, + 0x0b, 0x11, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x00, 0x01, 0x12, 0x04, 0xcb, 0x01, 0x12, + 0x1e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x00, 0x03, 0x12, 0x04, 0xcb, 0x01, 0x21, 0x22, + 0x0a, 0x70, 0x0a, 0x02, 0x04, 0x10, 0x12, 0x06, 0xd0, 0x01, 0x00, 0xd2, 0x01, 0x01, 0x1a, 0x62, + 0x20, 0x53, 0x61, 0x76, 0x65, 0x56, 0x31, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x69, 0x73, 0x20, 0x75, + 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x62, 0x61, + 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x62, 0x75, 0x69, + 0x6c, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, + 0x65, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x10, 0x01, 0x12, 0x04, 0xd0, 0x01, 0x08, 0x22, 0x0a, + 0x0c, 0x0a, 0x04, 0x04, 0x10, 0x02, 0x00, 0x12, 0x04, 0xd1, 0x01, 0x02, 0x49, 0x0a, 0x0d, 0x0a, + 0x05, 0x04, 0x10, 0x02, 0x00, 0x04, 0x12, 0x04, 0xd1, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, + 0x04, 0x10, 0x02, 0x00, 0x06, 0x12, 0x04, 0xd1, 0x01, 0x0b, 0x36, 0x0a, 0x0d, 0x0a, 0x05, 0x04, + 0x10, 0x02, 0x00, 0x01, 0x12, 0x04, 0xd1, 0x01, 0x37, 0x44, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x10, + 0x02, 0x00, 0x03, 0x12, 0x04, 0xd1, 0x01, 0x47, 0x48, 0x0a, 0x3f, 0x0a, 0x02, 0x04, 0x11, 0x12, + 0x04, 0xd5, 0x01, 0x00, 0x26, 0x1a, 0x33, 0x20, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, + 0x64, 0x65, 0x72, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x53, 0x61, 0x76, 0x65, 0x56, 0x31, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x11, + 0x01, 0x12, 0x04, 0xd5, 0x01, 0x08, 0x23, 0x0a, 0x2f, 0x0a, 0x02, 0x04, 0x12, 0x12, 0x06, 0xd8, + 0x01, 0x00, 0xda, 0x01, 0x01, 0x1a, 0x21, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x47, 0x65, 0x74, 0x56, 0x32, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x12, 0x01, 0x12, + 0x04, 0xd8, 0x01, 0x08, 0x20, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x12, 0x02, 0x00, 0x12, 0x04, 0xd9, + 0x01, 0x02, 0x49, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x12, 0x02, 0x00, 0x04, 0x12, 0x04, 0xd9, 0x01, + 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x12, 0x02, 0x00, 0x06, 0x12, 0x04, 0xd9, 0x01, 0x0b, + 0x36, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x12, 0x02, 0x00, 0x01, 0x12, 0x04, 0xd9, 0x01, 0x37, 0x44, + 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x12, 0x02, 0x00, 0x03, 0x12, 0x04, 0xd9, 0x01, 0x47, 0x48, 0x0a, + 0x93, 0x01, 0x0a, 0x02, 0x04, 0x13, 0x12, 0x06, 0xde, 0x01, 0x00, 0xe0, 0x01, 0x01, 0x1a, 0x84, + 0x01, 0x20, 0x55, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x20, + 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, + 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x68, + 0x61, 0x73, 0x20, 0x62, 0x65, 0x65, 0x6e, 0x20, 0x73, 0x65, 0x74, 0x75, 0x70, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x0a, 0x20, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x4f, 0x6e, 0x6c, 0x79, + 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x4d, 0x4d, 0x20, 0x53, 0x6e, 0x61, + 0x70, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x6c, 0x79, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x13, 0x01, 0x12, 0x04, 0xde, 0x01, + 0x08, 0x20, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x13, 0x02, 0x00, 0x12, 0x04, 0xdf, 0x01, 0x02, 0x1c, + 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x13, 0x02, 0x00, 0x05, 0x12, 0x04, 0xdf, 0x01, 0x02, 0x08, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x13, 0x02, 0x00, 0x01, 0x12, 0x04, 0xdf, 0x01, 0x09, 0x17, 0x0a, 0x0d, + 0x0a, 0x05, 0x04, 0x13, 0x02, 0x00, 0x03, 0x12, 0x04, 0xdf, 0x01, 0x1a, 0x1b, 0x0a, 0x34, 0x0a, + 0x02, 0x04, 0x14, 0x12, 0x06, 0xe3, 0x01, 0x00, 0xec, 0x01, 0x01, 0x1a, 0x26, 0x20, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x14, 0x01, 0x12, 0x04, 0xe3, 0x01, 0x08, 0x21, + 0x0a, 0x49, 0x0a, 0x04, 0x04, 0x14, 0x04, 0x00, 0x12, 0x06, 0xe5, 0x01, 0x02, 0xe9, 0x01, 0x03, + 0x1a, 0x39, 0x20, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, + 0x14, 0x04, 0x00, 0x01, 0x12, 0x04, 0xe5, 0x01, 0x07, 0x15, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x14, + 0x04, 0x00, 0x02, 0x00, 0x12, 0x04, 0xe6, 0x01, 0x04, 0x24, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x14, + 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xe6, 0x01, 0x04, 0x1f, 0x0a, 0x0f, 0x0a, 0x07, 0x04, + 0x14, 0x04, 0x00, 0x02, 0x00, 0x02, 0x12, 0x04, 0xe6, 0x01, 0x22, 0x23, 0x0a, 0x0e, 0x0a, 0x06, + 0x04, 0x14, 0x04, 0x00, 0x02, 0x01, 0x12, 0x04, 0xe7, 0x01, 0x04, 0x26, 0x0a, 0x0f, 0x0a, 0x07, + 0x04, 0x14, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0xe7, 0x01, 0x04, 0x21, 0x0a, 0x0f, 0x0a, + 0x07, 0x04, 0x14, 0x04, 0x00, 0x02, 0x01, 0x02, 0x12, 0x04, 0xe7, 0x01, 0x24, 0x25, 0x0a, 0x0e, + 0x0a, 0x06, 0x04, 0x14, 0x04, 0x00, 0x02, 0x02, 0x12, 0x04, 0xe8, 0x01, 0x04, 0x24, 0x0a, 0x0f, + 0x0a, 0x07, 0x04, 0x14, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, 0x04, 0xe8, 0x01, 0x04, 0x1f, 0x0a, + 0x0f, 0x0a, 0x07, 0x04, 0x14, 0x04, 0x00, 0x02, 0x02, 0x02, 0x12, 0x04, 0xe8, 0x01, 0x22, 0x23, + 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x14, 0x02, 0x00, 0x12, 0x04, 0xeb, 0x01, 0x02, 0x1c, 0x0a, 0x0d, + 0x0a, 0x05, 0x04, 0x14, 0x02, 0x00, 0x06, 0x12, 0x04, 0xeb, 0x01, 0x02, 0x10, 0x0a, 0x0d, 0x0a, + 0x05, 0x04, 0x14, 0x02, 0x00, 0x01, 0x12, 0x04, 0xeb, 0x01, 0x11, 0x17, 0x0a, 0x0d, 0x0a, 0x05, + 0x04, 0x14, 0x02, 0x00, 0x03, 0x12, 0x04, 0xeb, 0x01, 0x1a, 0x1b, 0x0a, 0x64, 0x0a, 0x02, 0x04, + 0x15, 0x12, 0x06, 0xf0, 0x01, 0x00, 0xf4, 0x01, 0x01, 0x1a, 0x56, 0x20, 0x55, 0x73, 0x65, 0x64, + 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x20, 0x61, 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x62, + 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x64, 0x20, + 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x15, 0x01, 0x12, 0x04, 0xf0, 0x01, 0x08, 0x1b, 0x0a, 0x0e, + 0x0a, 0x04, 0x04, 0x15, 0x08, 0x00, 0x12, 0x06, 0xf1, 0x01, 0x02, 0xf3, 0x01, 0x03, 0x0a, 0x0d, + 0x0a, 0x05, 0x04, 0x15, 0x08, 0x00, 0x01, 0x12, 0x04, 0xf1, 0x01, 0x08, 0x0e, 0x0a, 0x0c, 0x0a, + 0x04, 0x04, 0x15, 0x02, 0x00, 0x12, 0x04, 0xf2, 0x01, 0x04, 0x34, 0x0a, 0x0d, 0x0a, 0x05, 0x04, + 0x15, 0x02, 0x00, 0x06, 0x12, 0x04, 0xf2, 0x01, 0x04, 0x2c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x15, + 0x02, 0x00, 0x01, 0x12, 0x04, 0xf2, 0x01, 0x2d, 0x2f, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x15, 0x02, + 0x00, 0x03, 0x12, 0x04, 0xf2, 0x01, 0x32, 0x33, 0x0a, 0x42, 0x0a, 0x02, 0x04, 0x16, 0x12, 0x06, + 0xf7, 0x01, 0x00, 0xf9, 0x01, 0x01, 0x1a, 0x34, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, + 0x04, 0x16, 0x01, 0x12, 0x04, 0xf7, 0x01, 0x08, 0x1c, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x16, 0x02, + 0x00, 0x12, 0x04, 0xf8, 0x01, 0x02, 0x1a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x16, 0x02, 0x00, 0x06, + 0x12, 0x04, 0xf8, 0x01, 0x02, 0x0f, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x16, 0x02, 0x00, 0x01, 0x12, + 0x04, 0xf8, 0x01, 0x10, 0x15, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x16, 0x02, 0x00, 0x03, 0x12, 0x04, + 0xf8, 0x01, 0x18, 0x19, 0x0a, 0x64, 0x0a, 0x02, 0x04, 0x17, 0x12, 0x06, 0xfd, 0x01, 0x00, 0x83, + 0x02, 0x01, 0x1a, 0x56, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, + 0x6f, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x20, 0x61, 0x20, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x20, + 0x77, 0x69, 0x74, 0x68, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x0a, 0x20, 0x6f, 0x72, + 0x20, 0x61, 0x20, 0x70, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x17, + 0x01, 0x12, 0x04, 0xfd, 0x01, 0x08, 0x19, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x17, 0x02, 0x00, 0x12, + 0x04, 0xfe, 0x01, 0x02, 0x13, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x17, 0x02, 0x00, 0x05, 0x12, 0x04, + 0xfe, 0x01, 0x02, 0x07, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x17, 0x02, 0x00, 0x01, 0x12, 0x04, 0xfe, + 0x01, 0x08, 0x0e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x17, 0x02, 0x00, 0x03, 0x12, 0x04, 0xfe, 0x01, + 0x11, 0x12, 0x0a, 0x0e, 0x0a, 0x04, 0x04, 0x17, 0x08, 0x00, 0x12, 0x06, 0xff, 0x01, 0x02, 0x82, + 0x02, 0x03, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x17, 0x08, 0x00, 0x01, 0x12, 0x04, 0xff, 0x01, 0x08, + 0x0e, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x17, 0x02, 0x01, 0x12, 0x04, 0x80, 0x02, 0x04, 0x1a, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x17, 0x02, 0x01, 0x05, 0x12, 0x04, 0x80, 0x02, 0x04, 0x08, 0x0a, 0x0d, + 0x0a, 0x05, 0x04, 0x17, 0x02, 0x01, 0x01, 0x12, 0x04, 0x80, 0x02, 0x09, 0x15, 0x0a, 0x0d, 0x0a, + 0x05, 0x04, 0x17, 0x02, 0x01, 0x03, 0x12, 0x04, 0x80, 0x02, 0x18, 0x19, 0x0a, 0x0c, 0x0a, 0x04, + 0x04, 0x17, 0x02, 0x02, 0x12, 0x04, 0x81, 0x02, 0x04, 0x1c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x17, + 0x02, 0x02, 0x05, 0x12, 0x04, 0x81, 0x02, 0x04, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x17, 0x02, + 0x02, 0x01, 0x12, 0x04, 0x81, 0x02, 0x0b, 0x17, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x17, 0x02, 0x02, + 0x03, 0x12, 0x04, 0x81, 0x02, 0x1a, 0x1b, 0x0a, 0x58, 0x0a, 0x02, 0x05, 0x01, 0x12, 0x06, 0x86, + 0x02, 0x00, 0x8b, 0x02, 0x01, 0x1a, 0x4a, 0x20, 0x4a, 0x6f, 0x62, 0x54, 0x79, 0x70, 0x65, 0x20, + 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, + 0x66, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x6a, + 0x6f, 0x62, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x20, 0x77, 0x6f, + 0x75, 0x6c, 0x64, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x20, 0x6f, 0x6e, + 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x05, 0x01, 0x01, 0x12, 0x04, 0x86, 0x02, 0x05, 0x0c, 0x0a, 0x0c, + 0x0a, 0x04, 0x05, 0x01, 0x02, 0x00, 0x12, 0x04, 0x87, 0x02, 0x02, 0x1b, 0x0a, 0x0d, 0x0a, 0x05, + 0x05, 0x01, 0x02, 0x00, 0x01, 0x12, 0x04, 0x87, 0x02, 0x02, 0x16, 0x0a, 0x0d, 0x0a, 0x05, 0x05, + 0x01, 0x02, 0x00, 0x02, 0x12, 0x04, 0x87, 0x02, 0x19, 0x1a, 0x0a, 0x0c, 0x0a, 0x04, 0x05, 0x01, + 0x02, 0x01, 0x12, 0x04, 0x88, 0x02, 0x02, 0x1a, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x01, + 0x01, 0x12, 0x04, 0x88, 0x02, 0x02, 0x15, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x01, 0x02, + 0x12, 0x04, 0x88, 0x02, 0x18, 0x19, 0x0a, 0x0c, 0x0a, 0x04, 0x05, 0x01, 0x02, 0x02, 0x12, 0x04, + 0x89, 0x02, 0x02, 0x1a, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x02, 0x01, 0x12, 0x04, 0x89, + 0x02, 0x02, 0x15, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x02, 0x02, 0x12, 0x04, 0x89, 0x02, + 0x18, 0x19, 0x0a, 0x0c, 0x0a, 0x04, 0x05, 0x01, 0x02, 0x03, 0x12, 0x04, 0x8a, 0x02, 0x02, 0x1c, + 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x03, 0x01, 0x12, 0x04, 0x8a, 0x02, 0x02, 0x17, 0x0a, + 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x03, 0x02, 0x12, 0x04, 0x8a, 0x02, 0x1a, 0x1b, 0x0a, 0x56, + 0x0a, 0x02, 0x04, 0x18, 0x12, 0x06, 0x8e, 0x02, 0x00, 0x90, 0x02, 0x01, 0x1a, 0x48, 0x20, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x67, + 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x72, 0x75, 0x6e, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, + 0x68, 0x20, 0x6a, 0x6f, 0x62, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x18, 0x01, 0x12, 0x04, 0x8e, + 0x02, 0x08, 0x1c, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x18, 0x02, 0x00, 0x12, 0x04, 0x8f, 0x02, 0x02, + 0x17, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x18, 0x02, 0x00, 0x06, 0x12, 0x04, 0x8f, 0x02, 0x02, 0x09, + 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x18, 0x02, 0x00, 0x01, 0x12, 0x04, 0x8f, 0x02, 0x0a, 0x12, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x18, 0x02, 0x00, 0x03, 0x12, 0x04, 0x8f, 0x02, 0x15, 0x16, 0x0a, 0x5a, + 0x0a, 0x02, 0x04, 0x19, 0x12, 0x06, 0x93, 0x02, 0x00, 0x95, 0x02, 0x01, 0x1a, 0x4c, 0x20, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, + 0x72, 0x75, 0x6e, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x72, 0x65, + 0x66, 0x72, 0x65, 0x73, 0x68, 0x20, 0x6a, 0x6f, 0x62, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x19, + 0x01, 0x12, 0x04, 0x93, 0x02, 0x08, 0x1d, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x19, 0x02, 0x00, 0x12, + 0x04, 0x94, 0x02, 0x02, 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x19, 0x02, 0x00, 0x05, 0x12, 0x04, + 0x94, 0x02, 0x02, 0x07, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x19, 0x02, 0x00, 0x01, 0x12, 0x04, 0x94, + 0x02, 0x08, 0x13, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x19, 0x02, 0x00, 0x03, 0x12, 0x04, 0x94, 0x02, + 0x16, 0x17, 0x0a, 0x56, 0x0a, 0x02, 0x04, 0x1a, 0x12, 0x06, 0x98, 0x02, 0x00, 0x9b, 0x02, 0x01, + 0x1a, 0x48, 0x20, 0x53, 0x65, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, + 0x74, 0x6f, 0x20, 0x73, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, + 0x72, 0x75, 0x6e, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x72, 0x65, + 0x66, 0x72, 0x65, 0x73, 0x68, 0x20, 0x6a, 0x6f, 0x62, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x1a, + 0x01, 0x12, 0x04, 0x98, 0x02, 0x08, 0x1b, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x1a, 0x02, 0x00, 0x12, + 0x04, 0x99, 0x02, 0x02, 0x17, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1a, 0x02, 0x00, 0x06, 0x12, 0x04, + 0x99, 0x02, 0x02, 0x09, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1a, 0x02, 0x00, 0x01, 0x12, 0x04, 0x99, + 0x02, 0x0a, 0x12, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1a, 0x02, 0x00, 0x03, 0x12, 0x04, 0x99, 0x02, + 0x15, 0x16, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x1a, 0x02, 0x01, 0x12, 0x04, 0x9a, 0x02, 0x02, 0x18, + 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1a, 0x02, 0x01, 0x05, 0x12, 0x04, 0x9a, 0x02, 0x02, 0x07, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x1a, 0x02, 0x01, 0x01, 0x12, 0x04, 0x9a, 0x02, 0x08, 0x13, 0x0a, 0x0d, + 0x0a, 0x05, 0x04, 0x1a, 0x02, 0x01, 0x03, 0x12, 0x04, 0x9a, 0x02, 0x16, 0x17, 0x0a, 0x3d, 0x0a, + 0x02, 0x04, 0x1b, 0x12, 0x04, 0x9e, 0x02, 0x00, 0x20, 0x1a, 0x31, 0x20, 0x53, 0x65, 0x74, 0x52, + 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, + 0x04, 0x1b, 0x01, 0x12, 0x04, 0x9e, 0x02, 0x08, 0x1d, 0x0a, 0x42, 0x0a, 0x02, 0x04, 0x1c, 0x12, + 0x06, 0xa1, 0x02, 0x00, 0xa9, 0x02, 0x01, 0x1a, 0x34, 0x20, 0x41, 0x20, 0x6d, 0x61, 0x70, 0x70, + 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x20, 0x74, 0x6f, + 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, + 0x20, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, + 0x03, 0x04, 0x1c, 0x01, 0x12, 0x04, 0xa1, 0x02, 0x08, 0x10, 0x0a, 0x51, 0x0a, 0x04, 0x04, 0x1c, + 0x03, 0x00, 0x12, 0x06, 0xa3, 0x02, 0x02, 0xa7, 0x02, 0x03, 0x1a, 0x41, 0x20, 0x54, 0x6f, 0x70, + 0x69, 0x63, 0x44, 0x61, 0x74, 0x61, 0x20, 0x77, 0x72, 0x61, 0x70, 0x73, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x69, 0x74, + 0x20, 0x77, 0x61, 0x73, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x0a, 0x0a, 0x0d, 0x0a, + 0x05, 0x04, 0x1c, 0x03, 0x00, 0x01, 0x12, 0x04, 0xa3, 0x02, 0x0a, 0x13, 0x0a, 0x0e, 0x0a, 0x06, + 0x04, 0x1c, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0xa4, 0x02, 0x04, 0x1a, 0x0a, 0x0f, 0x0a, 0x07, + 0x04, 0x1c, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0xa4, 0x02, 0x04, 0x0a, 0x0a, 0x0f, 0x0a, + 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xa4, 0x02, 0x0b, 0x15, 0x0a, 0x0f, + 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0xa4, 0x02, 0x18, 0x19, 0x0a, + 0x0e, 0x0a, 0x06, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, 0xa5, 0x02, 0x04, 0x1c, 0x0a, + 0x0f, 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x04, 0xa5, 0x02, 0x04, 0x0a, + 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0xa5, 0x02, 0x0b, + 0x17, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, 0xa5, 0x02, + 0x1a, 0x1b, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x02, 0x12, 0x04, 0xa6, 0x02, + 0x04, 0x36, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x02, 0x06, 0x12, 0x04, 0xa6, + 0x02, 0x04, 0x26, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x04, + 0xa6, 0x02, 0x27, 0x31, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1c, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, + 0x04, 0xa6, 0x02, 0x34, 0x35, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x1c, 0x02, 0x00, 0x12, 0x04, 0xa8, + 0x02, 0x02, 0x24, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1c, 0x02, 0x00, 0x06, 0x12, 0x04, 0xa8, 0x02, + 0x02, 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1c, 0x02, 0x00, 0x01, 0x12, 0x04, 0xa8, 0x02, 0x19, + 0x1f, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1c, 0x02, 0x00, 0x03, 0x12, 0x04, 0xa8, 0x02, 0x22, 0x23, + 0x0a, 0x4f, 0x0a, 0x02, 0x04, 0x1d, 0x12, 0x06, 0xac, 0x02, 0x00, 0xae, 0x02, 0x01, 0x1a, 0x41, + 0x20, 0x55, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x67, 0x65, 0x74, 0x20, 0x61, 0x20, 0x6d, + 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x20, 0x74, 0x6f, + 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x6b, 0x65, 0x79, 0x73, + 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x1d, 0x01, 0x12, 0x04, 0xac, 0x02, 0x08, 0x26, 0x0a, 0x0c, + 0x0a, 0x04, 0x04, 0x1d, 0x02, 0x00, 0x12, 0x04, 0xad, 0x02, 0x02, 0x1d, 0x0a, 0x0d, 0x0a, 0x05, + 0x04, 0x1d, 0x02, 0x00, 0x04, 0x12, 0x04, 0xad, 0x02, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, + 0x1d, 0x02, 0x00, 0x05, 0x12, 0x04, 0xad, 0x02, 0x0b, 0x11, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1d, + 0x02, 0x00, 0x01, 0x12, 0x04, 0xad, 0x02, 0x12, 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1d, 0x02, + 0x00, 0x03, 0x12, 0x04, 0xad, 0x02, 0x1b, 0x1c, 0x0a, 0x36, 0x0a, 0x02, 0x04, 0x1e, 0x12, 0x06, + 0xb1, 0x02, 0x00, 0xbc, 0x02, 0x01, 0x1a, 0x28, 0x20, 0x41, 0x20, 0x6d, 0x61, 0x70, 0x70, 0x69, + 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x20, 0x74, 0x6f, 0x20, + 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x0a, + 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x1e, 0x01, 0x12, 0x04, 0xb1, 0x02, 0x08, 0x27, 0x0a, 0x5d, 0x0a, + 0x04, 0x04, 0x1e, 0x03, 0x00, 0x12, 0x06, 0xb3, 0x02, 0x02, 0xb6, 0x02, 0x03, 0x1a, 0x4d, 0x20, + 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x44, 0x61, 0x74, 0x61, 0x20, 0x77, 0x72, 0x61, 0x70, + 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, + 0x20, 0x33, 0x30, 0x20, 0x64, 0x61, 0x79, 0x20, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x73, 0x20, + 0x73, 0x69, 0x6e, 0x63, 0x65, 0x20, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, + 0x04, 0x1e, 0x03, 0x00, 0x01, 0x12, 0x04, 0xb3, 0x02, 0x0a, 0x15, 0x0a, 0x0e, 0x0a, 0x06, 0x04, + 0x1e, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0xb4, 0x02, 0x04, 0x2d, 0x0a, 0x0f, 0x0a, 0x07, 0x04, + 0x1e, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0xb4, 0x02, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, + 0x04, 0x1e, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xb4, 0x02, 0x0a, 0x28, 0x0a, 0x0f, 0x0a, + 0x07, 0x04, 0x1e, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0xb4, 0x02, 0x2b, 0x2c, 0x0a, 0x0e, + 0x0a, 0x06, 0x04, 0x1e, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, 0xb5, 0x02, 0x04, 0x17, 0x0a, 0x0f, + 0x0a, 0x07, 0x04, 0x1e, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x04, 0xb5, 0x02, 0x04, 0x09, 0x0a, + 0x0f, 0x0a, 0x07, 0x04, 0x1e, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0xb5, 0x02, 0x0a, 0x12, + 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1e, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, 0xb5, 0x02, 0x15, + 0x16, 0x0a, 0x42, 0x0a, 0x04, 0x04, 0x1e, 0x03, 0x01, 0x12, 0x06, 0xb8, 0x02, 0x02, 0xba, 0x02, + 0x03, 0x1a, 0x32, 0x20, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x20, 0x72, 0x65, 0x70, + 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, + 0x20, 0x48, 0x6d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x44, 0x61, 0x74, 0x61, 0x20, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1e, 0x03, 0x01, 0x01, 0x12, 0x04, + 0xb8, 0x02, 0x0a, 0x12, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x1e, 0x03, 0x01, 0x02, 0x00, 0x12, 0x04, + 0xb9, 0x02, 0x04, 0x24, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1e, 0x03, 0x01, 0x02, 0x00, 0x04, 0x12, + 0x04, 0xb9, 0x02, 0x04, 0x0c, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1e, 0x03, 0x01, 0x02, 0x00, 0x06, + 0x12, 0x04, 0xb9, 0x02, 0x0d, 0x18, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1e, 0x03, 0x01, 0x02, 0x00, + 0x01, 0x12, 0x04, 0xb9, 0x02, 0x19, 0x1f, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x1e, 0x03, 0x01, 0x02, + 0x00, 0x03, 0x12, 0x04, 0xb9, 0x02, 0x22, 0x23, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x1e, 0x02, 0x00, + 0x12, 0x04, 0xbb, 0x02, 0x02, 0x26, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1e, 0x02, 0x00, 0x06, 0x12, + 0x04, 0xbb, 0x02, 0x02, 0x17, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1e, 0x02, 0x00, 0x01, 0x12, 0x04, + 0xbb, 0x02, 0x18, 0x21, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x1e, 0x02, 0x00, 0x03, 0x12, 0x04, 0xbb, + 0x02, 0x24, 0x25, 0x0a, 0x4f, 0x0a, 0x02, 0x04, 0x1f, 0x12, 0x06, 0xbf, 0x02, 0x00, 0xc1, 0x02, + 0x01, 0x1a, 0x41, 0x20, 0x41, 0x20, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, + 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x20, + 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x20, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x20, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x1f, 0x01, 0x12, 0x04, 0xbf, 0x02, 0x08, + 0x23, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x1f, 0x02, 0x00, 0x12, 0x04, 0xc0, 0x02, 0x02, 0x4a, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x1f, 0x02, 0x00, 0x06, 0x12, 0x04, 0xc0, 0x02, 0x02, 0x3d, 0x0a, 0x0d, + 0x0a, 0x05, 0x04, 0x1f, 0x02, 0x00, 0x01, 0x12, 0x04, 0xc0, 0x02, 0x3e, 0x45, 0x0a, 0x0d, 0x0a, + 0x05, 0x04, 0x1f, 0x02, 0x00, 0x03, 0x12, 0x04, 0xc0, 0x02, 0x48, 0x49, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, ]; include!("xmtp.keystore_api.v1.serde.rs"); // @@protoc_insertion_point(module) \ No newline at end of file diff --git a/xmtp_proto/src/gen/xmtp.keystore_api.v1.serde.rs b/xmtp_proto/src/gen/xmtp.keystore_api.v1.serde.rs index 7160e4c43..1ad7f6d66 100644 --- a/xmtp_proto/src/gen/xmtp.keystore_api.v1.serde.rs +++ b/xmtp_proto/src/gen/xmtp.keystore_api.v1.serde.rs @@ -13,6 +13,7 @@ impl serde::Serialize for CreateAuthTokenRequest { let mut struct_ser = serializer.serialize_struct("xmtp.keystore_api.v1.CreateAuthTokenRequest", len)?; if let Some(v) = self.timestamp_ns.as_ref() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("timestampNs", ToString::to_string(&v).as_str())?; } struct_ser.end() @@ -123,6 +124,7 @@ impl serde::Serialize for CreateInviteRequest { } if self.created_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("createdNs", ToString::to_string(&self.created_ns).as_str())?; } if let Some(v) = self.consent_proof.as_ref() { @@ -261,6 +263,7 @@ impl serde::Serialize for CreateInviteResponse { } 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() @@ -566,6 +569,7 @@ impl serde::Serialize for decrypt_response::response::Success { let mut struct_ser = serializer.serialize_struct("xmtp.keystore_api.v1.DecryptResponse.Response.Success", len)?; if !self.decrypted.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("decrypted", pbjson::private::base64::encode(&self.decrypted).as_str())?; } struct_ser.end() @@ -766,6 +770,7 @@ impl serde::Serialize for decrypt_v1_request::Request { } if !self.header_bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("headerBytes", pbjson::private::base64::encode(&self.header_bytes).as_str())?; } if self.is_sender { @@ -999,6 +1004,7 @@ impl serde::Serialize for decrypt_v2_request::Request { } if !self.header_bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("headerBytes", pbjson::private::base64::encode(&self.header_bytes).as_str())?; } if !self.content_topic.is_empty() { @@ -1326,6 +1332,7 @@ impl serde::Serialize for encrypt_response::response::Success { } if !self.sender_hmac.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("senderHmac", pbjson::private::base64::encode(&self.sender_hmac).as_str())?; } struct_ser.end() @@ -1532,10 +1539,12 @@ impl serde::Serialize for encrypt_v1_request::Request { } 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())?; } if !self.header_bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("headerBytes", pbjson::private::base64::encode(&self.header_bytes).as_str())?; } struct_ser.end() @@ -1752,10 +1761,12 @@ impl serde::Serialize for encrypt_v2_request::Request { let mut struct_ser = serializer.serialize_struct("xmtp.keystore_api.v1.EncryptV2Request.Request", len)?; 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())?; } if !self.header_bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("headerBytes", pbjson::private::base64::encode(&self.header_bytes).as_str())?; } if !self.content_topic.is_empty() { @@ -2144,6 +2155,7 @@ impl serde::Serialize for get_conversation_hmac_keys_response::HmacKeyData { } if !self.hmac_key.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("hmacKey", pbjson::private::base64::encode(&self.hmac_key).as_str())?; } struct_ser.end() @@ -2879,6 +2891,7 @@ impl serde::Serialize for GetRefreshJobResponse { let mut struct_ser = serializer.serialize_struct("xmtp.keystore_api.v1.GetRefreshJobResponse", len)?; if self.last_run_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("lastRunNs", ToString::to_string(&self.last_run_ns).as_str())?; } struct_ser.end() @@ -3334,6 +3347,99 @@ impl<'de> serde::Deserialize<'de> for KeystoreError { deserializer.deserialize_struct("xmtp.keystore_api.v1.KeystoreError", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for PrivatePreferencesActionMap { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.actions.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.keystore_api.v1.PrivatePreferencesActionMap", len)?; + if !self.actions.is_empty() { + struct_ser.serialize_field("actions", &self.actions)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for PrivatePreferencesActionMap { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "actions", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Actions, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "actions" => Ok(GeneratedField::Actions), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = PrivatePreferencesActionMap; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.keystore_api.v1.PrivatePreferencesActionMap") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut actions__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Actions => { + if actions__.is_some() { + return Err(serde::de::Error::duplicate_field("actions")); + } + actions__ = Some( + map_.next_value::>()? + ); + } + } + } + Ok(PrivatePreferencesActionMap { + actions: actions__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.keystore_api.v1.PrivatePreferencesActionMap", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for SaveInvitesRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -3448,10 +3554,12 @@ impl serde::Serialize for save_invites_request::Request { } if self.timestamp_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("timestampNs", ToString::to_string(&self.timestamp_ns).as_str())?; } 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() @@ -4116,6 +4224,7 @@ impl serde::Serialize for self_decrypt_request::Request { let mut struct_ser = serializer.serialize_struct("xmtp.keystore_api.v1.SelfDecryptRequest.Request", len)?; 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() @@ -4301,6 +4410,7 @@ impl serde::Serialize for self_encrypt_request::Request { let mut struct_ser = serializer.serialize_struct("xmtp.keystore_api.v1.SelfEncryptRequest.Request", len)?; 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() @@ -4595,6 +4705,7 @@ impl serde::Serialize for self_encrypt_response::response::Success { let mut struct_ser = serializer.serialize_struct("xmtp.keystore_api.v1.SelfEncryptResponse.Response.Success", len)?; if !self.encrypted.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("encrypted", pbjson::private::base64::encode(&self.encrypted).as_str())?; } struct_ser.end() @@ -4697,6 +4808,7 @@ impl serde::Serialize for SetRefeshJobRequest { } if self.last_run_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("lastRunNs", ToString::to_string(&self.last_run_ns).as_str())?; } struct_ser.end() @@ -4878,6 +4990,7 @@ impl serde::Serialize for SignDigestRequest { let mut struct_ser = serializer.serialize_struct("xmtp.keystore_api.v1.SignDigestRequest", len)?; if !self.digest.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("digest", pbjson::private::base64::encode(&self.digest).as_str())?; } if let Some(v) = self.signer.as_ref() { @@ -5103,6 +5216,7 @@ impl serde::Serialize for topic_map::TopicData { let mut struct_ser = serializer.serialize_struct("xmtp.keystore_api.v1.TopicMap.TopicData", len)?; if self.created_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("createdNs", ToString::to_string(&self.created_ns).as_str())?; } if !self.peer_address.is_empty() { diff --git a/xmtp_proto/src/gen/xmtp.message_api.v1.rs b/xmtp_proto/src/gen/xmtp.message_api.v1.rs index 2c049df29..cdc466411 100644 --- a/xmtp_proto/src/gen/xmtp.message_api.v1.rs +++ b/xmtp_proto/src/gen/xmtp.message_api.v1.rs @@ -1,4 +1,5 @@ // @generated +// This file is @generated by prost-build. /// Token is used by clients to prove to the nodes /// that they are serving a specific wallet. #[allow(clippy::derive_partial_eq_without_eq)] @@ -101,7 +102,7 @@ pub struct PublishRequest { } /// Empty message as a response for Publish #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct PublishResponse { } /// Subscribe @@ -113,7 +114,7 @@ pub struct SubscribeRequest { } /// SubscribeAll #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct SubscribeAllRequest { } /// Query diff --git a/xmtp_proto/src/gen/xmtp.message_api.v1.serde.rs b/xmtp_proto/src/gen/xmtp.message_api.v1.serde.rs index bb932c481..2fae2170d 100644 --- a/xmtp_proto/src/gen/xmtp.message_api.v1.serde.rs +++ b/xmtp_proto/src/gen/xmtp.message_api.v1.serde.rs @@ -19,6 +19,7 @@ impl serde::Serialize for AuthData { } if self.created_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("createdNs", ToString::to_string(&self.created_ns).as_str())?; } struct_ser.end() @@ -413,10 +414,12 @@ impl serde::Serialize for Envelope { } if self.timestamp_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("timestampNs", ToString::to_string(&self.timestamp_ns).as_str())?; } if !self.message.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("message", pbjson::private::base64::encode(&self.message).as_str())?; } struct_ser.end() @@ -540,10 +543,12 @@ impl serde::Serialize for IndexCursor { let mut struct_ser = serializer.serialize_struct("xmtp.message_api.v1.IndexCursor", len)?; if !self.digest.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("digest", pbjson::private::base64::encode(&self.digest).as_str())?; } if self.sender_time_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("senderTimeNs", ToString::to_string(&self.sender_time_ns).as_str())?; } struct_ser.end() @@ -955,10 +960,12 @@ impl serde::Serialize for QueryRequest { } if self.start_time_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("startTimeNs", ToString::to_string(&self.start_time_ns).as_str())?; } if self.end_time_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("endTimeNs", ToString::to_string(&self.end_time_ns).as_str())?; } if let Some(v) = self.paging_info.as_ref() { @@ -1450,6 +1457,7 @@ impl serde::Serialize for Token { } if !self.auth_data_bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("authDataBytes", pbjson::private::base64::encode(&self.auth_data_bytes).as_str())?; } if let Some(v) = self.auth_data_signature.as_ref() { diff --git a/xmtp_proto/src/gen/xmtp.message_api.v1.tonic.rs b/xmtp_proto/src/gen/xmtp.message_api.v1.tonic.rs index 771e0335f..5876f968a 100644 --- a/xmtp_proto/src/gen/xmtp.message_api.v1.tonic.rs +++ b/xmtp_proto/src/gen/xmtp.message_api.v1.tonic.rs @@ -1,6 +1,6 @@ // @generated /// Generated client implementations. -#[cfg(feature = "tonic")] +#[cfg(not(target_arch = "wasm32"))] pub mod message_api_client { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] use tonic::codegen::*; @@ -237,7 +237,7 @@ pub mod message_api_client { } } /// Generated server implementations. -#[cfg(feature = "tonic")] +#[cfg(not(target_arch = "wasm32"))] pub mod message_api_server { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] use tonic::codegen::*; @@ -295,19 +295,17 @@ pub mod message_api_server { } #[derive(Debug)] pub struct MessageApiServer { - inner: _Inner, + inner: Arc, accept_compression_encodings: EnabledCompressionEncodings, send_compression_encodings: EnabledCompressionEncodings, max_decoding_message_size: Option, max_encoding_message_size: Option, } - struct _Inner(Arc); impl MessageApiServer { pub fn new(inner: T) -> Self { Self::from_arc(Arc::new(inner)) } pub fn from_arc(inner: Arc) -> Self { - let inner = _Inner(inner); Self { inner, accept_compression_encodings: Default::default(), @@ -370,7 +368,6 @@ pub mod message_api_server { Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { - let inner = self.inner.clone(); match req.uri().path() { "/xmtp.message_api.v1.MessageApi/Publish" => { #[allow(non_camel_case_types)] @@ -401,7 +398,6 @@ pub mod message_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = PublishSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -448,7 +444,6 @@ pub mod message_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = SubscribeSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -497,7 +492,6 @@ pub mod message_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = Subscribe2Svc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -544,7 +538,6 @@ pub mod message_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = SubscribeAllSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -588,7 +581,6 @@ pub mod message_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = QuerySvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -634,7 +626,6 @@ pub mod message_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = BatchQuerySvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -656,8 +647,11 @@ pub mod message_api_server { Ok( http::Response::builder() .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") + .header("grpc-status", tonic::Code::Unimplemented as i32) + .header( + http::header::CONTENT_TYPE, + tonic::metadata::GRPC_CONTENT_TYPE, + ) .body(empty_body()) .unwrap(), ) @@ -678,16 +672,6 @@ pub mod message_api_server { } } } - impl Clone for _Inner { - fn clone(&self) -> Self { - Self(Arc::clone(&self.0)) - } - } - impl std::fmt::Debug for _Inner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - } - } impl tonic::server::NamedService for MessageApiServer { const NAME: &'static str = "xmtp.message_api.v1.MessageApi"; } diff --git a/xmtp_proto/src/gen/xmtp.message_contents.rs b/xmtp_proto/src/gen/xmtp.message_contents.rs index 46078b71c..b4ec8ba5f 100644 --- a/xmtp_proto/src/gen/xmtp.message_contents.rs +++ b/xmtp_proto/src/gen/xmtp.message_contents.rs @@ -1,4 +1,5 @@ // @generated +// This file is @generated by prost-build. /// Signature represents a generalized public key signature, /// defined as a union to support cryptographic algorithm agility. #[allow(clippy::derive_partial_eq_without_eq)] @@ -496,6 +497,97 @@ pub mod encrypted_private_key_bundle { V1(super::EncryptedPrivateKeyBundleV1), } } +/// PrivatePreferencesAction is a message used to update the client's preference +/// store. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PrivatePreferencesAction { + #[prost(oneof="private_preferences_action::MessageType", tags="1, 2, 3, 4, 5, 6")] + pub message_type: ::core::option::Option, +} +/// Nested message and enum types in `PrivatePreferencesAction`. +pub mod private_preferences_action { + /// Allow 1:1 direct message (DM) access + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct AllowAddress { + /// Add the given wallet addresses to the allow list + #[prost(string, repeated, tag="1")] + pub wallet_addresses: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + } + /// Deny (block) 1:1 direct message (DM) access + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct DenyAddress { + /// Add the given wallet addresses to the deny list + #[prost(string, repeated, tag="1")] + pub wallet_addresses: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + } + /// Allow V3 1:1 direct message (DM) access + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct AllowInboxId { + /// Add the given inbox id to the allow list + #[prost(string, repeated, tag="1")] + pub inbox_ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + } + /// Deny (block) V3 1:1 direct message (DM) access + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct DenyInboxId { + /// Add the given inbox id to the deny list + #[prost(string, repeated, tag="1")] + pub inbox_ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + } + /// Allow Group access + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct AllowGroup { + /// Add the given group_ids to the allow list + #[prost(string, repeated, tag="1")] + pub group_ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + } + /// Deny (deny) Group access + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct DenyGroup { + /// Add the given group_ids to the deny list + #[prost(string, repeated, tag="1")] + pub group_ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum MessageType { + #[prost(message, tag="1")] + AllowAddress(AllowAddress), + #[prost(message, tag="2")] + DenyAddress(DenyAddress), + #[prost(message, tag="3")] + AllowGroup(AllowGroup), + #[prost(message, tag="4")] + DenyGroup(DenyGroup), + #[prost(message, tag="5")] + AllowInboxId(AllowInboxId), + #[prost(message, tag="6")] + DenyInboxId(DenyInboxId), + } +} +/// The payload that goes over the wire +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PrivatePreferencesPayload { + #[prost(oneof="private_preferences_payload::Version", tags="1")] + pub version: ::core::option::Option, +} +/// Nested message and enum types in `PrivatePreferencesPayload`. +pub mod private_preferences_payload { + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Version { + #[prost(message, tag="1")] + V1(super::Ciphertext), + } +} /// ContentTypeId is used to identify the type of content stored in a Message. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -812,97 +904,6 @@ pub struct DecodedMessage { #[prost(bytes="vec", tag="8")] pub content_bytes: ::prost::alloc::vec::Vec, } -/// PrivatePreferencesAction is a message used to update the client's preference -/// store. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PrivatePreferencesAction { - #[prost(oneof="private_preferences_action::MessageType", tags="1, 2, 3, 4, 5, 6")] - pub message_type: ::core::option::Option, -} -/// Nested message and enum types in `PrivatePreferencesAction`. -pub mod private_preferences_action { - /// Allow 1:1 direct message (DM) access - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct AllowAddress { - /// Add the given wallet addresses to the allow list - #[prost(string, repeated, tag="1")] - pub wallet_addresses: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - } - /// Deny (block) 1:1 direct message (DM) access - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct DenyAddress { - /// Add the given wallet addresses to the deny list - #[prost(string, repeated, tag="1")] - pub wallet_addresses: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - } - /// Allow V3 1:1 direct message (DM) access - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct AllowInboxId { - /// Add the given inbox id to the allow list - #[prost(string, repeated, tag="1")] - pub inbox_ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - } - /// Deny (block) V3 1:1 direct message (DM) access - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct DenyInboxId { - /// Add the given inbox id to the deny list - #[prost(string, repeated, tag="1")] - pub inbox_ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - } - /// Allow Group access - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct AllowGroup { - /// Add the given group_ids to the allow list - #[prost(bytes="vec", repeated, tag="1")] - pub group_ids: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, - } - /// Deny (deny) Group access - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct DenyGroup { - /// Add the given group_ids to the deny list - #[prost(bytes="vec", repeated, tag="1")] - pub group_ids: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum MessageType { - #[prost(message, tag="1")] - AllowAddress(AllowAddress), - #[prost(message, tag="2")] - DenyAddress(DenyAddress), - #[prost(message, tag="3")] - AllowGroup(AllowGroup), - #[prost(message, tag="4")] - DenyGroup(DenyGroup), - #[prost(message, tag="5")] - AllowInboxId(AllowInboxId), - #[prost(message, tag="6")] - DenyInboxId(DenyInboxId), - } -} -/// The payload that goes over the wire -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PrivatePreferencesPayload { - #[prost(oneof="private_preferences_payload::Version", tags="1")] - pub version: ::core::option::Option, -} -/// Nested message and enum types in `PrivatePreferencesPayload`. -pub mod private_preferences_payload { - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Version { - #[prost(message, tag="1")] - V1(super::Ciphertext), - } -} /// SignedPayload is a wrapper for a signature and a payload #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -2006,227 +2007,80 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[ 0x04, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x06, 0x12, 0x03, 0x5b, 0x04, 0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x01, 0x12, 0x03, 0x5b, 0x20, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x03, 0x12, 0x03, 0x5b, 0x25, 0x26, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xdd, 0x18, 0x0a, 0x1e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x21, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x1a, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0x95, 0x01, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, - 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x79, 0x70, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x79, 0x70, 0x65, 0x49, - 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6a, - 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x5f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x22, 0x83, 0x03, 0x0a, 0x0e, - 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x38, - 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, - 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x49, 0x64, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x55, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, - 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x78, - 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, - 0x1f, 0x0a, 0x08, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x00, 0x52, 0x08, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x88, 0x01, 0x01, - 0x12, 0x49, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, - 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x01, 0x52, 0x0b, 0x63, 0x6f, 0x6d, - 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x18, 0x0a, 0x07, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, - 0x6b, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x22, 0xaf, 0x01, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x44, 0x0a, - 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x06, 0x73, 0x65, 0x6e, - 0x64, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x53, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x2a, 0x3c, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, - 0x4e, 0x5f, 0x44, 0x45, 0x46, 0x4c, 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x43, - 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x47, 0x5a, 0x49, 0x50, 0x10, - 0x01, 0x42, 0xc8, 0x01, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, - 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, - 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, - 0x58, 0x4d, 0x58, 0xaa, 0x02, 0x14, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, 0x14, 0x58, 0x6d, 0x74, - 0x70, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x73, 0xe2, 0x02, 0x20, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xfd, 0x0f, 0x0a, - 0x06, 0x12, 0x04, 0x01, 0x00, 0x36, 0x01, 0x0a, 0x2f, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, - 0x12, 0x1a, 0x25, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, - 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x03, - 0x00, 0x1e, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x05, 0x00, 0x2b, 0x0a, 0x09, 0x0a, - 0x02, 0x03, 0x01, 0x12, 0x03, 0x06, 0x00, 0x2a, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x08, - 0x00, 0x43, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x08, 0x00, 0x43, 0x0a, 0x58, 0x0a, - 0x02, 0x04, 0x00, 0x12, 0x04, 0x0c, 0x00, 0x11, 0x01, 0x1a, 0x4c, 0x20, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x49, 0x64, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, - 0x64, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, - 0x0c, 0x08, 0x15, 0x0a, 0x34, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0d, 0x02, 0x1a, - 0x22, 0x27, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x67, 0x6f, 0x76, - 0x65, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, - 0x00, 0x05, 0x12, 0x03, 0x0d, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, - 0x12, 0x03, 0x0d, 0x09, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, - 0x0d, 0x18, 0x19, 0x0a, 0x1e, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x0e, 0x02, 0x15, - 0x22, 0x11, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, - 0x65, 0x72, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x0e, 0x02, - 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x0e, 0x09, 0x10, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x0e, 0x13, 0x14, 0x0a, 0x28, 0x0a, - 0x04, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x0f, 0x02, 0x1b, 0x22, 0x1b, 0x20, 0x6d, 0x61, 0x6a, - 0x6f, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x05, - 0x12, 0x03, 0x0f, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, - 0x0f, 0x09, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x0f, 0x19, - 0x1a, 0x0a, 0x28, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x03, 0x12, 0x03, 0x10, 0x02, 0x1b, 0x22, 0x1b, - 0x20, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, - 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x00, 0x02, 0x03, 0x05, 0x12, 0x03, 0x10, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, - 0x03, 0x01, 0x12, 0x03, 0x10, 0x09, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x03, - 0x12, 0x03, 0x10, 0x19, 0x1a, 0x0a, 0x67, 0x0a, 0x02, 0x05, 0x00, 0x12, 0x04, 0x15, 0x00, 0x18, - 0x01, 0x1a, 0x5b, 0x20, 0x52, 0x65, 0x63, 0x6f, 0x67, 0x6e, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x63, - 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6c, 0x67, 0x6f, 0x72, - 0x69, 0x74, 0x68, 0x6d, 0x73, 0x0a, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6c, 0x69, 0x6e, 0x74, - 0x3a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x46, 0x49, - 0x45, 0x4c, 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x56, - 0x41, 0x4c, 0x55, 0x45, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x57, 0x49, 0x54, 0x48, 0x0a, 0x0a, 0x0a, - 0x0a, 0x03, 0x05, 0x00, 0x01, 0x12, 0x03, 0x15, 0x05, 0x10, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, - 0x02, 0x00, 0x12, 0x03, 0x16, 0x02, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, - 0x12, 0x03, 0x16, 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, - 0x16, 0x18, 0x19, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x01, 0x12, 0x03, 0x17, 0x02, 0x17, - 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x17, 0x02, 0x12, 0x0a, 0x0c, - 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x17, 0x15, 0x16, 0x0a, 0xa3, 0x01, 0x0a, - 0x02, 0x04, 0x01, 0x12, 0x04, 0x1d, 0x00, 0x2b, 0x01, 0x1a, 0x96, 0x01, 0x20, 0x45, 0x6e, 0x63, - 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x62, 0x75, 0x6e, 0x64, - 0x6c, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, - 0x77, 0x69, 0x74, 0x68, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x73, 0x20, 0x74, 0x79, - 0x70, 0x65, 0x0a, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, - 0x72, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, - 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, - 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x1d, 0x08, 0x16, 0x0a, 0x65, - 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x20, 0x02, 0x19, 0x1a, 0x58, 0x20, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x61, - 0x74, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, - 0x77, 0x69, 0x74, 0x68, 0x0a, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, - 0x74, 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x61, 0x63, 0x68, 0x69, - 0x6e, 0x65, 0x72, 0x79, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, - 0x20, 0x02, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x20, 0x10, - 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x20, 0x17, 0x18, 0x0a, - 0x54, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, 0x12, 0x03, 0x22, 0x02, 0x25, 0x1a, 0x47, 0x20, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, - 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, - 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x6c, - 0x79, 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x06, 0x12, 0x03, - 0x22, 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x22, 0x16, - 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x22, 0x23, 0x24, 0x0a, - 0x84, 0x01, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x02, 0x12, 0x03, 0x25, 0x02, 0x1f, 0x1a, 0x77, 0x20, - 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, - 0x6b, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x68, 0x61, - 0x74, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, - 0x20, 0x63, 0x61, 0x73, 0x65, 0x0a, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x20, - 0x6f, 0x72, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x04, 0x12, - 0x03, 0x25, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x05, 0x12, 0x03, 0x25, - 0x0b, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x01, 0x12, 0x03, 0x25, 0x12, 0x1a, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x03, 0x12, 0x03, 0x25, 0x1d, 0x1e, 0x0a, 0x6e, - 0x0a, 0x04, 0x04, 0x01, 0x02, 0x03, 0x12, 0x03, 0x28, 0x02, 0x27, 0x1a, 0x61, 0x20, 0x6f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x3b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x69, 0x6e, - 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, - 0x6d, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, - 0x65, 0x73, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x0a, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x04, 0x12, 0x03, 0x28, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x01, 0x02, 0x03, 0x06, 0x12, 0x03, 0x28, 0x0b, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, - 0x02, 0x03, 0x01, 0x12, 0x03, 0x28, 0x17, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, - 0x03, 0x12, 0x03, 0x28, 0x25, 0x26, 0x0a, 0x25, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x04, 0x12, 0x03, - 0x2a, 0x02, 0x14, 0x1a, 0x18, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x66, 0x0a, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x01, 0x02, 0x04, 0x05, 0x12, 0x03, 0x2a, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x01, 0x02, 0x04, 0x01, 0x12, 0x03, 0x2a, 0x08, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, - 0x04, 0x03, 0x12, 0x03, 0x2a, 0x12, 0x13, 0x0a, 0x43, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x2e, - 0x00, 0x36, 0x01, 0x1a, 0x37, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x20, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x45, 0x6e, 0x63, 0x6f, - 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, - 0x04, 0x02, 0x01, 0x12, 0x03, 0x2e, 0x08, 0x15, 0x0a, 0x2a, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, - 0x12, 0x03, 0x30, 0x02, 0x14, 0x1a, 0x1d, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x20, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x05, 0x12, 0x03, 0x30, - 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x30, 0x08, 0x0f, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x30, 0x12, 0x13, 0x0a, 0x0b, - 0x0a, 0x04, 0x04, 0x02, 0x02, 0x01, 0x12, 0x03, 0x31, 0x02, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x02, 0x02, 0x01, 0x06, 0x12, 0x03, 0x31, 0x02, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, - 0x01, 0x01, 0x12, 0x03, 0x31, 0x18, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x03, - 0x12, 0x03, 0x31, 0x21, 0x22, 0x0a, 0x8a, 0x01, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x02, 0x12, 0x03, - 0x35, 0x02, 0x1a, 0x1a, 0x7d, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x63, - 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x0a, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x68, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x2c, - 0x0a, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x27, 0x73, 0x20, 0x70, 0x72, 0x65, 0x2d, 0x6b, 0x65, 0x79, - 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x06, 0x12, 0x03, 0x35, 0x02, 0x0b, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x01, 0x12, 0x03, 0x35, 0x0c, 0x15, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x03, 0x12, 0x03, 0x35, 0x18, 0x19, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xf3, 0x07, 0x0a, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x78, 0x6d, 0x74, 0x70, 0x2e, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0x1a, 0x1e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x22, 0xdb, 0x01, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x12, 0x3b, - 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x2e, - 0x50, 0x61, 0x72, 0x74, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x1a, 0x90, 0x01, 0x0a, 0x04, - 0x50, 0x61, 0x72, 0x74, 0x12, 0x3b, 0x0a, 0x04, 0x70, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, - 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x04, 0x70, 0x61, 0x72, - 0x74, 0x12, 0x40, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6d, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x48, 0x00, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0xca, - 0x01, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x0e, 0x43, 0x6f, - 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2c, + 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xf3, 0x19, 0x0a, 0x2a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x21, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x63, 0x69, 0x70, + 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x98, 0x07, + 0x0a, 0x18, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x63, 0x0a, 0x0d, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x3c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, + 0x00, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x60, 0x0a, 0x0c, 0x64, 0x65, 0x6e, 0x79, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x72, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6e, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x6e, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x5d, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, + 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x12, 0x5a, 0x0a, 0x0a, 0x64, 0x65, 0x6e, 0x79, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6e, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x48, + 0x00, 0x52, 0x09, 0x64, 0x65, 0x6e, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x64, 0x0a, 0x0e, + 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x49, 0x6e, 0x62, 0x6f, 0x78, + 0x49, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x49, 0x6e, 0x62, 0x6f, 0x78, + 0x49, 0x64, 0x12, 0x61, 0x0a, 0x0d, 0x64, 0x65, 0x6e, 0x79, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x78, + 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x78, 0x6d, 0x74, 0x70, + 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6e, 0x79, 0x49, + 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x6e, 0x79, 0x49, 0x6e, + 0x62, 0x6f, 0x78, 0x49, 0x64, 0x1a, 0x39, 0x0a, 0x0c, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, + 0x1a, 0x38, 0x0a, 0x0b, 0x44, 0x65, 0x6e, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x29, 0x0a, 0x10, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x1a, 0x2b, 0x0a, 0x0c, 0x41, 0x6c, + 0x6c, 0x6f, 0x77, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x6e, + 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, + 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x73, 0x1a, 0x2a, 0x0a, 0x0b, 0x44, 0x65, 0x6e, 0x79, 0x49, + 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, + 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x62, 0x6f, 0x78, + 0x49, 0x64, 0x73, 0x1a, 0x29, 0x0a, 0x0a, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x1a, 0x28, + 0x0a, 0x09, 0x44, 0x65, 0x6e, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x42, 0x0e, 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x5b, 0x0a, 0x19, 0x50, 0x72, 0x69, 0x76, + 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x50, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x33, 0x0a, 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, + 0x74, 0x65, 0x78, 0x74, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x42, 0x09, 0x0a, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0xd3, 0x01, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, + 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0x42, 0x17, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, + 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, @@ -2236,676 +2090,351 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[ 0xe2, 0x02, 0x20, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xe4, 0x03, 0x0a, 0x06, - 0x12, 0x04, 0x01, 0x00, 0x15, 0x01, 0x0a, 0x21, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, - 0x1a, 0x17, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x20, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, - 0x03, 0x00, 0x1e, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x05, 0x00, 0x28, 0x0a, 0x08, - 0x0a, 0x01, 0x08, 0x12, 0x03, 0x07, 0x00, 0x43, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, - 0x07, 0x00, 0x43, 0x0a, 0x4c, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x0b, 0x00, 0x15, 0x01, 0x1a, - 0x40, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x20, 0x69, 0x73, 0x20, 0x75, - 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x20, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, - 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x0b, 0x08, 0x11, 0x0a, 0x42, 0x0a, - 0x04, 0x04, 0x00, 0x03, 0x00, 0x12, 0x04, 0x0d, 0x02, 0x12, 0x03, 0x1a, 0x34, 0x20, 0x50, 0x61, - 0x72, 0x74, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x6e, - 0x65, 0x20, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x63, - 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, 0x00, 0x01, 0x12, 0x03, 0x0d, 0x0a, 0x0e, 0x0a, - 0x0e, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x08, 0x00, 0x12, 0x04, 0x0e, 0x04, 0x11, 0x05, 0x0a, - 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x08, 0x00, 0x01, 0x12, 0x03, 0x0e, 0x0a, 0x11, 0x0a, - 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0f, 0x06, 0x1e, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x0f, 0x06, 0x14, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0f, 0x15, 0x19, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0f, 0x1c, 0x1d, 0x0a, 0x0d, - 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x10, 0x06, 0x1e, 0x0a, 0x0e, 0x0a, - 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, 0x10, 0x06, 0x0f, 0x0a, 0x0e, 0x0a, - 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x10, 0x10, 0x19, 0x0a, 0x0e, 0x0a, - 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x10, 0x1c, 0x1d, 0x0a, 0x0b, 0x0a, - 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x14, 0x02, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, - 0x02, 0x00, 0x04, 0x12, 0x03, 0x14, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, - 0x06, 0x12, 0x03, 0x14, 0x0b, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, - 0x03, 0x14, 0x10, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x14, - 0x18, 0x19, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xd2, 0x0b, 0x0a, 0x1e, 0x6d, + 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xb4, 0x0f, 0x0a, 0x06, + 0x12, 0x04, 0x04, 0x00, 0x42, 0x01, 0x0a, 0x99, 0x01, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x04, 0x00, + 0x12, 0x1a, 0x8e, 0x01, 0x20, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x4b, 0x65, 0x79, + 0x20, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x20, 0x46, 0x6f, 0x6c, 0x6c, 0x6f, + 0x77, 0x69, 0x6e, 0x67, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2c, 0x20, 0x69, + 0x6e, 0x73, 0x74, 0x65, 0x61, 0x64, 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x20, 0x61, 0x0a, 0x20, 0x77, 0x61, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, + 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x2e, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x06, 0x00, 0x1e, 0x0a, 0x09, 0x0a, 0x02, + 0x03, 0x00, 0x12, 0x03, 0x08, 0x00, 0x2b, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x0a, 0x00, + 0x43, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x0a, 0x00, 0x43, 0x0a, 0x62, 0x0a, 0x02, + 0x04, 0x00, 0x12, 0x04, 0x0f, 0x00, 0x3d, 0x01, 0x1a, 0x56, 0x20, 0x50, 0x72, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x27, 0x73, 0x20, 0x70, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x0a, + 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x0f, 0x08, 0x20, 0x0a, 0x34, 0x0a, 0x04, + 0x04, 0x00, 0x03, 0x00, 0x12, 0x04, 0x11, 0x02, 0x14, 0x03, 0x1a, 0x26, 0x20, 0x41, 0x6c, 0x6c, + 0x6f, 0x77, 0x20, 0x31, 0x3a, 0x31, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x20, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x28, 0x44, 0x4d, 0x29, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, 0x00, 0x01, 0x12, 0x03, 0x11, 0x0a, 0x16, + 0x0a, 0x41, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x13, 0x04, 0x29, 0x1a, + 0x32, 0x20, 0x41, 0x64, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, + 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, + 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x20, 0x6c, 0x69, + 0x73, 0x74, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x04, 0x12, 0x03, + 0x13, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, + 0x13, 0x0d, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, + 0x13, 0x14, 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, + 0x13, 0x27, 0x28, 0x0a, 0x3b, 0x0a, 0x04, 0x04, 0x00, 0x03, 0x01, 0x12, 0x04, 0x17, 0x02, 0x1a, + 0x03, 0x1a, 0x2d, 0x20, 0x44, 0x65, 0x6e, 0x79, 0x20, 0x28, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x29, + 0x20, 0x31, 0x3a, 0x31, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x20, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x20, 0x28, 0x44, 0x4d, 0x29, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x0a, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, 0x01, 0x01, 0x12, 0x03, 0x17, 0x0a, 0x15, 0x0a, 0x40, + 0x0a, 0x06, 0x04, 0x00, 0x03, 0x01, 0x02, 0x00, 0x12, 0x03, 0x19, 0x04, 0x29, 0x1a, 0x31, 0x20, + 0x41, 0x64, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20, 0x74, + 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x6e, 0x79, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x0a, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x01, 0x02, 0x00, 0x04, 0x12, 0x03, 0x19, 0x04, 0x0c, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x01, 0x02, 0x00, 0x05, 0x12, 0x03, 0x19, 0x0d, 0x13, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x19, 0x14, 0x24, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x19, 0x27, 0x28, + 0x0a, 0x37, 0x0a, 0x04, 0x04, 0x00, 0x03, 0x02, 0x12, 0x04, 0x1d, 0x02, 0x20, 0x03, 0x1a, 0x29, + 0x20, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x20, 0x56, 0x33, 0x20, 0x31, 0x3a, 0x31, 0x20, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x28, 0x44, 0x4d, + 0x29, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, + 0x02, 0x01, 0x12, 0x03, 0x1d, 0x0a, 0x16, 0x0a, 0x39, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x02, 0x02, + 0x00, 0x12, 0x03, 0x1f, 0x04, 0x22, 0x1a, 0x2a, 0x20, 0x41, 0x64, 0x64, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x20, 0x69, 0x64, 0x20, + 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x20, 0x6c, 0x69, 0x73, + 0x74, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x02, 0x02, 0x00, 0x04, 0x12, 0x03, 0x1f, + 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x02, 0x02, 0x00, 0x05, 0x12, 0x03, 0x1f, + 0x0d, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1f, + 0x14, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1f, + 0x20, 0x21, 0x0a, 0x3e, 0x0a, 0x04, 0x04, 0x00, 0x03, 0x03, 0x12, 0x04, 0x23, 0x02, 0x26, 0x03, + 0x1a, 0x30, 0x20, 0x44, 0x65, 0x6e, 0x79, 0x20, 0x28, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x29, 0x20, + 0x56, 0x33, 0x20, 0x31, 0x3a, 0x31, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x20, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x28, 0x44, 0x4d, 0x29, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, 0x03, 0x01, 0x12, 0x03, 0x23, 0x0a, 0x15, + 0x0a, 0x38, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x03, 0x02, 0x00, 0x12, 0x03, 0x25, 0x04, 0x22, 0x1a, + 0x29, 0x20, 0x41, 0x64, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, + 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x20, 0x69, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x64, 0x65, 0x6e, 0x79, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, + 0x03, 0x03, 0x02, 0x00, 0x04, 0x12, 0x03, 0x25, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, + 0x03, 0x03, 0x02, 0x00, 0x05, 0x12, 0x03, 0x25, 0x0d, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, + 0x03, 0x03, 0x02, 0x00, 0x01, 0x12, 0x03, 0x25, 0x14, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, + 0x03, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, 0x25, 0x20, 0x21, 0x0a, 0x22, 0x0a, 0x04, 0x04, 0x00, + 0x03, 0x04, 0x12, 0x04, 0x2a, 0x02, 0x2d, 0x03, 0x1a, 0x14, 0x20, 0x41, 0x6c, 0x6c, 0x6f, 0x77, + 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x0a, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x00, 0x03, 0x04, 0x01, 0x12, 0x03, 0x2a, 0x0a, 0x14, 0x0a, 0x3a, 0x0a, 0x06, + 0x04, 0x00, 0x03, 0x04, 0x02, 0x00, 0x12, 0x03, 0x2c, 0x04, 0x22, 0x1a, 0x2b, 0x20, 0x41, 0x64, + 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x5f, 0x69, 0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x04, + 0x02, 0x00, 0x04, 0x12, 0x03, 0x2c, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x04, + 0x02, 0x00, 0x05, 0x12, 0x03, 0x2c, 0x0d, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x04, + 0x02, 0x00, 0x01, 0x12, 0x03, 0x2c, 0x14, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x04, + 0x02, 0x00, 0x03, 0x12, 0x03, 0x2c, 0x20, 0x21, 0x0a, 0x28, 0x0a, 0x04, 0x04, 0x00, 0x03, 0x05, + 0x12, 0x04, 0x30, 0x02, 0x33, 0x03, 0x1a, 0x1a, 0x20, 0x44, 0x65, 0x6e, 0x79, 0x20, 0x28, 0x64, + 0x65, 0x6e, 0x79, 0x29, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, 0x05, 0x01, 0x12, 0x03, 0x30, 0x0a, 0x13, + 0x0a, 0x39, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x05, 0x02, 0x00, 0x12, 0x03, 0x32, 0x04, 0x22, 0x1a, + 0x2a, 0x20, 0x41, 0x64, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x64, 0x65, 0x6e, 0x79, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x00, 0x03, 0x05, 0x02, 0x00, 0x04, 0x12, 0x03, 0x32, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x00, 0x03, 0x05, 0x02, 0x00, 0x05, 0x12, 0x03, 0x32, 0x0d, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x00, 0x03, 0x05, 0x02, 0x00, 0x01, 0x12, 0x03, 0x32, 0x14, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x00, 0x03, 0x05, 0x02, 0x00, 0x03, 0x12, 0x03, 0x32, 0x20, 0x21, 0x0a, 0x0c, 0x0a, 0x04, 0x04, + 0x00, 0x08, 0x00, 0x12, 0x04, 0x35, 0x02, 0x3c, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x08, + 0x00, 0x01, 0x12, 0x03, 0x35, 0x08, 0x14, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, + 0x03, 0x36, 0x04, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x36, + 0x04, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x36, 0x11, 0x1e, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x36, 0x21, 0x22, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x37, 0x04, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, 0x37, 0x04, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, + 0x01, 0x01, 0x12, 0x03, 0x37, 0x10, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, + 0x12, 0x03, 0x37, 0x1f, 0x20, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x38, + 0x04, 0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x06, 0x12, 0x03, 0x38, 0x04, 0x0e, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x38, 0x0f, 0x1a, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x38, 0x1d, 0x1e, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x00, 0x02, 0x03, 0x12, 0x03, 0x39, 0x04, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, + 0x03, 0x06, 0x12, 0x03, 0x39, 0x04, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x01, + 0x12, 0x03, 0x39, 0x0e, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x03, 0x12, 0x03, + 0x39, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x04, 0x12, 0x03, 0x3a, 0x04, 0x24, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x04, 0x06, 0x12, 0x03, 0x3a, 0x04, 0x10, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x00, 0x02, 0x04, 0x01, 0x12, 0x03, 0x3a, 0x11, 0x1f, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x00, 0x02, 0x04, 0x03, 0x12, 0x03, 0x3a, 0x22, 0x23, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, + 0x02, 0x05, 0x12, 0x03, 0x3b, 0x04, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x05, 0x06, + 0x12, 0x03, 0x3b, 0x04, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x05, 0x01, 0x12, 0x03, + 0x3b, 0x10, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x05, 0x03, 0x12, 0x03, 0x3b, 0x20, + 0x21, 0x0a, 0x31, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x40, 0x00, 0x42, 0x01, 0x1a, 0x25, 0x20, + 0x54, 0x68, 0x65, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x74, 0x68, 0x61, 0x74, + 0x20, 0x67, 0x6f, 0x65, 0x73, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, + 0x69, 0x72, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x40, 0x08, 0x21, + 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x08, 0x00, 0x12, 0x03, 0x41, 0x02, 0x26, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x01, 0x08, 0x00, 0x01, 0x12, 0x03, 0x41, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, + 0x01, 0x02, 0x00, 0x12, 0x03, 0x41, 0x12, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, + 0x06, 0x12, 0x03, 0x41, 0x12, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, + 0x03, 0x41, 0x1d, 0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x41, + 0x22, 0x23, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xdd, 0x18, 0x0a, 0x1e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x78, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x21, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, - 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x58, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x63, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x56, 0x31, 0x12, 0x45, 0x0a, 0x0a, 0x6b, 0x65, - 0x79, 0x5f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, - 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, - 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, - 0x65, 0x22, 0x5e, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x75, 0x6e, 0x64, - 0x6c, 0x65, 0x56, 0x32, 0x12, 0x4b, 0x0a, 0x0a, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x75, 0x6e, 0x64, - 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, - 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x09, 0x6b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, - 0x65, 0x22, 0x8e, 0x01, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x75, 0x6e, - 0x64, 0x6c, 0x65, 0x12, 0x38, 0x0a, 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x26, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, - 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x56, 0x31, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x12, 0x38, 0x0a, - 0x02, 0x76, 0x32, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x6d, 0x74, 0x70, - 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x56, - 0x32, 0x48, 0x00, 0x52, 0x02, 0x76, 0x32, 0x42, 0x09, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x42, 0xc8, 0x01, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0x42, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, - 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, - 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, - 0x03, 0x58, 0x4d, 0x58, 0xaa, 0x02, 0x14, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, 0x14, 0x58, 0x6d, - 0x74, 0x70, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x73, 0xe2, 0x02, 0x20, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xd7, 0x06, - 0x0a, 0x06, 0x12, 0x04, 0x05, 0x00, 0x1f, 0x01, 0x0a, 0xc9, 0x02, 0x0a, 0x01, 0x0c, 0x12, 0x03, - 0x05, 0x00, 0x12, 0x1a, 0xbe, 0x02, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x20, 0x42, - 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, - 0x74, 0x6f, 0x20, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, 0x73, 0x65, 0x20, 0x75, 0x73, 0x65, - 0x72, 0x27, 0x73, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, - 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x0a, - 0x20, 0x54, 0x68, 0x65, 0x79, 0x20, 0x61, 0x72, 0x65, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, - 0x68, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x77, 0x65, 0x6c, 0x6c, 0x20, 0x6b, 0x6e, 0x6f, 0x77, - 0x6e, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x20, 0x73, 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, - 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, - 0x6e, 0x74, 0x73, 0x0a, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x66, 0x69, 0x6e, 0x64, 0x20, 0x74, 0x68, - 0x65, 0x6d, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x77, 0x69, 0x73, - 0x68, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x65, - 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x0a, - 0x20, 0x54, 0x68, 0x65, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x6b, 0x65, 0x79, 0x73, - 0x20, 0x61, 0x72, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x69, 0x67, - 0x6e, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, - 0x6f, 0x20, 0x64, 0x65, 0x72, 0x69, 0x76, 0x65, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x0a, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x73, 0x6f, - 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, - 0x2c, 0x20, 0x65, 0x2e, 0x67, 0x2e, 0x20, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x07, 0x00, 0x1e, 0x0a, 0x09, - 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x09, 0x00, 0x2b, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, - 0x0b, 0x00, 0x43, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x0b, 0x00, 0x43, 0x0a, 0x5a, - 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x10, 0x00, 0x12, 0x01, 0x1a, 0x4e, 0x20, 0x4c, 0x45, 0x47, - 0x41, 0x43, 0x59, 0x3a, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x62, 0x75, - 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x56, 0x31, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x50, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x2e, 0x0a, 0x20, 0x54, 0x68, 0x65, 0x20, 0x50, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x62, - 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, - 0x01, 0x12, 0x03, 0x10, 0x08, 0x17, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, - 0x11, 0x02, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x11, 0x02, - 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x11, 0x12, 0x1c, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x11, 0x1f, 0x20, 0x0a, 0x38, 0x0a, - 0x02, 0x04, 0x01, 0x12, 0x04, 0x15, 0x00, 0x17, 0x01, 0x1a, 0x2c, 0x20, 0x55, 0x73, 0x65, 0x72, - 0x20, 0x6b, 0x65, 0x79, 0x20, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x56, 0x32, 0x20, 0x75, - 0x73, 0x69, 0x6e, 0x67, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x4b, 0x65, 0x79, 0x73, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, - 0x15, 0x08, 0x17, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x16, 0x02, 0x27, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, 0x16, 0x02, 0x17, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x16, 0x18, 0x22, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x16, 0x25, 0x26, 0x0a, 0x25, 0x0a, 0x02, 0x04, 0x02, - 0x12, 0x04, 0x1a, 0x00, 0x1f, 0x01, 0x1a, 0x19, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x64, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, - 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x1a, 0x08, 0x15, 0x0a, 0x0c, 0x0a, - 0x04, 0x04, 0x02, 0x08, 0x00, 0x12, 0x04, 0x1b, 0x02, 0x1e, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x02, 0x08, 0x00, 0x01, 0x12, 0x03, 0x1b, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, - 0x00, 0x12, 0x03, 0x1c, 0x04, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x06, 0x12, - 0x03, 0x1c, 0x04, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1c, - 0x14, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1c, 0x19, 0x1a, - 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x01, 0x12, 0x03, 0x1d, 0x04, 0x1b, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x02, 0x02, 0x01, 0x06, 0x12, 0x03, 0x1d, 0x04, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x02, 0x02, 0x01, 0x01, 0x12, 0x03, 0x1d, 0x14, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, - 0x01, 0x03, 0x12, 0x03, 0x1d, 0x19, 0x1a, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, - 0xe8, 0x04, 0x0a, 0x1c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x65, 0x63, 0x69, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x12, 0x15, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x2b, 0x0a, 0x0c, 0x45, 0x63, 0x69, 0x65, 0x73, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x42, 0x09, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x42, 0xc6, 0x01, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, - 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x73, 0x42, 0x0a, 0x45, 0x63, 0x69, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, - 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, - 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, - 0x03, 0x58, 0x4d, 0x58, 0xaa, 0x02, 0x14, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, 0x14, 0x58, 0x6d, - 0x74, 0x70, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x73, 0xe2, 0x02, 0x20, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xb2, 0x02, - 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x0e, 0x01, 0x0a, 0x31, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, - 0x00, 0x12, 0x1a, 0x27, 0x20, 0x45, 0x43, 0x49, 0x45, 0x53, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, - 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x45, 0x43, 0x49, 0x45, - 0x53, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, - 0x12, 0x03, 0x03, 0x00, 0x1e, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x05, 0x00, 0x43, 0x0a, - 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x05, 0x00, 0x43, 0x0a, 0x44, 0x0a, 0x02, 0x04, 0x00, - 0x12, 0x04, 0x09, 0x00, 0x0e, 0x01, 0x1a, 0x38, 0x20, 0x45, 0x63, 0x69, 0x65, 0x73, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, - 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x45, 0x43, 0x49, 0x45, 0x53, 0x20, 0x65, 0x6e, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x0a, - 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x09, 0x08, 0x14, 0x0a, 0x0c, 0x0a, 0x04, - 0x04, 0x00, 0x08, 0x00, 0x12, 0x04, 0x0a, 0x02, 0x0d, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, - 0x08, 0x00, 0x01, 0x12, 0x03, 0x0a, 0x08, 0x0f, 0x0a, 0x3e, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, - 0x12, 0x03, 0x0c, 0x04, 0x11, 0x1a, 0x31, 0x20, 0x45, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x45, 0x43, 0x49, 0x45, 0x53, 0x20, - 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, - 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, - 0x05, 0x12, 0x03, 0x0c, 0x04, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, - 0x03, 0x0c, 0x0a, 0x0c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0c, - 0x0f, 0x10, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xd5, 0x15, 0x0a, 0x1d, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, - 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x78, 0x6d, + 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x95, 0x01, 0x0a, 0x0d, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x17, + 0x0a, 0x07, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x74, 0x79, 0x70, 0x65, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x6e, 0x6f, + 0x72, 0x22, 0x83, 0x03, 0x0a, 0x0e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x49, 0x64, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x55, + 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, + 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, + 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, + 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, 0x08, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x66, 0x61, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x49, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x1a, 0x21, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd6, 0x02, 0x0a, 0x0f, 0x46, 0x72, 0x61, - 0x6d, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x1b, 0x0a, 0x09, - 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x75, 0x74, - 0x74, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x0b, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x20, 0x0a, 0x09, - 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, - 0x02, 0x18, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x44, - 0x0a, 0x1e, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1c, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x43, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x75, 0x6e, - 0x69, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x69, - 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x54, 0x65, 0x78, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x64, 0x22, 0xd5, 0x01, 0x0a, 0x0b, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x3e, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x53, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x12, 0x65, 0x0a, 0x18, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, + 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x48, + 0x01, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, + 0x01, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x66, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x6f, 0x6d, 0x70, + 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xaf, 0x01, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, + 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x12, 0x44, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, - 0x65, 0x52, 0x15, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, - 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6f, 0x64, 0x79, 0x42, 0xc7, 0x01, 0x0a, 0x19, 0x63, 0x6f, - 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x0b, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, - 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x58, 0xaa, 0x02, 0x14, 0x58, 0x6d, 0x74, - 0x70, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x73, 0xca, 0x02, 0x14, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xe2, 0x02, 0x20, 0x58, 0x6d, 0x74, 0x70, 0x5c, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, - 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x58, 0x6d, - 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x4a, 0xd4, 0x0e, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x2e, 0x01, 0x0a, 0x47, - 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x3d, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, - 0x1e, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x05, 0x00, 0x2b, 0x0a, 0x09, 0x0a, 0x02, - 0x03, 0x01, 0x12, 0x03, 0x06, 0x00, 0x2a, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x08, 0x00, - 0x43, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x08, 0x00, 0x43, 0x0a, 0x83, 0x01, 0x0a, - 0x02, 0x04, 0x00, 0x12, 0x04, 0x0d, 0x00, 0x22, 0x01, 0x1a, 0x77, 0x20, 0x54, 0x68, 0x65, 0x20, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x69, 0x6c, - 0x6c, 0x20, 0x62, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x20, 0x74, 0x68, - 0x65, 0x0a, 0x20, 0x60, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x60, - 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x46, 0x72, - 0x61, 0x6d, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x0d, 0x08, 0x17, 0x0a, 0x56, - 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x10, 0x02, 0x17, 0x1a, 0x49, 0x20, 0x54, 0x68, - 0x65, 0x20, 0x55, 0x52, 0x4c, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x72, 0x61, - 0x6d, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x61, 0x73, 0x20, 0x63, 0x6c, 0x69, 0x63, - 0x6b, 0x65, 0x64, 0x0a, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x64, 0x69, 0x66, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x60, 0x70, 0x6f, 0x73, 0x74, - 0x5f, 0x75, 0x72, 0x6c, 0x60, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x05, 0x12, - 0x03, 0x10, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x10, - 0x09, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x10, 0x15, 0x16, - 0x0a, 0x34, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x12, 0x02, 0x19, 0x1a, 0x27, 0x20, - 0x54, 0x68, 0x65, 0x20, 0x31, 0x2d, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x20, 0x62, 0x75, - 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x61, 0x73, 0x20, 0x63, 0x6c, - 0x69, 0x63, 0x6b, 0x65, 0x64, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x05, 0x12, - 0x03, 0x12, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x12, - 0x08, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x12, 0x17, 0x18, - 0x0a, 0x45, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x14, 0x02, 0x2b, 0x1a, 0x38, 0x20, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x20, 0x69, 0x6e, 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x20, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x05, - 0x12, 0x03, 0x14, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, - 0x14, 0x09, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x14, 0x15, - 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x08, 0x12, 0x03, 0x14, 0x17, 0x2a, 0x0a, - 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x02, 0x02, 0x08, 0x03, 0x12, 0x03, 0x14, 0x18, 0x29, 0x0a, 0x8a, - 0x01, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x03, 0x12, 0x03, 0x17, 0x02, 0x2c, 0x1a, 0x7d, 0x20, 0x41, - 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, - 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x74, 0x69, 0x65, - 0x64, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x79, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x6e, - 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x20, 0x57, - 0x69, 0x6c, 0x6c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x20, 0x6f, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x00, 0x02, 0x03, 0x05, 0x12, 0x03, 0x17, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, - 0x03, 0x01, 0x12, 0x03, 0x17, 0x09, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x03, - 0x12, 0x03, 0x17, 0x2a, 0x2b, 0x0a, 0x1d, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x04, 0x12, 0x03, 0x19, - 0x02, 0x1c, 0x1a, 0x10, 0x20, 0x55, 0x6e, 0x69, 0x78, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x04, 0x05, 0x12, 0x03, 0x19, - 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x04, 0x01, 0x12, 0x03, 0x19, 0x09, 0x17, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x04, 0x03, 0x12, 0x03, 0x19, 0x1a, 0x1b, 0x0a, 0x31, - 0x0a, 0x04, 0x04, 0x00, 0x02, 0x05, 0x12, 0x03, 0x1b, 0x02, 0x18, 0x1a, 0x24, 0x20, 0x49, 0x6e, - 0x70, 0x75, 0x74, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, - 0x74, 0x65, 0x78, 0x74, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x05, 0x05, 0x12, 0x03, 0x1b, 0x02, 0x08, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x05, 0x01, 0x12, 0x03, 0x1b, 0x09, 0x13, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x00, 0x02, 0x05, 0x03, 0x12, 0x03, 0x1b, 0x16, 0x17, 0x0a, 0x65, 0x0a, 0x04, 0x04, - 0x00, 0x02, 0x06, 0x12, 0x03, 0x1d, 0x02, 0x13, 0x1a, 0x58, 0x20, 0x41, 0x20, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x74, 0x6f, - 0x20, 0x61, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x28, 0x66, 0x6f, 0x72, 0x20, 0x65, - 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x76, 0x69, 0x61, 0x20, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, - 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x29, 0x29, 0x2e, 0x20, 0x4d, 0x61, - 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x34, 0x30, 0x39, 0x36, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, - 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x06, 0x05, 0x12, 0x03, 0x1d, 0x02, 0x08, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x06, 0x01, 0x12, 0x03, 0x1d, 0x09, 0x0e, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x00, 0x02, 0x06, 0x03, 0x12, 0x03, 0x1d, 0x11, 0x12, 0x0a, 0x22, 0x0a, 0x04, - 0x04, 0x00, 0x02, 0x07, 0x12, 0x03, 0x1f, 0x02, 0x15, 0x1a, 0x15, 0x20, 0x41, 0x20, 0x30, 0x78, - 0x20, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x0a, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x07, 0x05, 0x12, 0x03, 0x1f, 0x02, 0x08, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x00, 0x02, 0x07, 0x01, 0x12, 0x03, 0x1f, 0x09, 0x10, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x00, 0x02, 0x07, 0x03, 0x12, 0x03, 0x1f, 0x13, 0x14, 0x0a, 0x28, 0x0a, 0x04, 0x04, 0x00, - 0x02, 0x08, 0x12, 0x03, 0x21, 0x02, 0x1c, 0x1a, 0x1b, 0x20, 0x41, 0x20, 0x68, 0x61, 0x73, 0x68, - 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x08, 0x05, 0x12, 0x03, 0x21, - 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x08, 0x01, 0x12, 0x03, 0x21, 0x09, 0x17, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x08, 0x03, 0x12, 0x03, 0x21, 0x1a, 0x1b, 0x0a, 0x79, - 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x26, 0x00, 0x2e, 0x01, 0x1a, 0x6d, 0x20, 0x54, 0x68, 0x65, - 0x20, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x74, - 0x68, 0x61, 0x74, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x74, - 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x60, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x42, 0x79, 0x74, 0x65, 0x73, 0x60, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x60, - 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x60, 0x20, 0x70, 0x61, - 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, - 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, - 0x12, 0x03, 0x26, 0x08, 0x13, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x27, - 0x02, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, 0x27, 0x02, 0x0b, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x27, 0x0c, 0x15, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x27, 0x18, 0x19, 0x0a, 0x92, 0x01, 0x0a, - 0x04, 0x04, 0x01, 0x02, 0x01, 0x12, 0x03, 0x2a, 0x02, 0x35, 0x1a, 0x84, 0x01, 0x20, 0x54, 0x68, - 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, - 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, - 0x69, 0x67, 0x6e, 0x65, 0x72, 0x2c, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6c, - 0x69, 0x6e, 0x6b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x58, 0x4d, 0x54, 0x50, 0x20, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x0a, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x61, 0x20, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, - 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x06, 0x12, 0x03, 0x2a, 0x02, 0x17, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x2a, 0x18, 0x30, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x2a, 0x33, 0x34, 0x0a, 0x92, 0x01, 0x0a, 0x04, - 0x04, 0x01, 0x02, 0x02, 0x12, 0x03, 0x2d, 0x02, 0x18, 0x1a, 0x84, 0x01, 0x20, 0x53, 0x65, 0x72, - 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x42, 0x6f, 0x64, 0x79, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2c, - 0x20, 0x73, 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x61, 0x6e, 0x0a, 0x20, 0x68, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x20, - 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x62, 0x79, 0x74, 0x65, 0x2d, 0x70, 0x65, 0x72, 0x66, 0x65, 0x63, - 0x74, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x05, 0x12, 0x03, 0x2d, 0x02, 0x07, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x01, 0x12, 0x03, 0x2d, 0x08, 0x13, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x01, 0x02, 0x02, 0x03, 0x12, 0x03, 0x2d, 0x16, 0x17, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, 0x0a, 0xb5, 0x20, 0x0a, 0x1e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x21, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x63, - 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, - 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0xb5, 0x01, 0x0a, 0x0f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x56, 0x31, 0x12, 0x3e, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x06, 0x73, - 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, - 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, - 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, - 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x71, 0x0a, 0x09, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x56, 0x31, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x63, 0x69, 0x70, - 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, + 0x65, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x09, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x09, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x2a, 0x3c, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, + 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x4f, 0x4d, 0x50, + 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x46, 0x4c, 0x41, 0x54, 0x45, 0x10, + 0x00, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, + 0x5f, 0x47, 0x5a, 0x49, 0x50, 0x10, 0x01, 0x42, 0xc8, 0x01, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, - 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, 0x46, 0x0a, 0x0f, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x32, 0x12, - 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x4e, 0x73, 0x12, 0x14, - 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, - 0x6f, 0x70, 0x69, 0x63, 0x22, 0xdd, 0x01, 0x0a, 0x09, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x56, 0x32, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x74, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, - 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x6d, 0x74, 0x70, - 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x73, 0x2e, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0a, 0x63, 0x69, - 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x12, 0x24, 0x0a, 0x0b, 0x73, 0x65, 0x6e, 0x64, - 0x65, 0x72, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, - 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x48, 0x6d, 0x61, 0x63, 0x88, 0x01, 0x01, 0x12, 0x24, - 0x0a, 0x0b, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x0a, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x50, 0x75, 0x73, - 0x68, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, - 0x68, 0x6d, 0x61, 0x63, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x5f, - 0x70, 0x75, 0x73, 0x68, 0x22, 0x7c, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x32, 0x0a, 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x6d, - 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x56, 0x31, 0x48, 0x00, 0x52, - 0x02, 0x76, 0x31, 0x12, 0x32, 0x0a, 0x02, 0x76, 0x32, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x20, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x56, - 0x32, 0x48, 0x00, 0x52, 0x02, 0x76, 0x32, 0x42, 0x09, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x22, 0xed, 0x02, 0x0a, 0x0e, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, - 0x0a, 0x0e, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x30, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, - 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x00, 0x52, 0x10, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x65, 0x6e, 0x74, 0x5f, - 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x74, 0x4e, 0x73, - 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x70, 0x69, - 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x50, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, - 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x42, 0x14, 0x0a, 0x12, - 0x5f, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x42, 0xc8, 0x01, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0x42, 0x0c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, - 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, - 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, - 0x03, 0x58, 0x4d, 0x58, 0xaa, 0x02, 0x14, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, 0x14, 0x58, 0x6d, - 0x74, 0x70, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x73, 0xe2, 0x02, 0x20, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xf2, 0x14, - 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x4d, 0x01, 0x0a, 0x4a, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, - 0x00, 0x12, 0x1a, 0x40, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x20, 0x75, 0x73, - 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, - 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x20, 0x6f, 0x66, 0x20, - 0x75, 0x73, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, 0x1e, 0x0a, 0x09, - 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x05, 0x00, 0x2b, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x01, 0x12, - 0x03, 0x06, 0x00, 0x37, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x02, 0x12, 0x03, 0x07, 0x00, 0x2b, 0x0a, - 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x09, 0x00, 0x43, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, - 0x03, 0x09, 0x00, 0x43, 0x0a, 0x8c, 0x01, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x10, 0x00, 0x14, - 0x01, 0x1a, 0x72, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x68, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x73, 0x65, - 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x75, - 0x73, 0x65, 0x64, 0x0a, 0x20, 0x61, 0x73, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, - 0x65, 0x64, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, - 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x32, 0x0c, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, - 0x56, 0x31, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x10, 0x08, 0x17, 0x0a, - 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x11, 0x02, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x11, 0x02, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, - 0x02, 0x00, 0x01, 0x12, 0x03, 0x11, 0x12, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, - 0x03, 0x12, 0x03, 0x11, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, - 0x12, 0x02, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, 0x12, 0x02, - 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x12, 0x12, 0x1b, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x12, 0x1e, 0x1f, 0x0a, 0x0b, 0x0a, - 0x04, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x13, 0x02, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, - 0x02, 0x02, 0x05, 0x12, 0x03, 0x13, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, - 0x01, 0x12, 0x03, 0x13, 0x09, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x03, 0x12, - 0x03, 0x13, 0x15, 0x16, 0x0a, 0x37, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x17, 0x00, 0x1c, 0x01, - 0x1a, 0x2b, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x74, 0x6f, 0x70, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0a, 0x0a, - 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x17, 0x08, 0x11, 0x0a, 0x33, 0x0a, 0x04, 0x04, 0x01, 0x02, - 0x00, 0x12, 0x03, 0x19, 0x02, 0x19, 0x1a, 0x26, 0x20, 0x65, 0x6e, 0x63, 0x61, 0x70, 0x73, 0x75, - 0x6c, 0x61, 0x74, 0x65, 0x73, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x31, 0x0a, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x05, 0x12, 0x03, 0x19, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x19, 0x08, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, - 0x02, 0x00, 0x03, 0x12, 0x03, 0x19, 0x17, 0x18, 0x0a, 0x47, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, - 0x12, 0x03, 0x1b, 0x02, 0x1c, 0x1a, 0x3a, 0x20, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, - 0x78, 0x74, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, - 0x64, 0x20, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x06, 0x12, 0x03, 0x1b, 0x02, 0x0c, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x1b, 0x0d, 0x17, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x1b, 0x1a, 0x1b, 0x0a, 0x88, 0x02, 0x0a, 0x02, - 0x04, 0x02, 0x12, 0x04, 0x24, 0x00, 0x29, 0x01, 0x1a, 0xed, 0x01, 0x20, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x20, 0x63, 0x61, 0x72, 0x72, 0x69, - 0x65, 0x73, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, - 0x68, 0x61, 0x74, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, - 0x72, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x0a, 0x20, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x62, - 0x6c, 0x65, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x68, 0x6f, 0x77, 0x65, 0x76, 0x65, 0x72, - 0x20, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x61, - 0x73, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x64, 0x61, 0x74, - 0x61, 0x0a, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x41, 0x45, 0x41, 0x44, 0x20, 0x65, - 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, - 0x6f, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x2c, 0x0a, 0x20, 0x74, 0x68, 0x75, 0x73, 0x20, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x61, 0x6d, 0x70, 0x65, 0x72, 0x20, 0x65, 0x76, - 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x2e, 0x0a, 0x32, 0x0c, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x20, 0x56, 0x32, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x24, - 0x08, 0x17, 0x0a, 0x35, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x26, 0x02, 0x18, 0x1a, - 0x28, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, - 0x65, 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, - 0x00, 0x05, 0x12, 0x03, 0x26, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, - 0x12, 0x03, 0x26, 0x09, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, - 0x26, 0x16, 0x17, 0x0a, 0x2f, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x01, 0x12, 0x03, 0x28, 0x02, 0x13, - 0x1a, 0x22, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x62, 0x65, 0x6c, 0x6f, 0x6e, 0x67, 0x73, - 0x20, 0x74, 0x6f, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x05, 0x12, 0x03, 0x28, - 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x01, 0x12, 0x03, 0x28, 0x09, 0x0e, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, 0x03, 0x28, 0x11, 0x12, 0x0a, 0x4d, - 0x0a, 0x02, 0x04, 0x03, 0x12, 0x04, 0x2c, 0x00, 0x37, 0x01, 0x1a, 0x41, 0x20, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x65, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, - 0x03, 0x04, 0x03, 0x01, 0x12, 0x03, 0x2c, 0x08, 0x11, 0x0a, 0x33, 0x0a, 0x04, 0x04, 0x03, 0x02, - 0x00, 0x12, 0x03, 0x2e, 0x02, 0x19, 0x1a, 0x26, 0x20, 0x65, 0x6e, 0x63, 0x61, 0x70, 0x73, 0x75, - 0x6c, 0x61, 0x74, 0x65, 0x73, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x32, 0x0a, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x05, 0x12, 0x03, 0x2e, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x03, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2e, 0x08, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, - 0x02, 0x00, 0x03, 0x12, 0x03, 0x2e, 0x17, 0x18, 0x0a, 0x46, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x01, - 0x12, 0x03, 0x30, 0x02, 0x1c, 0x1a, 0x39, 0x20, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, - 0x78, 0x74, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, - 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x06, 0x12, 0x03, 0x30, 0x02, 0x0c, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x12, 0x03, 0x30, 0x0d, 0x17, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x03, 0x02, 0x01, 0x03, 0x12, 0x03, 0x30, 0x1a, 0x1b, 0x0a, 0x5c, 0x0a, 0x04, 0x04, 0x03, - 0x02, 0x02, 0x12, 0x03, 0x33, 0x02, 0x21, 0x1a, 0x4f, 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x6f, - 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x63, 0x69, - 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x64, 0x65, 0x72, 0x69, - 0x76, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x6f, 0x70, - 0x69, 0x63, 0x0a, 0x20, 0x6b, 0x65, 0x79, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, - 0x04, 0x12, 0x03, 0x33, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x05, 0x12, - 0x03, 0x33, 0x0b, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x01, 0x12, 0x03, 0x33, - 0x11, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x03, 0x12, 0x03, 0x33, 0x1f, 0x20, - 0x0a, 0x5f, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x03, 0x12, 0x03, 0x36, 0x02, 0x20, 0x1a, 0x52, 0x20, - 0x46, 0x6c, 0x61, 0x67, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, - 0x77, 0x68, 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x70, 0x75, - 0x73, 0x68, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x6e, 0x6f, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x03, 0x04, 0x12, 0x03, 0x36, 0x02, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x03, 0x05, 0x12, 0x03, 0x36, 0x0b, 0x0f, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x03, 0x02, 0x03, 0x01, 0x12, 0x03, 0x36, 0x10, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x03, 0x02, 0x03, 0x03, 0x12, 0x03, 0x36, 0x1e, 0x1f, 0x0a, 0x1f, 0x0a, 0x02, 0x04, 0x04, 0x12, - 0x04, 0x3a, 0x00, 0x3f, 0x01, 0x1a, 0x13, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x64, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x04, - 0x01, 0x12, 0x03, 0x3a, 0x08, 0x0f, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x04, 0x08, 0x00, 0x12, 0x04, - 0x3b, 0x02, 0x3e, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x08, 0x00, 0x01, 0x12, 0x03, 0x3b, - 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, 0x03, 0x3c, 0x04, 0x15, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x06, 0x12, 0x03, 0x3c, 0x04, 0x0d, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x3c, 0x0e, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x3c, 0x13, 0x14, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, - 0x01, 0x12, 0x03, 0x3d, 0x04, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x06, 0x12, - 0x03, 0x3d, 0x04, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x01, 0x12, 0x03, 0x3d, - 0x0e, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x03, 0x12, 0x03, 0x3d, 0x13, 0x14, - 0x0a, 0xae, 0x01, 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04, 0x44, 0x00, 0x4d, 0x01, 0x1a, 0xa1, 0x01, - 0x20, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, - 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, - 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x0a, 0x20, 0x44, 0x65, 0x63, 0x6f, - 0x64, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x0a, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, - 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, - 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x05, 0x01, 0x12, 0x03, 0x44, 0x08, 0x16, 0x0a, 0x0b, 0x0a, - 0x04, 0x04, 0x05, 0x02, 0x00, 0x12, 0x03, 0x45, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, - 0x02, 0x00, 0x05, 0x12, 0x03, 0x45, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, - 0x01, 0x12, 0x03, 0x45, 0x09, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x03, 0x12, - 0x03, 0x45, 0x0e, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x01, 0x12, 0x03, 0x46, 0x02, - 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x05, 0x12, 0x03, 0x46, 0x02, 0x08, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x01, 0x12, 0x03, 0x46, 0x09, 0x18, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x05, 0x02, 0x01, 0x03, 0x12, 0x03, 0x46, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, - 0x05, 0x02, 0x02, 0x12, 0x03, 0x47, 0x02, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x02, - 0x05, 0x12, 0x03, 0x47, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x02, 0x01, 0x12, - 0x03, 0x47, 0x09, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x02, 0x03, 0x12, 0x03, 0x47, - 0x1a, 0x1b, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x03, 0x12, 0x03, 0x48, 0x02, 0x28, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x03, 0x04, 0x12, 0x03, 0x48, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x05, 0x02, 0x03, 0x05, 0x12, 0x03, 0x48, 0x0b, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x05, 0x02, 0x03, 0x01, 0x12, 0x03, 0x48, 0x12, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, - 0x03, 0x03, 0x12, 0x03, 0x48, 0x26, 0x27, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x04, 0x12, - 0x03, 0x49, 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x04, 0x05, 0x12, 0x03, 0x49, - 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x04, 0x01, 0x12, 0x03, 0x49, 0x09, 0x10, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x04, 0x03, 0x12, 0x03, 0x49, 0x13, 0x14, 0x0a, 0x0b, - 0x0a, 0x04, 0x04, 0x05, 0x02, 0x05, 0x12, 0x03, 0x4a, 0x02, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x05, 0x02, 0x05, 0x05, 0x12, 0x03, 0x4a, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, - 0x05, 0x01, 0x12, 0x03, 0x4a, 0x09, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x05, 0x03, - 0x12, 0x03, 0x4a, 0x19, 0x1a, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x06, 0x12, 0x03, 0x4b, - 0x02, 0x29, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x06, 0x06, 0x12, 0x03, 0x4b, 0x02, 0x17, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x06, 0x01, 0x12, 0x03, 0x4b, 0x18, 0x24, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x05, 0x02, 0x06, 0x03, 0x12, 0x03, 0x4b, 0x27, 0x28, 0x0a, 0x2a, 0x0a, 0x04, - 0x04, 0x05, 0x02, 0x07, 0x12, 0x03, 0x4c, 0x02, 0x1a, 0x22, 0x1d, 0x20, 0x65, 0x6e, 0x63, 0x61, - 0x70, 0x73, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x20, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, - 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x07, - 0x05, 0x12, 0x03, 0x4c, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x07, 0x01, 0x12, - 0x03, 0x4c, 0x08, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x07, 0x03, 0x12, 0x03, 0x4c, - 0x18, 0x19, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xf3, 0x19, 0x0a, 0x2a, 0x6d, + 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, + 0x67, 0x6f, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x58, 0xaa, 0x02, 0x14, 0x58, 0x6d, 0x74, 0x70, + 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, + 0xca, 0x02, 0x14, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xe2, 0x02, 0x20, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, + 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x58, 0x6d, 0x74, + 0x70, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x4a, 0xfd, 0x0f, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x36, 0x01, 0x0a, 0x2f, 0x0a, + 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x25, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, + 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x0a, 0x0a, 0x08, + 0x0a, 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, 0x1e, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, + 0x05, 0x00, 0x2b, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x01, 0x12, 0x03, 0x06, 0x00, 0x2a, 0x0a, 0x08, + 0x0a, 0x01, 0x08, 0x12, 0x03, 0x08, 0x00, 0x43, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, + 0x08, 0x00, 0x43, 0x0a, 0x58, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x0c, 0x00, 0x11, 0x01, 0x1a, + 0x4c, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x49, 0x64, 0x20, + 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66, 0x20, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x69, + 0x6e, 0x20, 0x61, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, + 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x0c, 0x08, 0x15, 0x0a, 0x34, 0x0a, 0x04, 0x04, 0x00, 0x02, + 0x00, 0x12, 0x03, 0x0d, 0x02, 0x1a, 0x22, 0x27, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x74, 0x79, 0x20, 0x67, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x69, + 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x0d, 0x02, 0x08, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0d, 0x09, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0d, 0x18, 0x19, 0x0a, 0x1e, 0x0a, 0x04, 0x04, 0x00, 0x02, + 0x01, 0x12, 0x03, 0x0e, 0x02, 0x15, 0x22, 0x11, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, + 0x01, 0x05, 0x12, 0x03, 0x0e, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, + 0x12, 0x03, 0x0e, 0x09, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, + 0x0e, 0x13, 0x14, 0x0a, 0x28, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x0f, 0x02, 0x1b, + 0x22, 0x1b, 0x20, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x0f, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x0f, 0x09, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, + 0x02, 0x03, 0x12, 0x03, 0x0f, 0x19, 0x1a, 0x0a, 0x28, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x03, 0x12, + 0x03, 0x10, 0x02, 0x1b, 0x22, 0x1b, 0x20, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x20, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, + 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x05, 0x12, 0x03, 0x10, 0x02, 0x08, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x10, 0x09, 0x16, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x00, 0x02, 0x03, 0x03, 0x12, 0x03, 0x10, 0x19, 0x1a, 0x0a, 0x67, 0x0a, 0x02, 0x05, + 0x00, 0x12, 0x04, 0x15, 0x00, 0x18, 0x01, 0x1a, 0x5b, 0x20, 0x52, 0x65, 0x63, 0x6f, 0x67, 0x6e, + 0x69, 0x7a, 0x65, 0x64, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x20, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x73, 0x0a, 0x20, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x6c, 0x69, 0x6e, 0x74, 0x3a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x45, + 0x4e, 0x55, 0x4d, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x5f, + 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x57, + 0x49, 0x54, 0x48, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x00, 0x01, 0x12, 0x03, 0x15, 0x05, 0x10, + 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x00, 0x12, 0x03, 0x16, 0x02, 0x1a, 0x0a, 0x0c, 0x0a, + 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x16, 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x05, + 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x16, 0x18, 0x19, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, + 0x01, 0x12, 0x03, 0x17, 0x02, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x01, 0x12, + 0x03, 0x17, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x17, + 0x15, 0x16, 0x0a, 0xa3, 0x01, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x1d, 0x00, 0x2b, 0x01, 0x1a, + 0x96, 0x01, 0x20, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x20, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x69, 0x6e, 0x67, 0x20, + 0x69, 0x74, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x61, + 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x20, 0x64, 0x65, + 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, + 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, + 0x03, 0x1d, 0x08, 0x16, 0x0a, 0x65, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x20, 0x02, + 0x19, 0x1a, 0x58, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, + 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x75, 0x73, 0x65, 0x64, + 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x0a, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, + 0x20, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, 0x20, 0x02, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, + 0x00, 0x01, 0x12, 0x03, 0x20, 0x10, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, + 0x12, 0x03, 0x20, 0x17, 0x18, 0x0a, 0x54, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, 0x12, 0x03, 0x22, + 0x02, 0x25, 0x1a, 0x47, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x65, 0x6e, + 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, + 0x72, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x01, 0x02, 0x01, 0x06, 0x12, 0x03, 0x22, 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, + 0x01, 0x01, 0x12, 0x03, 0x22, 0x16, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, + 0x12, 0x03, 0x22, 0x23, 0x24, 0x0a, 0x84, 0x01, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x02, 0x12, 0x03, + 0x25, 0x02, 0x1f, 0x1a, 0x77, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x66, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x75, + 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x63, 0x61, 0x73, 0x65, 0x0a, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x64, + 0x65, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x01, 0x02, 0x02, 0x04, 0x12, 0x03, 0x25, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, + 0x02, 0x02, 0x05, 0x12, 0x03, 0x25, 0x0b, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, + 0x01, 0x12, 0x03, 0x25, 0x12, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x03, 0x12, + 0x03, 0x25, 0x1d, 0x1e, 0x0a, 0x6e, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x03, 0x12, 0x03, 0x28, 0x02, + 0x27, 0x1a, 0x61, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6d, + 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x6c, + 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x0a, + 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, + 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x62, 0x79, + 0x74, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x04, 0x12, 0x03, 0x28, + 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x06, 0x12, 0x03, 0x28, 0x0b, 0x16, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x01, 0x12, 0x03, 0x28, 0x17, 0x22, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x03, 0x12, 0x03, 0x28, 0x25, 0x26, 0x0a, 0x25, 0x0a, 0x04, + 0x04, 0x01, 0x02, 0x04, 0x12, 0x03, 0x2a, 0x02, 0x14, 0x1a, 0x18, 0x20, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x65, 0x64, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x74, 0x73, 0x65, + 0x6c, 0x66, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x04, 0x05, 0x12, 0x03, 0x2a, 0x02, + 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x04, 0x01, 0x12, 0x03, 0x2a, 0x08, 0x0f, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x04, 0x03, 0x12, 0x03, 0x2a, 0x12, 0x13, 0x0a, 0x43, 0x0a, + 0x02, 0x04, 0x02, 0x12, 0x04, 0x2e, 0x00, 0x36, 0x01, 0x1a, 0x37, 0x20, 0x53, 0x69, 0x67, 0x6e, + 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, + 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x74, + 0x6f, 0x20, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x2e, 0x08, 0x15, 0x0a, 0x2a, + 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x30, 0x02, 0x14, 0x1a, 0x1d, 0x20, 0x4d, 0x55, + 0x53, 0x54, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x45, 0x6e, 0x63, 0x6f, 0x64, + 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, + 0x02, 0x00, 0x05, 0x12, 0x03, 0x30, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, + 0x01, 0x12, 0x03, 0x30, 0x08, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, + 0x03, 0x30, 0x12, 0x13, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x01, 0x12, 0x03, 0x31, 0x02, + 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x06, 0x12, 0x03, 0x31, 0x02, 0x17, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x01, 0x12, 0x03, 0x31, 0x18, 0x1e, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, 0x03, 0x31, 0x21, 0x22, 0x0a, 0x8a, 0x01, 0x0a, 0x04, + 0x04, 0x02, 0x02, 0x02, 0x12, 0x03, 0x35, 0x02, 0x1a, 0x1a, 0x7d, 0x20, 0x4d, 0x55, 0x53, 0x54, + 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, + 0x6f, 0x66, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x0a, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x2c, 0x0a, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x62, + 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x27, 0x73, 0x20, 0x70, + 0x72, 0x65, 0x2d, 0x6b, 0x65, 0x79, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, + 0x06, 0x12, 0x03, 0x35, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x01, 0x12, + 0x03, 0x35, 0x0c, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x03, 0x12, 0x03, 0x35, + 0x18, 0x19, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xf3, 0x07, 0x0a, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, - 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, - 0x63, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x78, 0x6d, 0x74, 0x70, 0x2e, + 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x15, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x1e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdb, 0x01, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x65, 0x12, 0x3b, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x65, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, + 0x73, 0x1a, 0x90, 0x01, 0x0a, 0x04, 0x50, 0x61, 0x72, 0x74, 0x12, 0x3b, 0x0a, 0x04, 0x70, 0x61, + 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0x1a, 0x21, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x73, 0x2f, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0x98, 0x07, 0x0a, 0x18, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, - 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x63, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, - 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, - 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x60, 0x0a, 0x0c, 0x64, 0x65, 0x6e, 0x79, 0x5f, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x78, 0x6d, - 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, - 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6e, - 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x6e, 0x79, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x5d, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x78, - 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x6c, - 0x6c, 0x6f, 0x77, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x5a, 0x0a, 0x0a, 0x64, 0x65, 0x6e, 0x79, 0x5f, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x78, 0x6d, 0x74, + 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x48, + 0x00, 0x52, 0x04, 0x70, 0x61, 0x72, 0x74, 0x12, 0x40, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6e, 0x79, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x48, 0x00, 0x52, 0x09, 0x64, 0x65, 0x6e, 0x79, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x12, 0x64, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x62, 0x6f, - 0x78, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x78, 0x6d, 0x74, + 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x48, 0x00, 0x52, 0x09, + 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x42, 0xca, 0x01, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x6c, 0x6c, 0x6f, - 0x77, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x12, 0x61, 0x0a, 0x0d, 0x64, 0x65, 0x6e, 0x79, - 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x3b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, - 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x44, 0x65, 0x6e, 0x79, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x48, 0x00, 0x52, 0x0b, - 0x64, 0x65, 0x6e, 0x79, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x1a, 0x39, 0x0a, 0x0c, 0x41, - 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x1a, 0x38, 0x0a, 0x0b, 0x44, 0x65, 0x6e, 0x79, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x5f, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, - 0x1a, 0x2b, 0x0a, 0x0c, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, - 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x73, 0x1a, 0x2a, 0x0a, - 0x0b, 0x44, 0x65, 0x6e, 0x79, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, - 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x08, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x73, 0x1a, 0x29, 0x0a, 0x0a, 0x41, 0x6c, 0x6c, - 0x6f, 0x77, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x49, 0x64, 0x73, 0x1a, 0x28, 0x0a, 0x09, 0x44, 0x65, 0x6e, 0x79, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x42, 0x0e, - 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x5b, - 0x0a, 0x19, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, - 0x6e, 0x63, 0x65, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x33, 0x0a, 0x02, 0x76, - 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, - 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, - 0x42, 0x09, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0xd3, 0x01, 0x0a, 0x19, - 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x17, 0x50, 0x72, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x50, 0x72, 0x6f, + 0x74, 0x73, 0x42, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, @@ -2916,130 +2445,602 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[ 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x73, 0x4a, 0xb4, 0x0f, 0x0a, 0x06, 0x12, 0x04, 0x04, 0x00, 0x42, 0x01, 0x0a, 0x99, 0x01, 0x0a, - 0x01, 0x0c, 0x12, 0x03, 0x04, 0x00, 0x12, 0x1a, 0x8e, 0x01, 0x20, 0x50, 0x72, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x0a, 0x0a, - 0x20, 0x46, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x75, - 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6c, 0x2c, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x65, 0x61, 0x64, 0x20, 0x74, 0x68, 0x65, - 0x79, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x20, 0x61, 0x0a, 0x20, 0x77, 0x61, 0x79, - 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x70, 0x72, - 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x73, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x06, - 0x00, 0x1e, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x08, 0x00, 0x2b, 0x0a, 0x08, 0x0a, - 0x01, 0x08, 0x12, 0x03, 0x0a, 0x00, 0x43, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x0a, - 0x00, 0x43, 0x0a, 0x62, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x0f, 0x00, 0x3d, 0x01, 0x1a, 0x56, - 0x20, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, - 0x63, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x27, 0x73, 0x20, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x20, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x0f, - 0x08, 0x20, 0x0a, 0x34, 0x0a, 0x04, 0x04, 0x00, 0x03, 0x00, 0x12, 0x04, 0x11, 0x02, 0x14, 0x03, - 0x1a, 0x26, 0x20, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x20, 0x31, 0x3a, 0x31, 0x20, 0x64, 0x69, 0x72, - 0x65, 0x63, 0x74, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x28, 0x44, 0x4d, 0x29, - 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, 0x00, - 0x01, 0x12, 0x03, 0x11, 0x0a, 0x16, 0x0a, 0x41, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, - 0x12, 0x03, 0x13, 0x04, 0x29, 0x1a, 0x32, 0x20, 0x41, 0x64, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x20, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x6c, - 0x6c, 0x6f, 0x77, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, - 0x00, 0x02, 0x00, 0x04, 0x12, 0x03, 0x13, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, - 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x13, 0x0d, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, - 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x13, 0x14, 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, - 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x13, 0x27, 0x28, 0x0a, 0x3b, 0x0a, 0x04, 0x04, 0x00, 0x03, - 0x01, 0x12, 0x04, 0x17, 0x02, 0x1a, 0x03, 0x1a, 0x2d, 0x20, 0x44, 0x65, 0x6e, 0x79, 0x20, 0x28, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x29, 0x20, 0x31, 0x3a, 0x31, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x28, 0x44, 0x4d, 0x29, 0x20, 0x61, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, 0x01, 0x01, 0x12, - 0x03, 0x17, 0x0a, 0x15, 0x0a, 0x40, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x01, 0x02, 0x00, 0x12, 0x03, - 0x19, 0x04, 0x29, 0x1a, 0x31, 0x20, 0x41, 0x64, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x69, - 0x76, 0x65, 0x6e, 0x20, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x6e, 0x79, - 0x20, 0x6c, 0x69, 0x73, 0x74, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x01, 0x02, 0x00, - 0x04, 0x12, 0x03, 0x19, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x01, 0x02, 0x00, - 0x05, 0x12, 0x03, 0x19, 0x0d, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x01, 0x02, 0x00, - 0x01, 0x12, 0x03, 0x19, 0x14, 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x01, 0x02, 0x00, - 0x03, 0x12, 0x03, 0x19, 0x27, 0x28, 0x0a, 0x37, 0x0a, 0x04, 0x04, 0x00, 0x03, 0x02, 0x12, 0x04, - 0x1d, 0x02, 0x20, 0x03, 0x1a, 0x29, 0x20, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x20, 0x56, 0x33, 0x20, - 0x31, 0x3a, 0x31, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x20, 0x28, 0x44, 0x4d, 0x29, 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, 0x02, 0x01, 0x12, 0x03, 0x1d, 0x0a, 0x16, 0x0a, 0x39, 0x0a, - 0x06, 0x04, 0x00, 0x03, 0x02, 0x02, 0x00, 0x12, 0x03, 0x1f, 0x04, 0x22, 0x1a, 0x2a, 0x20, 0x41, - 0x64, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x69, 0x6e, 0x62, - 0x6f, 0x78, 0x20, 0x69, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x02, - 0x02, 0x00, 0x04, 0x12, 0x03, 0x1f, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x02, - 0x02, 0x00, 0x05, 0x12, 0x03, 0x1f, 0x0d, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x02, - 0x02, 0x00, 0x01, 0x12, 0x03, 0x1f, 0x14, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x02, - 0x02, 0x00, 0x03, 0x12, 0x03, 0x1f, 0x20, 0x21, 0x0a, 0x3e, 0x0a, 0x04, 0x04, 0x00, 0x03, 0x03, - 0x12, 0x04, 0x23, 0x02, 0x26, 0x03, 0x1a, 0x30, 0x20, 0x44, 0x65, 0x6e, 0x79, 0x20, 0x28, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x29, 0x20, 0x56, 0x33, 0x20, 0x31, 0x3a, 0x31, 0x20, 0x64, 0x69, 0x72, - 0x65, 0x63, 0x74, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x28, 0x44, 0x4d, 0x29, - 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, 0x03, - 0x01, 0x12, 0x03, 0x23, 0x0a, 0x15, 0x0a, 0x38, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x03, 0x02, 0x00, - 0x12, 0x03, 0x25, 0x04, 0x22, 0x1a, 0x29, 0x20, 0x41, 0x64, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x20, 0x69, 0x64, 0x20, 0x74, - 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x6e, 0x79, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x0a, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x03, 0x02, 0x00, 0x04, 0x12, 0x03, 0x25, 0x04, 0x0c, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x03, 0x02, 0x00, 0x05, 0x12, 0x03, 0x25, 0x0d, 0x13, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x03, 0x02, 0x00, 0x01, 0x12, 0x03, 0x25, 0x14, 0x1d, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, 0x25, 0x20, 0x21, - 0x0a, 0x22, 0x0a, 0x04, 0x04, 0x00, 0x03, 0x04, 0x12, 0x04, 0x2a, 0x02, 0x2d, 0x03, 0x1a, 0x14, - 0x20, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, 0x04, 0x01, 0x12, 0x03, 0x2a, - 0x0a, 0x14, 0x0a, 0x3a, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x04, 0x02, 0x00, 0x12, 0x03, 0x2c, 0x04, - 0x21, 0x1a, 0x2b, 0x20, 0x41, 0x64, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x69, 0x76, 0x65, - 0x6e, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x0a, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x00, 0x03, 0x04, 0x02, 0x00, 0x04, 0x12, 0x03, 0x2c, 0x04, 0x0c, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x00, 0x03, 0x04, 0x02, 0x00, 0x05, 0x12, 0x03, 0x2c, 0x0d, 0x12, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x00, 0x03, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2c, 0x13, 0x1c, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x00, 0x03, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2c, 0x1f, 0x20, 0x0a, 0x28, - 0x0a, 0x04, 0x04, 0x00, 0x03, 0x05, 0x12, 0x04, 0x30, 0x02, 0x33, 0x03, 0x1a, 0x1a, 0x20, 0x44, - 0x65, 0x6e, 0x79, 0x20, 0x28, 0x64, 0x65, 0x6e, 0x79, 0x29, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x20, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, 0x05, - 0x01, 0x12, 0x03, 0x30, 0x0a, 0x13, 0x0a, 0x39, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x05, 0x02, 0x00, - 0x12, 0x03, 0x32, 0x04, 0x21, 0x1a, 0x2a, 0x20, 0x41, 0x64, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x20, - 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x6e, 0x79, 0x20, 0x6c, 0x69, 0x73, 0x74, - 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x05, 0x02, 0x00, 0x04, 0x12, 0x03, 0x32, 0x04, - 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x05, 0x02, 0x00, 0x05, 0x12, 0x03, 0x32, 0x0d, - 0x12, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x05, 0x02, 0x00, 0x01, 0x12, 0x03, 0x32, 0x13, - 0x1c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x05, 0x02, 0x00, 0x03, 0x12, 0x03, 0x32, 0x1f, - 0x20, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x00, 0x08, 0x00, 0x12, 0x04, 0x35, 0x02, 0x3c, 0x03, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x08, 0x00, 0x01, 0x12, 0x03, 0x35, 0x08, 0x14, 0x0a, 0x0b, 0x0a, - 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x36, 0x04, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, - 0x02, 0x00, 0x06, 0x12, 0x03, 0x36, 0x04, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, - 0x01, 0x12, 0x03, 0x36, 0x11, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, - 0x03, 0x36, 0x21, 0x22, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x37, 0x04, - 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, 0x37, 0x04, 0x0f, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x37, 0x10, 0x1c, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x37, 0x1f, 0x20, 0x0a, 0x0b, 0x0a, 0x04, 0x04, - 0x00, 0x02, 0x02, 0x12, 0x03, 0x38, 0x04, 0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, - 0x06, 0x12, 0x03, 0x38, 0x04, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, - 0x03, 0x38, 0x0f, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x38, - 0x1d, 0x1e, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x03, 0x12, 0x03, 0x39, 0x04, 0x1d, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x06, 0x12, 0x03, 0x39, 0x04, 0x0d, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x39, 0x0e, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x00, 0x02, 0x03, 0x03, 0x12, 0x03, 0x39, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, - 0x04, 0x12, 0x03, 0x3a, 0x04, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x04, 0x06, 0x12, - 0x03, 0x3a, 0x04, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x04, 0x01, 0x12, 0x03, 0x3a, - 0x11, 0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x04, 0x03, 0x12, 0x03, 0x3a, 0x22, 0x23, - 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x05, 0x12, 0x03, 0x3b, 0x04, 0x22, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x00, 0x02, 0x05, 0x06, 0x12, 0x03, 0x3b, 0x04, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x00, 0x02, 0x05, 0x01, 0x12, 0x03, 0x3b, 0x10, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, - 0x05, 0x03, 0x12, 0x03, 0x3b, 0x20, 0x21, 0x0a, 0x31, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x40, - 0x00, 0x42, 0x01, 0x1a, 0x25, 0x20, 0x54, 0x68, 0x65, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x67, 0x6f, 0x65, 0x73, 0x20, 0x6f, 0x76, 0x65, 0x72, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x69, 0x72, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, - 0x01, 0x12, 0x03, 0x40, 0x08, 0x21, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x08, 0x00, 0x12, 0x03, - 0x41, 0x02, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x08, 0x00, 0x01, 0x12, 0x03, 0x41, 0x08, - 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x41, 0x12, 0x24, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, 0x41, 0x12, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x41, 0x1d, 0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, - 0x02, 0x00, 0x03, 0x12, 0x03, 0x41, 0x22, 0x23, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x4a, 0xe4, 0x03, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x15, 0x01, 0x0a, 0x21, 0x0a, 0x01, + 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x17, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x0a, 0x0a, + 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, 0x1e, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, + 0x03, 0x05, 0x00, 0x28, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x07, 0x00, 0x43, 0x0a, 0x09, + 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x07, 0x00, 0x43, 0x0a, 0x4c, 0x0a, 0x02, 0x04, 0x00, 0x12, + 0x04, 0x0b, 0x00, 0x15, 0x01, 0x1a, 0x40, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x65, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6d, 0x70, + 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6f, 0x72, 0x67, 0x2f, + 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, + 0x0b, 0x08, 0x11, 0x0a, 0x42, 0x0a, 0x04, 0x04, 0x00, 0x03, 0x00, 0x12, 0x04, 0x0d, 0x02, 0x12, + 0x03, 0x1a, 0x34, 0x20, 0x50, 0x61, 0x72, 0x74, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, + 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x6f, 0x66, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x65, 0x20, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, 0x00, 0x01, + 0x12, 0x03, 0x0d, 0x0a, 0x0e, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x08, 0x00, 0x12, + 0x04, 0x0e, 0x04, 0x11, 0x05, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x08, 0x00, 0x01, + 0x12, 0x03, 0x0e, 0x0a, 0x11, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x12, + 0x03, 0x0f, 0x06, 0x1e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, + 0x03, 0x0f, 0x06, 0x14, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, + 0x03, 0x0f, 0x15, 0x19, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, + 0x03, 0x0f, 0x1c, 0x1d, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, + 0x10, 0x06, 0x1e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, + 0x10, 0x06, 0x0f, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, + 0x10, 0x10, 0x19, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, + 0x10, 0x1c, 0x1d, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x14, 0x02, 0x1a, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x04, 0x12, 0x03, 0x14, 0x02, 0x0a, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x14, 0x0b, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x14, 0x10, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, + 0x02, 0x00, 0x03, 0x12, 0x03, 0x14, 0x18, 0x19, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x0a, 0xd2, 0x0b, 0x0a, 0x1e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x21, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x58, 0x0a, + 0x0f, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x56, 0x31, + 0x12, 0x45, 0x0a, 0x0a, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x09, 0x6b, 0x65, + 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x22, 0x5e, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x74, 0x61, + 0x63, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x56, 0x32, 0x12, 0x4b, 0x0a, 0x0a, 0x6b, 0x65, + 0x79, 0x5f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, + 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x09, 0x6b, 0x65, + 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x22, 0x8e, 0x01, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x63, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x38, 0x0a, 0x02, 0x76, 0x31, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x56, 0x31, 0x48, 0x00, 0x52, + 0x02, 0x76, 0x31, 0x12, 0x38, 0x0a, 0x02, 0x76, 0x32, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x26, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x42, + 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x56, 0x32, 0x48, 0x00, 0x52, 0x02, 0x76, 0x32, 0x42, 0x09, 0x0a, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0xc8, 0x01, 0x0a, 0x19, 0x63, 0x6f, 0x6d, + 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, + 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x58, 0xaa, 0x02, 0x14, 0x58, 0x6d, 0x74, + 0x70, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0xca, 0x02, 0x14, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xe2, 0x02, 0x20, 0x58, 0x6d, 0x74, 0x70, 0x5c, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, + 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x58, 0x6d, + 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0x4a, 0xd7, 0x06, 0x0a, 0x06, 0x12, 0x04, 0x05, 0x00, 0x1f, 0x01, 0x0a, 0xc9, + 0x02, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x05, 0x00, 0x12, 0x1a, 0xbe, 0x02, 0x20, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x63, 0x74, 0x20, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, + 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x64, 0x76, 0x65, 0x72, 0x74, 0x69, + 0x73, 0x65, 0x20, 0x75, 0x73, 0x65, 0x72, 0x27, 0x73, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x0a, 0x20, 0x54, 0x68, 0x65, 0x79, 0x20, 0x61, 0x72, 0x65, 0x20, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x77, 0x65, 0x6c, + 0x6c, 0x20, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x20, 0x73, + 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x70, 0x61, 0x72, + 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x73, 0x0a, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x66, + 0x69, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6d, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x79, 0x20, 0x77, 0x69, 0x73, 0x68, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x75, + 0x6e, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x75, 0x73, 0x65, 0x72, 0x2e, 0x0a, 0x20, 0x54, 0x68, 0x65, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, + 0x74, 0x6f, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x65, 0x72, 0x69, 0x76, 0x65, 0x20, 0x65, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x0a, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x2d, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2c, 0x20, 0x65, 0x2e, 0x67, 0x2e, 0x20, 0x69, 0x6e, 0x76, + 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, + 0x03, 0x07, 0x00, 0x1e, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x09, 0x00, 0x2b, 0x0a, + 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x0b, 0x00, 0x43, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, + 0x03, 0x0b, 0x00, 0x43, 0x0a, 0x5a, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x10, 0x00, 0x12, 0x01, + 0x1a, 0x4e, 0x20, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x3a, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, + 0x6b, 0x65, 0x79, 0x20, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x56, 0x31, 0x20, 0x75, 0x73, + 0x69, 0x6e, 0x67, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x2e, 0x0a, + 0x20, 0x54, 0x68, 0x65, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x20, + 0x4d, 0x55, 0x53, 0x54, 0x20, 0x62, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x2e, 0x0a, + 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x10, 0x08, 0x17, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x11, 0x02, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, + 0x00, 0x06, 0x12, 0x03, 0x11, 0x02, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, + 0x12, 0x03, 0x11, 0x12, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, + 0x11, 0x1f, 0x20, 0x0a, 0x38, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x15, 0x00, 0x17, 0x01, 0x1a, + 0x2c, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x62, 0x75, 0x6e, 0x64, 0x6c, + 0x65, 0x20, 0x56, 0x32, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x73, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, + 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x15, 0x08, 0x17, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, + 0x00, 0x12, 0x03, 0x16, 0x02, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, + 0x03, 0x16, 0x02, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x16, + 0x18, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x16, 0x25, 0x26, + 0x0a, 0x25, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x1a, 0x00, 0x1f, 0x01, 0x1a, 0x19, 0x20, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, + 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, + 0x1a, 0x08, 0x15, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x02, 0x08, 0x00, 0x12, 0x04, 0x1b, 0x02, 0x1e, + 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x08, 0x00, 0x01, 0x12, 0x03, 0x1b, 0x08, 0x0f, 0x0a, + 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x1c, 0x04, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x02, 0x02, 0x00, 0x06, 0x12, 0x03, 0x1c, 0x04, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, + 0x02, 0x00, 0x01, 0x12, 0x03, 0x1c, 0x14, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, + 0x03, 0x12, 0x03, 0x1c, 0x19, 0x1a, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x01, 0x12, 0x03, + 0x1d, 0x04, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x06, 0x12, 0x03, 0x1d, 0x04, + 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x01, 0x12, 0x03, 0x1d, 0x14, 0x16, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, 0x03, 0x1d, 0x19, 0x1a, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xe8, 0x04, 0x0a, 0x1c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x65, 0x63, 0x69, 0x65, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x2b, 0x0a, + 0x0c, 0x45, 0x63, 0x69, 0x65, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, + 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x42, + 0x09, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0xc6, 0x01, 0x0a, 0x19, 0x63, + 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x0a, 0x45, 0x63, 0x69, 0x65, 0x73, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, + 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x58, 0xaa, 0x02, 0x14, 0x58, 0x6d, 0x74, + 0x70, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0xca, 0x02, 0x14, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xe2, 0x02, 0x20, 0x58, 0x6d, 0x74, 0x70, 0x5c, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, + 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x58, 0x6d, + 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0x4a, 0xb2, 0x02, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x0e, 0x01, 0x0a, 0x31, + 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x27, 0x20, 0x45, 0x43, 0x49, 0x45, 0x53, + 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x45, 0x43, 0x49, 0x45, 0x53, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x73, + 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, 0x1e, 0x0a, 0x08, 0x0a, 0x01, 0x08, + 0x12, 0x03, 0x05, 0x00, 0x43, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x05, 0x00, 0x43, + 0x0a, 0x44, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x09, 0x00, 0x0e, 0x01, 0x1a, 0x38, 0x20, 0x45, + 0x63, 0x69, 0x65, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, + 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x45, 0x43, 0x49, + 0x45, 0x53, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x20, 0x70, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x09, + 0x08, 0x14, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x00, 0x08, 0x00, 0x12, 0x04, 0x0a, 0x02, 0x0d, 0x03, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x08, 0x00, 0x01, 0x12, 0x03, 0x0a, 0x08, 0x0f, 0x0a, 0x3e, + 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0c, 0x04, 0x11, 0x1a, 0x31, 0x20, 0x45, 0x78, + 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, + 0x45, 0x43, 0x49, 0x45, 0x53, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x20, + 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x0a, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x0c, 0x04, 0x09, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0c, 0x0a, 0x0c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, + 0x02, 0x00, 0x03, 0x12, 0x03, 0x0c, 0x0f, 0x10, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x0a, 0xd5, 0x15, 0x0a, 0x1d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x15, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x21, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd6, + 0x02, 0x0a, 0x0f, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6f, + 0x64, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x72, 0x6c, 0x12, + 0x21, 0x0a, 0x0c, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x12, 0x20, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x12, 0x44, 0x0a, 0x1e, 0x6f, 0x70, 0x61, 0x71, 0x75, 0x65, 0x5f, 0x63, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1c, 0x6f, 0x70, + 0x61, 0x71, 0x75, 0x65, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x75, 0x6e, + 0x69, 0x78, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0d, 0x75, 0x6e, 0x69, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x54, 0x65, 0x78, 0x74, + 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xd5, 0x01, 0x0a, 0x0b, 0x46, 0x72, 0x61, 0x6d, + 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x09, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x65, 0x0a, 0x18, 0x73, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x75, 0x6e, + 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, + 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, + 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x15, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x1f, + 0x0a, 0x0b, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6f, 0x64, 0x79, 0x42, + 0xc7, 0x01, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x0b, 0x46, + 0x72, 0x61, 0x6d, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x58, + 0xaa, 0x02, 0x14, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, 0x14, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xe2, 0x02, + 0x20, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0xea, 0x02, 0x15, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xd4, 0x0e, 0x0a, 0x06, 0x12, 0x04, + 0x01, 0x00, 0x2e, 0x01, 0x0a, 0x47, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x3d, + 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, + 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x6b, 0x65, 0x79, + 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x0a, 0x0a, 0x08, 0x0a, + 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, 0x1e, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x05, + 0x00, 0x2b, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x01, 0x12, 0x03, 0x06, 0x00, 0x2a, 0x0a, 0x08, 0x0a, + 0x01, 0x08, 0x12, 0x03, 0x08, 0x00, 0x43, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x08, + 0x00, 0x43, 0x0a, 0x83, 0x01, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x0d, 0x00, 0x22, 0x01, 0x1a, + 0x77, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x74, 0x68, + 0x61, 0x74, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x73, + 0x69, 0x64, 0x65, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x60, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x60, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, + 0x03, 0x0d, 0x08, 0x17, 0x0a, 0x56, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x10, 0x02, + 0x17, 0x1a, 0x49, 0x20, 0x54, 0x68, 0x65, 0x20, 0x55, 0x52, 0x4c, 0x20, 0x6f, 0x66, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x61, + 0x73, 0x20, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x65, 0x64, 0x0a, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x62, + 0x65, 0x20, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d, + 0x20, 0x60, 0x70, 0x6f, 0x73, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x60, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x10, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, + 0x02, 0x00, 0x01, 0x12, 0x03, 0x10, 0x09, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, + 0x03, 0x12, 0x03, 0x10, 0x15, 0x16, 0x0a, 0x34, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, + 0x12, 0x02, 0x19, 0x1a, 0x27, 0x20, 0x54, 0x68, 0x65, 0x20, 0x31, 0x2d, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x65, 0x64, 0x20, 0x62, 0x75, 0x74, 0x74, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, + 0x77, 0x61, 0x73, 0x20, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x65, 0x64, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x12, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, + 0x02, 0x01, 0x01, 0x12, 0x03, 0x12, 0x08, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, + 0x03, 0x12, 0x03, 0x12, 0x17, 0x18, 0x0a, 0x45, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, + 0x14, 0x02, 0x2b, 0x1a, 0x38, 0x20, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, + 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x20, 0x69, 0x6e, 0x20, + 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x20, 0x73, 0x69, 0x6e, + 0x63, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x0a, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x14, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x14, 0x09, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, + 0x02, 0x03, 0x12, 0x03, 0x14, 0x15, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x08, + 0x12, 0x03, 0x14, 0x17, 0x2a, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x02, 0x02, 0x08, 0x03, 0x12, + 0x03, 0x14, 0x18, 0x29, 0x0a, 0x8a, 0x01, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x03, 0x12, 0x03, 0x17, + 0x02, 0x2c, 0x1a, 0x7d, 0x20, 0x41, 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x6e, + 0x6f, 0x74, 0x20, 0x74, 0x69, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x79, 0x74, 0x68, + 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x2e, 0x20, 0x57, 0x69, 0x6c, 0x6c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x20, 0x6f, 0x72, + 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, + 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x05, 0x12, 0x03, 0x17, 0x02, 0x08, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x17, 0x09, 0x27, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x00, 0x02, 0x03, 0x03, 0x12, 0x03, 0x17, 0x2a, 0x2b, 0x0a, 0x1d, 0x0a, 0x04, 0x04, + 0x00, 0x02, 0x04, 0x12, 0x03, 0x19, 0x02, 0x1c, 0x1a, 0x10, 0x20, 0x55, 0x6e, 0x69, 0x78, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, + 0x02, 0x04, 0x05, 0x12, 0x03, 0x19, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x04, + 0x01, 0x12, 0x03, 0x19, 0x09, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x04, 0x03, 0x12, + 0x03, 0x19, 0x1a, 0x1b, 0x0a, 0x31, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x05, 0x12, 0x03, 0x1b, 0x02, + 0x18, 0x1a, 0x24, 0x20, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x66, + 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x05, 0x05, + 0x12, 0x03, 0x1b, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x05, 0x01, 0x12, 0x03, + 0x1b, 0x09, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x05, 0x03, 0x12, 0x03, 0x1b, 0x16, + 0x17, 0x0a, 0x65, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x06, 0x12, 0x03, 0x1d, 0x02, 0x13, 0x1a, 0x58, + 0x20, 0x41, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, + 0x7a, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, + 0x28, 0x66, 0x6f, 0x72, 0x20, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x76, 0x69, 0x61, + 0x20, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, + 0x29, 0x29, 0x2e, 0x20, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x34, 0x30, 0x39, 0x36, + 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x06, + 0x05, 0x12, 0x03, 0x1d, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x06, 0x01, 0x12, + 0x03, 0x1d, 0x09, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x06, 0x03, 0x12, 0x03, 0x1d, + 0x11, 0x12, 0x0a, 0x22, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x07, 0x12, 0x03, 0x1f, 0x02, 0x15, 0x1a, + 0x15, 0x20, 0x41, 0x20, 0x30, 0x78, 0x20, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x20, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x07, 0x05, 0x12, + 0x03, 0x1f, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x07, 0x01, 0x12, 0x03, 0x1f, + 0x09, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x07, 0x03, 0x12, 0x03, 0x1f, 0x13, 0x14, + 0x0a, 0x28, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x08, 0x12, 0x03, 0x21, 0x02, 0x1c, 0x1a, 0x1b, 0x20, + 0x41, 0x20, 0x68, 0x61, 0x73, 0x68, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, + 0x02, 0x08, 0x05, 0x12, 0x03, 0x21, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x08, + 0x01, 0x12, 0x03, 0x21, 0x09, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x08, 0x03, 0x12, + 0x03, 0x21, 0x1a, 0x1b, 0x0a, 0x79, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x26, 0x00, 0x2e, 0x01, + 0x1a, 0x6d, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x20, 0x70, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, + 0x65, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x60, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x60, 0x20, 0x69, 0x6e, 0x20, + 0x74, 0x68, 0x65, 0x0a, 0x20, 0x60, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, + 0x74, 0x61, 0x60, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x46, 0x72, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, + 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x26, 0x08, 0x13, 0x0a, 0x0b, 0x0a, 0x04, 0x04, + 0x01, 0x02, 0x00, 0x12, 0x03, 0x27, 0x02, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, + 0x06, 0x12, 0x03, 0x27, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, + 0x03, 0x27, 0x0c, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x27, + 0x18, 0x19, 0x0a, 0x92, 0x01, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, 0x12, 0x03, 0x2a, 0x02, 0x35, + 0x1a, 0x84, 0x01, 0x20, 0x54, 0x68, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x2c, 0x20, 0x75, 0x73, 0x65, + 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x58, 0x4d, + 0x54, 0x50, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x0a, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x20, 0x61, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, + 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x20, + 0x61, 0x20, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x06, + 0x12, 0x03, 0x2a, 0x02, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, + 0x2a, 0x18, 0x30, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x2a, 0x33, + 0x34, 0x0a, 0x92, 0x01, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x02, 0x12, 0x03, 0x2d, 0x02, 0x18, 0x1a, + 0x84, 0x01, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x46, 0x72, + 0x61, 0x6d, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x6f, 0x64, 0x79, 0x20, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x73, 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x76, 0x65, 0x72, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x61, 0x6e, 0x0a, 0x20, 0x68, + 0x61, 0x70, 0x70, 0x65, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x62, 0x79, 0x74, 0x65, 0x2d, + 0x70, 0x65, 0x72, 0x66, 0x65, 0x63, 0x74, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x05, 0x12, + 0x03, 0x2d, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x01, 0x12, 0x03, 0x2d, + 0x08, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x03, 0x12, 0x03, 0x2d, 0x16, 0x17, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xb5, 0x20, 0x0a, 0x1e, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x1a, 0x21, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, + 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb5, 0x01, 0x0a, 0x0f, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x31, 0x12, 0x3e, 0x0a, 0x06, 0x73, + 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x6d, + 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x75, 0x6e, + 0x64, 0x6c, 0x65, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x09, 0x72, + 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, + 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, + 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, + 0x71, 0x0a, 0x09, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x56, 0x31, 0x12, 0x21, 0x0a, 0x0c, + 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, + 0x41, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x69, 0x70, 0x68, + 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x74, 0x22, 0x46, 0x0a, 0x0f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x56, 0x32, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x64, 0x4e, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x22, 0xdd, 0x01, 0x0a, 0x09, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x56, 0x32, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, + 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x63, + 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x21, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x74, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x12, 0x24, + 0x0a, 0x0b, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x48, 0x6d, 0x61, + 0x63, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x5f, 0x70, + 0x75, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x0a, 0x73, 0x68, 0x6f, + 0x75, 0x6c, 0x64, 0x50, 0x75, 0x73, 0x68, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x73, + 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x73, + 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x22, 0x7c, 0x0a, 0x07, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x32, 0x0a, 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x56, 0x31, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x12, 0x32, 0x0a, 0x02, 0x76, 0x32, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x56, 0x32, 0x48, 0x00, 0x52, 0x02, 0x76, 0x32, 0x42, 0x09, 0x0a, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xed, 0x02, 0x0a, 0x0e, 0x44, 0x65, 0x63, + 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x30, 0x0a, 0x11, 0x72, + 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, + 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, + 0x07, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, + 0x73, 0x65, 0x6e, 0x74, 0x4e, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x50, 0x0a, 0x0c, 0x63, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, + 0x0c, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, + 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x74, + 0x65, 0x73, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0xc8, 0x01, 0x0a, 0x19, 0x63, 0x6f, 0x6d, + 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x0c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, + 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x58, 0xaa, 0x02, 0x14, 0x58, 0x6d, 0x74, + 0x70, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0xca, 0x02, 0x14, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xe2, 0x02, 0x20, 0x58, 0x6d, 0x74, 0x70, 0x5c, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, + 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x58, 0x6d, + 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0x4a, 0xf2, 0x14, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x4d, 0x01, 0x0a, 0x4a, + 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x40, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, + 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, + 0x03, 0x03, 0x00, 0x1e, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x05, 0x00, 0x2b, 0x0a, + 0x09, 0x0a, 0x02, 0x03, 0x01, 0x12, 0x03, 0x06, 0x00, 0x37, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x02, + 0x12, 0x03, 0x07, 0x00, 0x2b, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x09, 0x00, 0x43, 0x0a, + 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x09, 0x00, 0x43, 0x0a, 0x8c, 0x01, 0x0a, 0x02, 0x04, + 0x00, 0x12, 0x04, 0x10, 0x00, 0x14, 0x01, 0x1a, 0x72, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x65, 0x64, 0x20, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x20, 0x61, + 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, + 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x75, 0x73, 0x65, 0x64, 0x0a, 0x20, 0x61, 0x73, 0x20, 0x61, 0x73, + 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, + 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x32, 0x0c, 0x20, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x56, 0x31, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, + 0x12, 0x03, 0x10, 0x08, 0x17, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x11, + 0x02, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x11, 0x02, 0x11, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x11, 0x12, 0x18, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x11, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x12, 0x02, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, + 0x01, 0x06, 0x12, 0x03, 0x12, 0x02, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, + 0x12, 0x03, 0x12, 0x12, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, + 0x12, 0x1e, 0x1f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x13, 0x02, 0x17, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x13, 0x02, 0x08, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x13, 0x09, 0x12, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x13, 0x15, 0x16, 0x0a, 0x37, 0x0a, 0x02, 0x04, 0x01, + 0x12, 0x04, 0x17, 0x00, 0x1c, 0x01, 0x1a, 0x2b, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x6f, 0x70, 0x20, 0x6c, 0x65, 0x76, 0x65, + 0x6c, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x17, 0x08, 0x11, 0x0a, + 0x33, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x19, 0x02, 0x19, 0x1a, 0x26, 0x20, 0x65, + 0x6e, 0x63, 0x61, 0x70, 0x73, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x20, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x65, 0x64, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x56, 0x31, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x05, 0x12, 0x03, 0x19, + 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x19, 0x08, 0x14, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x19, 0x17, 0x18, 0x0a, 0x47, + 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, 0x12, 0x03, 0x1b, 0x02, 0x1c, 0x1a, 0x3a, 0x20, 0x43, 0x69, + 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x65, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x20, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x06, + 0x12, 0x03, 0x1b, 0x02, 0x0c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, + 0x1b, 0x0d, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x1b, 0x1a, + 0x1b, 0x0a, 0x88, 0x02, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x24, 0x00, 0x29, 0x01, 0x1a, 0xed, + 0x01, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x20, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x0a, 0x20, 0x6f, 0x62, + 0x73, 0x65, 0x72, 0x76, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x68, + 0x6f, 0x77, 0x65, 0x76, 0x65, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x64, 0x20, 0x61, 0x73, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, + 0x65, 0x64, 0x20, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x41, 0x45, 0x41, 0x44, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2c, 0x0a, 0x20, 0x74, 0x68, + 0x75, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x61, 0x6d, + 0x70, 0x65, 0x72, 0x20, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x2e, 0x0a, 0x32, 0x0c, + 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x56, 0x32, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, + 0x04, 0x02, 0x01, 0x12, 0x03, 0x24, 0x08, 0x17, 0x0a, 0x35, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, + 0x12, 0x03, 0x26, 0x02, 0x18, 0x1a, 0x28, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x73, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x0a, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x05, 0x12, 0x03, 0x26, 0x02, 0x08, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x26, 0x09, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x26, 0x16, 0x17, 0x0a, 0x2f, 0x0a, 0x04, 0x04, 0x02, 0x02, + 0x01, 0x12, 0x03, 0x28, 0x02, 0x13, 0x1a, 0x22, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x6f, 0x70, + 0x69, 0x63, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x62, + 0x65, 0x6c, 0x6f, 0x6e, 0x67, 0x73, 0x20, 0x74, 0x6f, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, + 0x02, 0x01, 0x05, 0x12, 0x03, 0x28, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, + 0x01, 0x12, 0x03, 0x28, 0x09, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, + 0x03, 0x28, 0x11, 0x12, 0x0a, 0x4d, 0x0a, 0x02, 0x04, 0x03, 0x12, 0x04, 0x2c, 0x00, 0x37, 0x01, + 0x1a, 0x41, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x62, 0x69, + 0x6e, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, + 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x03, 0x2c, 0x08, 0x11, 0x0a, + 0x33, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x00, 0x12, 0x03, 0x2e, 0x02, 0x19, 0x1a, 0x26, 0x20, 0x65, + 0x6e, 0x63, 0x61, 0x70, 0x73, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x20, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x65, 0x64, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x56, 0x32, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x05, 0x12, 0x03, 0x2e, + 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2e, 0x08, 0x14, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2e, 0x17, 0x18, 0x0a, 0x46, + 0x0a, 0x04, 0x04, 0x03, 0x02, 0x01, 0x12, 0x03, 0x30, 0x02, 0x1c, 0x1a, 0x39, 0x20, 0x43, 0x69, + 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x65, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x06, 0x12, + 0x03, 0x30, 0x02, 0x0c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x12, 0x03, 0x30, + 0x0d, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x03, 0x12, 0x03, 0x30, 0x1a, 0x1b, + 0x0a, 0x5c, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x02, 0x12, 0x03, 0x33, 0x02, 0x21, 0x1a, 0x4f, 0x20, + 0x48, 0x4d, 0x41, 0x43, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x20, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, + 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x6b, 0x65, + 0x79, 0x20, 0x64, 0x65, 0x72, 0x69, 0x76, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x0a, 0x20, 0x6b, 0x65, 0x79, 0x0a, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x04, 0x12, 0x03, 0x33, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x03, 0x02, 0x02, 0x05, 0x12, 0x03, 0x33, 0x0b, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, + 0x02, 0x02, 0x01, 0x12, 0x03, 0x33, 0x11, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, + 0x03, 0x12, 0x03, 0x33, 0x1f, 0x20, 0x0a, 0x5f, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x03, 0x12, 0x03, + 0x36, 0x02, 0x20, 0x1a, 0x52, 0x20, 0x46, 0x6c, 0x61, 0x67, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x77, 0x68, 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, + 0x20, 0x62, 0x65, 0x20, 0x70, 0x75, 0x73, 0x68, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, + 0x61, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x03, 0x04, + 0x12, 0x03, 0x36, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x03, 0x05, 0x12, 0x03, + 0x36, 0x0b, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x03, 0x01, 0x12, 0x03, 0x36, 0x10, + 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x03, 0x03, 0x12, 0x03, 0x36, 0x1e, 0x1f, 0x0a, + 0x1f, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x3a, 0x00, 0x3f, 0x01, 0x1a, 0x13, 0x20, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, + 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x04, 0x01, 0x12, 0x03, 0x3a, 0x08, 0x0f, 0x0a, 0x0c, 0x0a, 0x04, + 0x04, 0x04, 0x08, 0x00, 0x12, 0x04, 0x3b, 0x02, 0x3e, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, + 0x08, 0x00, 0x01, 0x12, 0x03, 0x3b, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, + 0x12, 0x03, 0x3c, 0x04, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x06, 0x12, 0x03, + 0x3c, 0x04, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x3c, 0x0e, + 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x3c, 0x13, 0x14, 0x0a, + 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x01, 0x12, 0x03, 0x3d, 0x04, 0x15, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x04, 0x02, 0x01, 0x06, 0x12, 0x03, 0x3d, 0x04, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, + 0x02, 0x01, 0x01, 0x12, 0x03, 0x3d, 0x0e, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, + 0x03, 0x12, 0x03, 0x3d, 0x13, 0x14, 0x0a, 0xae, 0x01, 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04, 0x44, + 0x00, 0x4d, 0x01, 0x1a, 0xa1, 0x01, 0x20, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x20, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, + 0x0a, 0x20, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6e, + 0x6f, 0x74, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2c, 0x20, 0x62, 0x75, 0x74, 0x0a, 0x20, 0x6d, + 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x05, 0x01, 0x12, 0x03, + 0x44, 0x08, 0x16, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, 0x12, 0x03, 0x45, 0x02, 0x10, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x05, 0x12, 0x03, 0x45, 0x02, 0x08, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x01, 0x12, 0x03, 0x45, 0x09, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x05, 0x02, 0x00, 0x03, 0x12, 0x03, 0x45, 0x0e, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, + 0x02, 0x01, 0x12, 0x03, 0x46, 0x02, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x05, + 0x12, 0x03, 0x46, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x01, 0x12, 0x03, + 0x46, 0x09, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x03, 0x12, 0x03, 0x46, 0x1b, + 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x02, 0x12, 0x03, 0x47, 0x02, 0x1c, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x05, 0x02, 0x02, 0x05, 0x12, 0x03, 0x47, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x05, 0x02, 0x02, 0x01, 0x12, 0x03, 0x47, 0x09, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, + 0x02, 0x02, 0x03, 0x12, 0x03, 0x47, 0x1a, 0x1b, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x03, + 0x12, 0x03, 0x48, 0x02, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x03, 0x04, 0x12, 0x03, + 0x48, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x03, 0x05, 0x12, 0x03, 0x48, 0x0b, + 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x03, 0x01, 0x12, 0x03, 0x48, 0x12, 0x23, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x03, 0x03, 0x12, 0x03, 0x48, 0x26, 0x27, 0x0a, 0x0b, 0x0a, + 0x04, 0x04, 0x05, 0x02, 0x04, 0x12, 0x03, 0x49, 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, + 0x02, 0x04, 0x05, 0x12, 0x03, 0x49, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x04, + 0x01, 0x12, 0x03, 0x49, 0x09, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x04, 0x03, 0x12, + 0x03, 0x49, 0x13, 0x14, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x05, 0x12, 0x03, 0x4a, 0x02, + 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x05, 0x05, 0x12, 0x03, 0x4a, 0x02, 0x08, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x05, 0x01, 0x12, 0x03, 0x4a, 0x09, 0x16, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x05, 0x02, 0x05, 0x03, 0x12, 0x03, 0x4a, 0x19, 0x1a, 0x0a, 0x0b, 0x0a, 0x04, 0x04, + 0x05, 0x02, 0x06, 0x12, 0x03, 0x4b, 0x02, 0x29, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x06, + 0x06, 0x12, 0x03, 0x4b, 0x02, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x06, 0x01, 0x12, + 0x03, 0x4b, 0x18, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x06, 0x03, 0x12, 0x03, 0x4b, + 0x27, 0x28, 0x0a, 0x2a, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x07, 0x12, 0x03, 0x4c, 0x02, 0x1a, 0x22, + 0x1d, 0x20, 0x65, 0x6e, 0x63, 0x61, 0x70, 0x73, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x20, 0x45, + 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x05, 0x02, 0x07, 0x05, 0x12, 0x03, 0x4c, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x05, 0x02, 0x07, 0x01, 0x12, 0x03, 0x4c, 0x08, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, + 0x02, 0x07, 0x03, 0x12, 0x03, 0x4c, 0x18, 0x19, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xe0, 0x05, 0x0a, 0x25, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x78, 0x6d, 0x74, 0x70, diff --git a/xmtp_proto/src/gen/xmtp.message_contents.serde.rs b/xmtp_proto/src/gen/xmtp.message_contents.serde.rs index a28bf3ea2..279816e33 100644 --- a/xmtp_proto/src/gen/xmtp.message_contents.serde.rs +++ b/xmtp_proto/src/gen/xmtp.message_contents.serde.rs @@ -116,14 +116,17 @@ impl serde::Serialize for ciphertext::Aes256gcmHkdfsha256 { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.Ciphertext.Aes256gcmHkdfsha256", len)?; if !self.hkdf_salt.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("hkdfSalt", pbjson::private::base64::encode(&self.hkdf_salt).as_str())?; } if !self.gcm_nonce.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("gcmNonce", pbjson::private::base64::encode(&self.gcm_nonce).as_str())?; } 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() @@ -526,6 +529,7 @@ impl serde::Serialize for ConsentProofPayload { } if self.timestamp != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("timestamp", ToString::to_string(&self.timestamp).as_str())?; } if self.payload_version != 0 { @@ -1180,6 +1184,7 @@ impl serde::Serialize for ConversationReference { } if self.created_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("createdNs", ToString::to_string(&self.created_ns).as_str())?; } if let Some(v) = self.context.as_ref() { @@ -1360,6 +1365,7 @@ impl serde::Serialize for DecodedMessage { } if self.sent_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("sentNs", ToString::to_string(&self.sent_ns).as_str())?; } if !self.content_topic.is_empty() { @@ -1370,6 +1376,7 @@ impl serde::Serialize for DecodedMessage { } if !self.content_bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("contentBytes", pbjson::private::base64::encode(&self.content_bytes).as_str())?; } struct_ser.end() @@ -1551,6 +1558,7 @@ impl serde::Serialize for EciesMessage { match v { ecies_message::Version::V1(v) => { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("v1", pbjson::private::base64::encode(&v).as_str())?; } } @@ -1671,6 +1679,7 @@ impl serde::Serialize for EncodedContent { } if !self.content.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("content", pbjson::private::base64::encode(&self.content).as_str())?; } struct_ser.end() @@ -1910,6 +1919,7 @@ impl serde::Serialize for EncryptedPrivateKeyBundleV1 { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.EncryptedPrivateKeyBundleV1", len)?; if !self.wallet_pre_key.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("walletPreKey", pbjson::private::base64::encode(&self.wallet_pre_key).as_str())?; } if let Some(v) = self.ciphertext.as_ref() { @@ -2031,6 +2041,7 @@ impl serde::Serialize for FrameAction { } if !self.action_body.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("actionBody", pbjson::private::base64::encode(&self.action_body).as_str())?; } struct_ser.end() @@ -2179,6 +2190,7 @@ impl serde::Serialize for FrameActionBody { } if self.timestamp != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("timestamp", ToString::to_string(&self.timestamp).as_str())?; } if !self.opaque_conversation_identifier.is_empty() { @@ -2538,6 +2550,7 @@ impl serde::Serialize for invitation_v1::Aes256gcmHkdfsha256 { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.InvitationV1.Aes256gcmHkdfsha256", len)?; if !self.key_material.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("keyMaterial", pbjson::private::base64::encode(&self.key_material).as_str())?; } struct_ser.end() @@ -2865,6 +2878,7 @@ impl serde::Serialize for MessageHeaderV1 { } if self.timestamp != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("timestamp", ToString::to_string(&self.timestamp).as_str())?; } struct_ser.end() @@ -2984,6 +2998,7 @@ impl serde::Serialize for MessageHeaderV2 { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.MessageHeaderV2", len)?; if self.created_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("createdNs", ToString::to_string(&self.created_ns).as_str())?; } if !self.topic.is_empty() { @@ -3096,6 +3111,7 @@ impl serde::Serialize for MessageV1 { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.MessageV1", len)?; if !self.header_bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("headerBytes", pbjson::private::base64::encode(&self.header_bytes).as_str())?; } if let Some(v) = self.ciphertext.as_ref() { @@ -3214,6 +3230,7 @@ impl serde::Serialize for MessageV2 { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.MessageV2", len)?; if !self.header_bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("headerBytes", pbjson::private::base64::encode(&self.header_bytes).as_str())?; } if let Some(v) = self.ciphertext.as_ref() { @@ -3221,6 +3238,7 @@ impl serde::Serialize for MessageV2 { } if let Some(v) = self.sender_hmac.as_ref() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("senderHmac", pbjson::private::base64::encode(&v).as_str())?; } if let Some(v) = self.should_push.as_ref() { @@ -3362,6 +3380,7 @@ impl serde::Serialize for PrivateKey { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.PrivateKey", len)?; if self.timestamp != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("timestamp", ToString::to_string(&self.timestamp).as_str())?; } if let Some(v) = self.public_key.as_ref() { @@ -3490,6 +3509,7 @@ impl serde::Serialize for private_key::Secp256k1 { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.PrivateKey.Secp256k1", len)?; if !self.bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("bytes", pbjson::private::base64::encode(&self.bytes).as_str())?; } struct_ser.end() @@ -4171,7 +4191,7 @@ impl serde::Serialize for private_preferences_action::AllowGroup { } let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.PrivatePreferencesAction.AllowGroup", len)?; if !self.group_ids.is_empty() { - struct_ser.serialize_field("groupIds", &self.group_ids.iter().map(pbjson::private::base64::encode).collect::>())?; + struct_ser.serialize_field("groupIds", &self.group_ids)?; } struct_ser.end() } @@ -4238,10 +4258,7 @@ impl<'de> serde::Deserialize<'de> for private_preferences_action::AllowGroup { if group_ids__.is_some() { return Err(serde::de::Error::duplicate_field("groupIds")); } - group_ids__ = - Some(map_.next_value::>>()? - .into_iter().map(|x| x.0).collect()) - ; + group_ids__ = Some(map_.next_value()?); } } } @@ -4450,7 +4467,7 @@ impl serde::Serialize for private_preferences_action::DenyGroup { } let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.PrivatePreferencesAction.DenyGroup", len)?; if !self.group_ids.is_empty() { - struct_ser.serialize_field("groupIds", &self.group_ids.iter().map(pbjson::private::base64::encode).collect::>())?; + struct_ser.serialize_field("groupIds", &self.group_ids)?; } struct_ser.end() } @@ -4517,10 +4534,7 @@ impl<'de> serde::Deserialize<'de> for private_preferences_action::DenyGroup { if group_ids__.is_some() { return Err(serde::de::Error::duplicate_field("groupIds")); } - group_ids__ = - Some(map_.next_value::>>()? - .into_iter().map(|x| x.0).collect()) - ; + group_ids__ = Some(map_.next_value()?); } } } @@ -4740,6 +4754,7 @@ impl serde::Serialize for PublicKey { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.PublicKey", len)?; if self.timestamp != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("timestamp", ToString::to_string(&self.timestamp).as_str())?; } if let Some(v) = self.signature.as_ref() { @@ -4868,6 +4883,7 @@ impl serde::Serialize for public_key::Secp256k1Uncompressed { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.PublicKey.Secp256k1Uncompressed", len)?; if !self.bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("bytes", pbjson::private::base64::encode(&self.bytes).as_str())?; } struct_ser.end() @@ -5180,6 +5196,7 @@ impl serde::Serialize for SealedInvitationHeaderV1 { } if self.created_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("createdNs", ToString::to_string(&self.created_ns).as_str())?; } struct_ser.end() @@ -5300,6 +5317,7 @@ impl serde::Serialize for SealedInvitationV1 { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.SealedInvitationV1", len)?; if !self.header_bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("headerBytes", pbjson::private::base64::encode(&self.header_bytes).as_str())?; } if let Some(v) = self.ciphertext.as_ref() { @@ -5523,6 +5541,7 @@ impl serde::Serialize for signature::EcdsaCompact { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.Signature.ECDSACompact", len)?; if !self.bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("bytes", pbjson::private::base64::encode(&self.bytes).as_str())?; } if self.recovery != 0 { @@ -5636,6 +5655,7 @@ impl serde::Serialize for signature::WalletEcdsaCompact { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.Signature.WalletECDSACompact", len)?; if !self.bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("bytes", pbjson::private::base64::encode(&self.bytes).as_str())?; } if self.recovery != 0 { @@ -5752,6 +5772,7 @@ impl serde::Serialize for SignedContent { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.SignedContent", len)?; 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())?; } if let Some(v) = self.sender.as_ref() { @@ -5877,6 +5898,7 @@ impl serde::Serialize for SignedEciesCiphertext { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.SignedEciesCiphertext", len)?; if !self.ecies_bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("eciesBytes", pbjson::private::base64::encode(&self.ecies_bytes).as_str())?; } if let Some(v) = self.signature.as_ref() { @@ -5995,18 +6017,22 @@ impl serde::Serialize for signed_ecies_ciphertext::Ecies { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.SignedEciesCiphertext.Ecies", len)?; if !self.ephemeral_public_key.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("ephemeralPublicKey", pbjson::private::base64::encode(&self.ephemeral_public_key).as_str())?; } if !self.iv.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("iv", pbjson::private::base64::encode(&self.iv).as_str())?; } if !self.mac.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("mac", pbjson::private::base64::encode(&self.mac).as_str())?; } 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())?; } struct_ser.end() @@ -6144,6 +6170,7 @@ impl serde::Serialize for SignedPayload { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.SignedPayload", len)?; 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())?; } if let Some(v) = self.signature.as_ref() { @@ -6258,6 +6285,7 @@ impl serde::Serialize for SignedPrivateKey { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.SignedPrivateKey", len)?; if self.created_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("createdNs", ToString::to_string(&self.created_ns).as_str())?; } if let Some(v) = self.public_key.as_ref() { @@ -6387,6 +6415,7 @@ impl serde::Serialize for signed_private_key::Secp256k1 { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.SignedPrivateKey.Secp256k1", len)?; if !self.bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("bytes", pbjson::private::base64::encode(&self.bytes).as_str())?; } struct_ser.end() @@ -6484,6 +6513,7 @@ impl serde::Serialize for SignedPublicKey { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.SignedPublicKey", len)?; if !self.key_bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("keyBytes", pbjson::private::base64::encode(&self.key_bytes).as_str())?; } if let Some(v) = self.signature.as_ref() { @@ -6706,6 +6736,7 @@ impl serde::Serialize for UnsignedPublicKey { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.UnsignedPublicKey", len)?; if self.created_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("createdNs", ToString::to_string(&self.created_ns).as_str())?; } if let Some(v) = self.union.as_ref() { @@ -6821,6 +6852,7 @@ impl serde::Serialize for unsigned_public_key::Secp256k1Uncompressed { let mut struct_ser = serializer.serialize_struct("xmtp.message_contents.UnsignedPublicKey.Secp256k1Uncompressed", len)?; if !self.bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("bytes", pbjson::private::base64::encode(&self.bytes).as_str())?; } struct_ser.end() diff --git a/xmtp_proto/src/gen/xmtp.mls.api.v1.rs b/xmtp_proto/src/gen/xmtp.mls.api.v1.rs index b22c16f11..aaee1da0d 100644 --- a/xmtp_proto/src/gen/xmtp.mls.api.v1.rs +++ b/xmtp_proto/src/gen/xmtp.mls.api.v1.rs @@ -1,4 +1,5 @@ // @generated +// This file is @generated by prost-build. /// Full representation of a welcome message #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -101,6 +102,7 @@ pub mod group_message_input { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct V1 { + /// Serialized MlsProtocolMessage #[prost(bytes="vec", tag="1")] pub data: ::prost::alloc::vec::Vec, #[prost(bytes="vec", tag="2")] @@ -139,23 +141,6 @@ pub struct KeyPackageUpload { #[prost(bytes="vec", tag="1")] pub key_package_tls_serialized: ::prost::alloc::vec::Vec, } -/// Register a new installation -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct RegisterInstallationRequest { - /// The Key Package contains all information needed to register an installation - #[prost(message, optional, tag="1")] - pub key_package: ::core::option::Option, - #[prost(bool, tag="2")] - pub is_inbox_id_credential: bool, -} -/// The response to a RegisterInstallationRequest -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct RegisterInstallationResponse { - #[prost(bytes="vec", tag="1")] - pub installation_key: ::prost::alloc::vec::Vec, -} /// Upload a new key packages #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -196,84 +181,9 @@ pub mod fetch_key_packages_response { pub key_package_tls_serialized: ::prost::alloc::vec::Vec, } } -/// Revoke an installation -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct RevokeInstallationRequest { - #[prost(bytes="vec", tag="1")] - pub installation_key: ::prost::alloc::vec::Vec, - /// All revocations must be validated with a wallet signature over the - /// installation_id being revoked (and some sort of standard prologue) - #[prost(message, optional, tag="2")] - pub wallet_signature: ::core::option::Option, -} -/// Get all updates for an identity since the specified time -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetIdentityUpdatesRequest { - #[prost(string, repeated, tag="1")] - pub account_addresses: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - #[prost(uint64, tag="2")] - pub start_time_ns: u64, -} -/// Used to get any new or revoked installations for a list of wallet addresses -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetIdentityUpdatesResponse { - /// A list of updates (or empty objects if no changes) in the original order - /// of the request - #[prost(message, repeated, tag="1")] - pub updates: ::prost::alloc::vec::Vec, -} -/// Nested message and enum types in `GetIdentityUpdatesResponse`. -pub mod get_identity_updates_response { - /// A new installation key was seen for the first time by the nodes - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct NewInstallationUpdate { - #[prost(bytes="vec", tag="1")] - pub installation_key: ::prost::alloc::vec::Vec, - #[prost(bytes="vec", tag="2")] - pub credential_identity: ::prost::alloc::vec::Vec, - } - /// An installation was revoked - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct RevokedInstallationUpdate { - #[prost(bytes="vec", tag="1")] - pub installation_key: ::prost::alloc::vec::Vec, - } - /// A wrapper for any update to the wallet - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct Update { - #[prost(uint64, tag="1")] - pub timestamp_ns: u64, - #[prost(oneof="update::Kind", tags="2, 3")] - pub kind: ::core::option::Option, - } - /// Nested message and enum types in `Update`. - pub mod update { - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Kind { - #[prost(message, tag="2")] - NewInstallation(super::NewInstallationUpdate), - #[prost(message, tag="3")] - RevokedInstallation(super::RevokedInstallationUpdate), - } - } - /// A wrapper for the updates for a single wallet - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct WalletUpdates { - #[prost(message, repeated, tag="1")] - pub updates: ::prost::alloc::vec::Vec, - } -} /// Pagination config for queries #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct PagingInfo { #[prost(enumeration="SortDirection", tag="1")] pub direction: i32, @@ -388,987 +298,707 @@ impl SortDirection { } /// Encoded file descriptor set for the `xmtp.mls.api.v1` package pub const FILE_DESCRIPTOR_SET: &[u8] = &[ - 0x0a, 0xc0, 0x7a, 0x0a, 0x14, 0x6d, 0x6c, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, + 0x0a, 0xc3, 0x57, 0x0a, 0x14, 0x6d, 0x6c, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6c, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, - 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xee, 0x01, 0x0a, 0x0e, 0x57, 0x65, 0x6c, 0x63, - 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x34, 0x0a, 0x02, 0x76, 0x31, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, - 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x56, 0x31, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, - 0x1a, 0x9a, 0x01, 0x0a, 0x02, 0x56, 0x31, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x64, 0x4e, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, - 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x26, 0x0a, 0x0f, 0x68, 0x70, 0x6b, 0x65, 0x5f, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, - 0x68, 0x70, 0x6b, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x09, 0x0a, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xc8, 0x01, 0x0a, 0x13, 0x57, 0x65, 0x6c, - 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x12, 0x39, 0x0a, 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, - 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, - 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, - 0x75, 0x74, 0x2e, 0x56, 0x31, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x1a, 0x6b, 0x0a, 0x02, 0x56, - 0x31, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x26, 0x0a, 0x0f, 0x68, 0x70, 0x6b, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x68, 0x70, 0x6b, 0x65, 0x50, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x09, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x22, 0xd3, 0x01, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x32, 0x0a, 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x20, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, - 0x56, 0x31, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x1a, 0x83, 0x01, 0x0a, 0x02, 0x56, 0x31, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x4e, 0x73, 0x12, 0x19, - 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, - 0x0b, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x48, 0x6d, 0x61, 0x63, 0x42, 0x09, - 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x92, 0x01, 0x0a, 0x11, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, - 0x37, 0x0a, 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, - 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x2e, - 0x56, 0x31, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x1a, 0x39, 0x0a, 0x02, 0x56, 0x31, 0x12, 0x12, - 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, - 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x6d, 0x61, - 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x48, - 0x6d, 0x61, 0x63, 0x42, 0x09, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5a, - 0x0a, 0x18, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3e, 0x0a, 0x08, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, - 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0x5e, 0x0a, 0x1a, 0x53, 0x65, - 0x6e, 0x64, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, + 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xee, 0x01, 0x0a, 0x0e, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, + 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x34, 0x0a, 0x02, 0x76, 0x31, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x56, 0x31, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x1a, 0x9a, + 0x01, 0x0a, 0x02, 0x56, 0x31, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x64, 0x4e, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, + 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x12, 0x26, 0x0a, 0x0f, 0x68, 0x70, 0x6b, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x68, 0x70, + 0x6b, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x09, 0x0a, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xc8, 0x01, 0x0a, 0x13, 0x57, 0x65, 0x6c, 0x63, 0x6f, + 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x39, + 0x0a, 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0x4f, 0x0a, 0x10, 0x4b, 0x65, - 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x3b, - 0x0a, 0x1a, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6c, - 0x73, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x17, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x54, 0x6c, - 0x73, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x22, 0x96, 0x01, 0x0a, 0x1b, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0b, 0x6b, - 0x65, 0x79, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x21, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x55, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x52, 0x0a, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, - 0x33, 0x0a, 0x16, 0x69, 0x73, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x5f, 0x63, - 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x13, 0x69, 0x73, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x61, 0x6c, 0x22, 0x49, 0x0a, 0x1c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x22, - 0x92, 0x01, 0x0a, 0x17, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, - 0x6b, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0b, 0x6b, - 0x65, 0x79, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x21, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x55, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x52, 0x0a, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, - 0x33, 0x0a, 0x16, 0x69, 0x73, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x5f, 0x63, - 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x13, 0x69, 0x73, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x61, 0x6c, 0x22, 0x46, 0x0a, 0x17, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4b, 0x65, 0x79, - 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x10, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x22, 0xbe, 0x01, 0x0a, - 0x18, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0c, 0x6b, 0x65, 0x79, - 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x34, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4b, 0x65, 0x79, 0x50, 0x61, - 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x0b, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, - 0x65, 0x73, 0x1a, 0x49, 0x0a, 0x0a, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, - 0x12, 0x3b, 0x0a, 0x1a, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, - 0x74, 0x6c, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x17, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, - 0x54, 0x6c, 0x73, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x22, 0x93, 0x01, - 0x0a, 0x19, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x4b, 0x0a, 0x10, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x20, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x52, 0x0f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x22, 0x6c, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x61, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x22, 0x0a, - 0x0d, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x4e, - 0x73, 0x22, 0xaf, 0x05, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x53, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x39, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x57, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x07, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x73, 0x1a, 0x73, 0x0a, 0x15, 0x4e, 0x65, 0x77, 0x49, 0x6e, 0x73, 0x74, - 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x29, - 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x61, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x1a, 0x46, 0x0a, 0x19, 0x52, 0x65, - 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, - 0x65, 0x79, 0x1a, 0x9f, 0x02, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, - 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6e, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4e, 0x73, - 0x12, 0x6e, 0x0a, 0x10, 0x6e, 0x65, 0x77, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x78, 0x6d, 0x74, - 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x65, 0x77, 0x49, 0x6e, 0x73, 0x74, 0x61, - 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, - 0x0f, 0x6e, 0x65, 0x77, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x7a, 0x0a, 0x14, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x45, + 0x2e, 0x56, 0x31, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x1a, 0x6b, 0x0a, 0x02, 0x56, 0x31, 0x12, + 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x26, + 0x0a, 0x0f, 0x68, 0x70, 0x6b, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x68, 0x70, 0x6b, 0x65, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x09, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x22, 0xd3, 0x01, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x32, 0x0a, 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x76, 0x6f, - 0x6b, 0x65, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x13, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x06, 0x0a, 0x04, - 0x6b, 0x69, 0x6e, 0x64, 0x1a, 0x5d, 0x0a, 0x0d, 0x57, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, - 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x73, 0x22, 0x7d, 0x0a, 0x0a, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x3c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x72, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x64, 0x5f, 0x63, 0x75, 0x72, 0x73, - 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x69, 0x64, 0x43, 0x75, 0x72, 0x73, - 0x6f, 0x72, 0x22, 0x74, 0x0a, 0x19, 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x0b, 0x70, 0x61, - 0x67, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x70, 0x61, - 0x67, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x95, 0x01, 0x0a, 0x1a, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x78, 0x6d, 0x74, 0x70, + 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x56, 0x31, + 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x1a, 0x83, 0x01, 0x0a, 0x02, 0x56, 0x31, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, + 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x4e, 0x73, 0x12, 0x19, 0x0a, 0x08, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x73, + 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x48, 0x6d, 0x61, 0x63, 0x42, 0x09, 0x0a, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x92, 0x01, 0x0a, 0x11, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x37, 0x0a, + 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0b, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x66, - 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, - 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x67, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, - 0x22, 0x86, 0x01, 0x0a, 0x1b, 0x51, 0x75, 0x65, 0x72, 0x79, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, - 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x3c, 0x0a, 0x0b, 0x70, + 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x2e, 0x56, 0x31, + 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x1a, 0x39, 0x0a, 0x02, 0x56, 0x31, 0x12, 0x12, 0x0a, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x6d, 0x61, 0x63, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x48, 0x6d, 0x61, + 0x63, 0x42, 0x09, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5a, 0x0a, 0x18, + 0x53, 0x65, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3e, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x08, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0x5e, 0x0a, 0x1a, 0x53, 0x65, 0x6e, 0x64, + 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, + 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x65, 0x6c, 0x63, 0x6f, + 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x08, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0x4f, 0x0a, 0x10, 0x4b, 0x65, 0x79, 0x50, + 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x3b, 0x0a, 0x1a, + 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6c, 0x73, 0x5f, + 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x17, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x54, 0x6c, 0x73, 0x53, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x22, 0x92, 0x01, 0x0a, 0x17, 0x55, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0b, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x63, + 0x6b, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, + 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0a, 0x6b, + 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x33, 0x0a, 0x16, 0x69, 0x73, 0x5f, + 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x73, 0x49, 0x6e, 0x62, + 0x6f, 0x78, 0x49, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x22, 0x46, + 0x0a, 0x17, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0c, 0x52, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x73, 0x22, 0xbe, 0x01, 0x0a, 0x18, 0x46, 0x65, 0x74, 0x63, 0x68, + 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0c, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, + 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x78, 0x6d, 0x74, 0x70, + 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x65, 0x74, 0x63, + 0x68, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, + 0x0b, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x1a, 0x49, 0x0a, 0x0a, + 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x3b, 0x0a, 0x1a, 0x6b, 0x65, + 0x79, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x73, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x17, + 0x6b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x54, 0x6c, 0x73, 0x53, 0x65, 0x72, + 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x22, 0x7d, 0x0a, 0x0a, 0x50, 0x61, 0x67, 0x69, 0x6e, + 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, + 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x72, 0x74, 0x44, + 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x64, 0x5f, + 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x69, 0x64, + 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0x74, 0x0a, 0x19, 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x3c, + 0x0a, 0x0b, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x95, 0x01, 0x0a, + 0x1a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, + 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0b, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x67, + 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x6d, + 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, + 0x67, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x67, + 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x86, 0x01, 0x0a, 0x1b, 0x51, 0x75, 0x65, 0x72, 0x79, 0x57, 0x65, + 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, + 0x3c, 0x0a, 0x0b, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x99, 0x01, + 0x0a, 0x1c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, + 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1f, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0b, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x70, - 0x61, 0x67, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x99, 0x01, 0x0a, 0x1c, 0x51, 0x75, - 0x65, 0x72, 0x79, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, - 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, - 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0b, 0x70, 0x61, 0x67, 0x69, 0x6e, - 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, - 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x50, - 0x61, 0x67, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, - 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xb2, 0x01, 0x0a, 0x1d, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x62, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, - 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, - 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x1a, 0x40, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x1b, 0x0a, - 0x09, 0x69, 0x64, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x08, 0x69, 0x64, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0xc6, 0x01, 0x0a, 0x1f, 0x53, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x51, - 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x37, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x57, 0x65, 0x6c, 0x63, 0x6f, - 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, - 0x73, 0x1a, 0x50, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x10, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x64, 0x5f, 0x63, 0x75, 0x72, - 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x69, 0x64, 0x43, 0x75, 0x72, - 0x73, 0x6f, 0x72, 0x2a, 0x6c, 0x0a, 0x0d, 0x53, 0x6f, 0x72, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x44, 0x49, 0x52, - 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x44, 0x49, 0x52, - 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x53, 0x43, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, - 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, - 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x53, 0x43, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, - 0x02, 0x32, 0xcd, 0x0c, 0x0a, 0x06, 0x4d, 0x6c, 0x73, 0x41, 0x70, 0x69, 0x12, 0x7e, 0x0a, 0x11, - 0x53, 0x65, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x73, 0x12, 0x29, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x3a, 0x01, 0x2a, 0x22, - 0x1b, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x6e, 0x64, 0x2d, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x84, 0x01, 0x0a, - 0x13, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x73, 0x12, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x6c, 0x63, 0x6f, - 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x22, 0x3a, 0x01, 0x2a, 0x22, 0x1d, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, - 0x6e, 0x64, 0x2d, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x73, 0x12, 0x9d, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x2e, 0x78, - 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x78, 0x6d, 0x74, - 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x22, 0x3a, 0x01, 0x2a, 0x22, 0x1d, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x2d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x7b, 0x0a, 0x10, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x65, 0x79, - 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x28, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, - 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, - 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1f, 0x3a, 0x01, 0x2a, 0x22, 0x1a, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x70, - 0x6c, 0x6f, 0x61, 0x64, 0x2d, 0x6b, 0x65, 0x79, 0x2d, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, - 0x12, 0x8e, 0x01, 0x0a, 0x10, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, - 0x6b, 0x61, 0x67, 0x65, 0x73, 0x12, 0x28, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4b, 0x65, 0x79, - 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x29, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x1f, 0x3a, 0x01, 0x2a, 0x22, 0x1a, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x66, - 0x65, 0x74, 0x63, 0x68, 0x2d, 0x6b, 0x65, 0x79, 0x2d, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, - 0x73, 0x12, 0x80, 0x01, 0x0a, 0x12, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x49, 0x6e, 0x73, 0x74, - 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, - 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, - 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x26, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x20, 0x3a, 0x01, 0x2a, 0x22, 0x1b, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, 0x31, - 0x2f, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x2d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x96, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x49, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x2a, 0x2e, 0x78, 0x6d, - 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, - 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x27, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x3a, 0x01, 0x2a, 0x22, - 0x1c, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x2d, 0x69, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2d, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x96, 0x01, - 0x0a, 0x12, 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x73, 0x12, 0x2a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, 0x72, 0x6f, 0x75, + 0x61, 0x67, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xb2, 0x01, 0x0a, 0x1d, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x07, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x1a, 0x40, 0x0a, 0x06, + 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, + 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x64, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x69, 0x64, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0xc6, + 0x01, 0x0a, 0x1f, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x57, 0x65, 0x6c, 0x63, + 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x51, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x57, + 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x07, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x73, 0x1a, 0x50, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, + 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x64, + 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x69, + 0x64, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x2a, 0x6c, 0x0a, 0x0d, 0x53, 0x6f, 0x72, 0x74, 0x44, + 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x4f, 0x52, 0x54, + 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, + 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x4f, 0x52, 0x54, + 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x53, 0x43, 0x45, 0x4e, + 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x44, + 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x53, 0x43, 0x45, 0x4e, 0x44, + 0x49, 0x4e, 0x47, 0x10, 0x02, 0x32, 0x91, 0x09, 0x0a, 0x06, 0x4d, 0x6c, 0x73, 0x41, 0x70, 0x69, + 0x12, 0x7e, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x29, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x27, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x3a, 0x01, 0x2a, 0x22, 0x1c, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, - 0x31, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2d, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x9e, 0x01, 0x0a, 0x14, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, - 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x23, 0x3a, 0x01, 0x2a, 0x22, 0x1e, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, 0x31, - 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2d, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x2d, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x96, 0x01, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x73, 0x12, 0x2e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x3a, 0x01, 0x2a, 0x22, 0x20, 0x2f, 0x6d, - 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x2d, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x30, 0x01, - 0x12, 0x9e, 0x01, 0x0a, 0x18, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x57, 0x65, - 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x30, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1f, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x22, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, 0x3a, 0x01, 0x2a, 0x22, 0x22, 0x2f, 0x6d, 0x6c, - 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x2d, 0x77, - 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x30, - 0x01, 0x42, 0xb8, 0x01, 0x92, 0x41, 0x0f, 0x12, 0x0d, 0x0a, 0x06, 0x4d, 0x6c, 0x73, 0x41, 0x70, - 0x69, 0x32, 0x03, 0x31, 0x2e, 0x30, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, - 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x08, 0x4d, 0x6c, 0x73, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, - 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0xa2, - 0x02, 0x03, 0x58, 0x4d, 0x41, 0xaa, 0x02, 0x0f, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x6c, 0x73, - 0x2e, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0f, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, - 0x6c, 0x73, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x1b, 0x58, 0x6d, 0x74, 0x70, - 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x12, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, - 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x4a, 0xec, 0x4c, 0x0a, - 0x07, 0x12, 0x05, 0x01, 0x00, 0xce, 0x02, 0x01, 0x0a, 0x17, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, - 0x00, 0x12, 0x1a, 0x0d, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x41, 0x50, 0x49, - 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x02, 0x00, 0x18, 0x0a, 0x09, 0x0a, 0x02, 0x03, - 0x00, 0x12, 0x03, 0x04, 0x00, 0x26, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x01, 0x12, 0x03, 0x05, 0x00, - 0x25, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x02, 0x12, 0x03, 0x06, 0x00, 0x2a, 0x0a, 0x09, 0x0a, 0x02, - 0x03, 0x03, 0x12, 0x03, 0x07, 0x00, 0x38, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x09, 0x00, - 0x3d, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x09, 0x00, 0x3d, 0x0a, 0x09, 0x0a, 0x01, - 0x08, 0x12, 0x04, 0x0b, 0x00, 0x10, 0x02, 0x0a, 0x0b, 0x0a, 0x03, 0x08, 0x92, 0x08, 0x12, 0x04, - 0x0b, 0x00, 0x10, 0x02, 0x0a, 0x0c, 0x0a, 0x04, 0x08, 0x92, 0x08, 0x02, 0x12, 0x04, 0x0c, 0x02, - 0x0f, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x08, 0x92, 0x08, 0x02, 0x01, 0x12, 0x03, 0x0d, 0x04, 0x13, - 0x0a, 0x0c, 0x0a, 0x05, 0x08, 0x92, 0x08, 0x02, 0x06, 0x12, 0x03, 0x0e, 0x04, 0x12, 0x0a, 0x26, - 0x0a, 0x02, 0x06, 0x00, 0x12, 0x04, 0x13, 0x00, 0x6f, 0x01, 0x1a, 0x1a, 0x20, 0x52, 0x50, 0x43, - 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x4d, 0x4c, - 0x53, 0x20, 0x41, 0x50, 0x49, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x06, 0x00, 0x01, 0x12, 0x03, 0x13, - 0x08, 0x0e, 0x0a, 0x5f, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x00, 0x12, 0x04, 0x16, 0x02, 0x1b, 0x03, - 0x1a, 0x51, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x4d, 0x4c, 0x53, 0x20, 0x70, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2c, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x6f, 0x75, 0x6c, - 0x64, 0x20, 0x62, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x62, - 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x6f, 0x72, - 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x6e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x16, 0x06, - 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x16, 0x18, 0x30, 0x0a, - 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x16, 0x3b, 0x50, 0x0a, 0x0d, 0x0a, - 0x05, 0x06, 0x00, 0x02, 0x00, 0x04, 0x12, 0x04, 0x17, 0x04, 0x1a, 0x06, 0x0a, 0x11, 0x0a, 0x09, - 0x06, 0x00, 0x02, 0x00, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x04, 0x17, 0x04, 0x1a, 0x06, 0x0a, - 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x00, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x04, 0x12, 0x03, 0x18, - 0x06, 0x29, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x00, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x07, - 0x12, 0x03, 0x19, 0x06, 0x0f, 0x0a, 0x30, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x01, 0x12, 0x04, 0x1e, - 0x02, 0x23, 0x03, 0x1a, 0x22, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, - 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x01, - 0x12, 0x03, 0x1e, 0x06, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, - 0x1e, 0x1a, 0x34, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x1e, 0x3f, - 0x54, 0x0a, 0x0d, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x04, 0x12, 0x04, 0x1f, 0x04, 0x22, 0x06, - 0x0a, 0x11, 0x0a, 0x09, 0x06, 0x00, 0x02, 0x01, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x04, 0x1f, - 0x04, 0x22, 0x06, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x01, 0x04, 0xb0, 0xca, 0xbc, 0x22, - 0x04, 0x12, 0x03, 0x20, 0x06, 0x2b, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x01, 0x04, 0xb0, - 0xca, 0xbc, 0x22, 0x07, 0x12, 0x03, 0x21, 0x06, 0x0f, 0x0a, 0x54, 0x0a, 0x04, 0x06, 0x00, 0x02, - 0x02, 0x12, 0x04, 0x26, 0x02, 0x2b, 0x03, 0x1a, 0x46, 0x20, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x77, 0x6f, 0x75, - 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, - 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x26, 0x06, 0x1a, 0x0a, 0x0c, 0x0a, - 0x05, 0x06, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x26, 0x1b, 0x36, 0x0a, 0x0c, 0x0a, 0x05, 0x06, - 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x26, 0x41, 0x5d, 0x0a, 0x0d, 0x0a, 0x05, 0x06, 0x00, 0x02, - 0x02, 0x04, 0x12, 0x04, 0x27, 0x04, 0x2a, 0x06, 0x0a, 0x11, 0x0a, 0x09, 0x06, 0x00, 0x02, 0x02, - 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x04, 0x27, 0x04, 0x2a, 0x06, 0x0a, 0x11, 0x0a, 0x0a, 0x06, - 0x00, 0x02, 0x02, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x04, 0x12, 0x03, 0x28, 0x06, 0x2b, 0x0a, 0x11, - 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x02, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x07, 0x12, 0x03, 0x29, 0x06, - 0x0f, 0x0a, 0x50, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x03, 0x12, 0x04, 0x2e, 0x02, 0x33, 0x03, 0x1a, - 0x42, 0x20, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x4b, - 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, - 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x20, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x61, - 0x67, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x2e, 0x06, - 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x03, 0x02, 0x12, 0x03, 0x2e, 0x17, 0x2e, 0x0a, - 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x03, 0x03, 0x12, 0x03, 0x2e, 0x39, 0x4e, 0x0a, 0x0d, 0x0a, - 0x05, 0x06, 0x00, 0x02, 0x03, 0x04, 0x12, 0x04, 0x2f, 0x04, 0x32, 0x06, 0x0a, 0x11, 0x0a, 0x09, - 0x06, 0x00, 0x02, 0x03, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x04, 0x2f, 0x04, 0x32, 0x06, 0x0a, - 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x03, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x04, 0x12, 0x03, 0x30, - 0x06, 0x28, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x03, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x07, - 0x12, 0x03, 0x31, 0x06, 0x0f, 0x0a, 0x3f, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x04, 0x12, 0x04, 0x36, - 0x02, 0x3b, 0x03, 0x1a, 0x31, 0x20, 0x47, 0x65, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x72, - 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, - 0x65, 0x73, 0x20, 0x62, 0x79, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x01, 0x12, - 0x03, 0x36, 0x06, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x02, 0x12, 0x03, 0x36, - 0x17, 0x2e, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x03, 0x12, 0x03, 0x36, 0x39, 0x51, - 0x0a, 0x0d, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x04, 0x12, 0x04, 0x37, 0x04, 0x3a, 0x06, 0x0a, - 0x11, 0x0a, 0x09, 0x06, 0x00, 0x02, 0x04, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x04, 0x37, 0x04, - 0x3a, 0x06, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x04, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x04, - 0x12, 0x03, 0x38, 0x06, 0x28, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x04, 0x04, 0xb0, 0xca, - 0xbc, 0x22, 0x07, 0x12, 0x03, 0x39, 0x06, 0x0f, 0x0a, 0x80, 0x01, 0x0a, 0x04, 0x06, 0x00, 0x02, - 0x05, 0x12, 0x04, 0x3f, 0x02, 0x44, 0x03, 0x1a, 0x72, 0x20, 0x57, 0x6f, 0x75, 0x6c, 0x64, 0x20, - 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, - 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, - 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x61, 0x72, - 0x6b, 0x0a, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x68, 0x61, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x62, 0x65, - 0x65, 0x6e, 0x20, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, - 0x00, 0x02, 0x05, 0x01, 0x12, 0x03, 0x3f, 0x06, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, - 0x05, 0x02, 0x12, 0x03, 0x3f, 0x19, 0x32, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x05, 0x03, - 0x12, 0x03, 0x3f, 0x3d, 0x52, 0x0a, 0x0d, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x05, 0x04, 0x12, 0x04, - 0x40, 0x04, 0x43, 0x06, 0x0a, 0x11, 0x0a, 0x09, 0x06, 0x00, 0x02, 0x05, 0x04, 0xb0, 0xca, 0xbc, - 0x22, 0x12, 0x04, 0x40, 0x04, 0x43, 0x06, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x05, 0x04, - 0xb0, 0xca, 0xbc, 0x22, 0x04, 0x12, 0x03, 0x41, 0x06, 0x29, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, - 0x02, 0x05, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x07, 0x12, 0x03, 0x42, 0x06, 0x0f, 0x0a, 0xc6, 0x01, - 0x0a, 0x04, 0x06, 0x00, 0x02, 0x06, 0x12, 0x04, 0x49, 0x02, 0x4e, 0x03, 0x1a, 0xb7, 0x01, 0x20, - 0x55, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x66, 0x6f, - 0x72, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x65, - 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, 0x6f, 0x66, 0x20, - 0x61, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x0a, 0x20, 0x57, 0x6f, 0x75, 0x6c, 0x64, 0x20, - 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, - 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, - 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x0a, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2c, 0x20, 0x61, 0x6e, - 0x64, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x72, 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x68, 0x61, 0x70, 0x70, - 0x65, 0x6e, 0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x06, 0x01, 0x12, - 0x03, 0x49, 0x06, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x06, 0x02, 0x12, 0x03, 0x49, - 0x19, 0x32, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x06, 0x03, 0x12, 0x03, 0x49, 0x3d, 0x57, - 0x0a, 0x0d, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x06, 0x04, 0x12, 0x04, 0x4a, 0x04, 0x4d, 0x06, 0x0a, - 0x11, 0x0a, 0x09, 0x06, 0x00, 0x02, 0x06, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x04, 0x4a, 0x04, - 0x4d, 0x06, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x06, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x04, - 0x12, 0x03, 0x4b, 0x06, 0x2a, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x06, 0x04, 0xb0, 0xca, - 0xbc, 0x22, 0x07, 0x12, 0x03, 0x4c, 0x06, 0x0f, 0x0a, 0x2b, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x07, - 0x12, 0x04, 0x51, 0x02, 0x56, 0x03, 0x1a, 0x1d, 0x20, 0x51, 0x75, 0x65, 0x72, 0x79, 0x20, 0x73, - 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x07, 0x01, 0x12, 0x03, - 0x51, 0x06, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x07, 0x02, 0x12, 0x03, 0x51, 0x19, - 0x32, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x07, 0x03, 0x12, 0x03, 0x51, 0x3d, 0x57, 0x0a, - 0x0d, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x07, 0x04, 0x12, 0x04, 0x52, 0x04, 0x55, 0x06, 0x0a, 0x11, - 0x0a, 0x09, 0x06, 0x00, 0x02, 0x07, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x04, 0x52, 0x04, 0x55, - 0x06, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x07, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x04, 0x12, - 0x03, 0x53, 0x06, 0x2a, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x07, 0x04, 0xb0, 0xca, 0xbc, - 0x22, 0x07, 0x12, 0x03, 0x54, 0x06, 0x0f, 0x0a, 0x2b, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x08, 0x12, - 0x04, 0x59, 0x02, 0x5e, 0x03, 0x1a, 0x1d, 0x20, 0x51, 0x75, 0x65, 0x72, 0x79, 0x20, 0x73, 0x74, - 0x6f, 0x72, 0x65, 0x64, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x08, 0x01, 0x12, 0x03, 0x59, - 0x06, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x08, 0x02, 0x12, 0x03, 0x59, 0x1b, 0x36, - 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x08, 0x03, 0x12, 0x03, 0x59, 0x41, 0x5d, 0x0a, 0x0d, - 0x0a, 0x05, 0x06, 0x00, 0x02, 0x08, 0x04, 0x12, 0x04, 0x5a, 0x04, 0x5d, 0x06, 0x0a, 0x11, 0x0a, - 0x09, 0x06, 0x00, 0x02, 0x08, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x04, 0x5a, 0x04, 0x5d, 0x06, - 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x08, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x04, 0x12, 0x03, - 0x5b, 0x06, 0x2c, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x08, 0x04, 0xb0, 0xca, 0xbc, 0x22, - 0x07, 0x12, 0x03, 0x5c, 0x06, 0x0f, 0x0a, 0x36, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x09, 0x12, 0x04, - 0x61, 0x02, 0x66, 0x03, 0x1a, 0x28, 0x20, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, - 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x20, 0x6f, 0x66, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0c, - 0x0a, 0x05, 0x06, 0x00, 0x02, 0x09, 0x01, 0x12, 0x03, 0x61, 0x06, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, - 0x06, 0x00, 0x02, 0x09, 0x02, 0x12, 0x03, 0x61, 0x1d, 0x3a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, - 0x02, 0x09, 0x06, 0x12, 0x03, 0x61, 0x45, 0x4b, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x09, - 0x03, 0x12, 0x03, 0x61, 0x4c, 0x58, 0x0a, 0x0d, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x09, 0x04, 0x12, - 0x04, 0x62, 0x04, 0x65, 0x06, 0x0a, 0x11, 0x0a, 0x09, 0x06, 0x00, 0x02, 0x09, 0x04, 0xb0, 0xca, - 0xbc, 0x22, 0x12, 0x04, 0x62, 0x04, 0x65, 0x06, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x09, - 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x04, 0x12, 0x03, 0x63, 0x06, 0x2e, 0x0a, 0x11, 0x0a, 0x0a, 0x06, - 0x00, 0x02, 0x09, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x07, 0x12, 0x03, 0x64, 0x06, 0x0f, 0x0a, 0x38, - 0x0a, 0x04, 0x06, 0x00, 0x02, 0x0a, 0x12, 0x04, 0x69, 0x02, 0x6e, 0x03, 0x1a, 0x2a, 0x20, 0x53, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x20, - 0x6f, 0x66, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x0a, - 0x01, 0x12, 0x03, 0x69, 0x06, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x0a, 0x02, 0x12, - 0x03, 0x69, 0x1f, 0x3e, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x0a, 0x06, 0x12, 0x03, 0x69, - 0x49, 0x4f, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x0a, 0x03, 0x12, 0x03, 0x69, 0x50, 0x5e, - 0x0a, 0x0d, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x0a, 0x04, 0x12, 0x04, 0x6a, 0x04, 0x6d, 0x06, 0x0a, - 0x11, 0x0a, 0x09, 0x06, 0x00, 0x02, 0x0a, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x04, 0x6a, 0x04, - 0x6d, 0x06, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x0a, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x04, - 0x12, 0x03, 0x6b, 0x06, 0x30, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x0a, 0x04, 0xb0, 0xca, - 0xbc, 0x22, 0x07, 0x12, 0x03, 0x6c, 0x06, 0x0f, 0x0a, 0x36, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, - 0x72, 0x00, 0x7f, 0x01, 0x1a, 0x2a, 0x20, 0x46, 0x75, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x70, 0x72, - 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, - 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, - 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x72, 0x08, 0x16, 0x0a, 0x36, 0x0a, 0x04, - 0x04, 0x00, 0x03, 0x00, 0x12, 0x04, 0x74, 0x02, 0x7a, 0x03, 0x1a, 0x28, 0x20, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x57, 0x65, - 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, 0x00, 0x01, 0x12, 0x03, 0x74, - 0x0a, 0x0c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x75, 0x04, - 0x12, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x75, 0x04, - 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x75, 0x0b, - 0x0d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x75, 0x10, - 0x11, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x76, 0x04, 0x1a, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x76, 0x04, 0x0a, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x76, 0x0b, 0x15, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x76, 0x18, 0x19, - 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x02, 0x12, 0x03, 0x77, 0x04, 0x1f, 0x0a, - 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x77, 0x04, 0x09, 0x0a, - 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x77, 0x0a, 0x1a, 0x0a, - 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x77, 0x1d, 0x1e, 0x0a, - 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x03, 0x12, 0x03, 0x78, 0x04, 0x13, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x03, 0x05, 0x12, 0x03, 0x78, 0x04, 0x09, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x78, 0x0a, 0x0e, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x03, 0x03, 0x12, 0x03, 0x78, 0x11, 0x12, 0x0a, 0x0d, - 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x04, 0x12, 0x03, 0x79, 0x04, 0x1e, 0x0a, 0x0e, 0x0a, - 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x04, 0x05, 0x12, 0x03, 0x79, 0x04, 0x09, 0x0a, 0x0e, 0x0a, - 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x04, 0x01, 0x12, 0x03, 0x79, 0x0a, 0x19, 0x0a, 0x0e, 0x0a, - 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x04, 0x03, 0x12, 0x03, 0x79, 0x1c, 0x1d, 0x0a, 0x0c, 0x0a, - 0x04, 0x04, 0x00, 0x08, 0x00, 0x12, 0x04, 0x7c, 0x02, 0x7e, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x00, 0x08, 0x00, 0x01, 0x12, 0x03, 0x7c, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, - 0x00, 0x12, 0x03, 0x7d, 0x04, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x06, 0x12, - 0x03, 0x7d, 0x04, 0x06, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x7d, - 0x07, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x7d, 0x0c, 0x0d, - 0x0a, 0x30, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x06, 0x82, 0x01, 0x00, 0x8d, 0x01, 0x01, 0x1a, 0x22, - 0x20, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, - 0x61, 0x20, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x04, 0x82, 0x01, 0x08, 0x1b, 0x0a, - 0x3d, 0x0a, 0x04, 0x04, 0x01, 0x03, 0x00, 0x12, 0x06, 0x84, 0x01, 0x02, 0x88, 0x01, 0x03, 0x1a, + 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, + 0x3a, 0x01, 0x2a, 0x22, 0x1b, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x6e, + 0x64, 0x2d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, + 0x12, 0x84, 0x01, 0x0a, 0x13, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, + 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, + 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x28, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x3a, 0x01, 0x2a, 0x22, 0x1d, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, + 0x31, 0x2f, 0x73, 0x65, 0x6e, 0x64, 0x2d, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x2d, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x7b, 0x0a, 0x10, 0x55, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x28, 0x2e, 0x78, 0x6d, + 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x25, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x3a, 0x01, 0x2a, 0x22, 0x1a, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, + 0x31, 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x2d, 0x6b, 0x65, 0x79, 0x2d, 0x70, 0x61, 0x63, + 0x6b, 0x61, 0x67, 0x65, 0x12, 0x8e, 0x01, 0x0a, 0x10, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4b, 0x65, + 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x12, 0x28, 0x2e, 0x78, 0x6d, 0x74, 0x70, + 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x65, 0x74, 0x63, + 0x68, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4b, 0x65, 0x79, 0x50, 0x61, + 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x3a, 0x01, 0x2a, 0x22, 0x1a, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, + 0x76, 0x31, 0x2f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x2d, 0x6b, 0x65, 0x79, 0x2d, 0x70, 0x61, 0x63, + 0x6b, 0x61, 0x67, 0x65, 0x73, 0x12, 0x96, 0x01, 0x0a, 0x12, 0x51, 0x75, 0x65, 0x72, 0x79, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x2a, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, + 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x27, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x3a, 0x01, 0x2a, + 0x22, 0x1c, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2d, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x9e, + 0x01, 0x0a, 0x14, 0x51, 0x75, 0x65, 0x72, 0x79, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, + 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x57, + 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x57, 0x65, 0x6c, + 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x3a, 0x01, 0x2a, 0x22, + 0x1e, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2d, 0x77, + 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x2d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, + 0x96, 0x01, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x2e, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x25, 0x3a, 0x01, 0x2a, 0x22, 0x20, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x2d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2d, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x30, 0x01, 0x12, 0x9e, 0x01, 0x0a, 0x18, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, + 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, + 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, + 0x3a, 0x01, 0x2a, 0x22, 0x22, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x2d, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x2d, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x30, 0x01, 0x42, 0xb8, 0x01, 0x92, 0x41, 0x0f, 0x12, + 0x0d, 0x0a, 0x06, 0x4d, 0x6c, 0x73, 0x41, 0x70, 0x69, 0x32, 0x03, 0x31, 0x2e, 0x30, 0x0a, 0x13, + 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x42, 0x08, 0x4d, 0x6c, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, + 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x6c, 0x73, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x41, 0xaa, 0x02, 0x0f, + 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x6c, 0x73, 0x2e, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, + 0x02, 0x0f, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, + 0x31, 0xe2, 0x02, 0x1b, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x41, 0x70, 0x69, + 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, + 0x02, 0x12, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x41, 0x70, 0x69, + 0x3a, 0x3a, 0x56, 0x31, 0x4a, 0xe7, 0x36, 0x0a, 0x07, 0x12, 0x05, 0x01, 0x00, 0xf8, 0x01, 0x01, + 0x0a, 0x17, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x0d, 0x20, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x20, 0x41, 0x50, 0x49, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, + 0x02, 0x00, 0x18, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x04, 0x00, 0x26, 0x0a, 0x09, + 0x0a, 0x02, 0x03, 0x01, 0x12, 0x03, 0x05, 0x00, 0x25, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x02, 0x12, + 0x03, 0x06, 0x00, 0x38, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x08, 0x00, 0x3d, 0x0a, 0x09, + 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x08, 0x00, 0x3d, 0x0a, 0x09, 0x0a, 0x01, 0x08, 0x12, 0x04, + 0x0a, 0x00, 0x0f, 0x02, 0x0a, 0x0b, 0x0a, 0x03, 0x08, 0x92, 0x08, 0x12, 0x04, 0x0a, 0x00, 0x0f, + 0x02, 0x0a, 0x0c, 0x0a, 0x04, 0x08, 0x92, 0x08, 0x02, 0x12, 0x04, 0x0b, 0x02, 0x0e, 0x03, 0x0a, + 0x0c, 0x0a, 0x05, 0x08, 0x92, 0x08, 0x02, 0x01, 0x12, 0x03, 0x0c, 0x04, 0x13, 0x0a, 0x0c, 0x0a, + 0x05, 0x08, 0x92, 0x08, 0x02, 0x06, 0x12, 0x03, 0x0d, 0x04, 0x12, 0x0a, 0x26, 0x0a, 0x02, 0x06, + 0x00, 0x12, 0x04, 0x12, 0x00, 0x53, 0x01, 0x1a, 0x1a, 0x20, 0x52, 0x50, 0x43, 0x73, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x4d, 0x4c, 0x53, 0x20, 0x41, + 0x50, 0x49, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x06, 0x00, 0x01, 0x12, 0x03, 0x12, 0x08, 0x0e, 0x0a, + 0x5f, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x00, 0x12, 0x04, 0x15, 0x02, 0x1a, 0x03, 0x1a, 0x51, 0x20, + 0x53, 0x65, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x4d, 0x4c, 0x53, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x2c, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, + 0x65, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x62, 0x65, 0x66, 0x6f, + 0x72, 0x65, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, + 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x0a, + 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x15, 0x06, 0x17, 0x0a, 0x0c, + 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x15, 0x18, 0x30, 0x0a, 0x0c, 0x0a, 0x05, + 0x06, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x15, 0x3b, 0x50, 0x0a, 0x0d, 0x0a, 0x05, 0x06, 0x00, + 0x02, 0x00, 0x04, 0x12, 0x04, 0x16, 0x04, 0x19, 0x06, 0x0a, 0x11, 0x0a, 0x09, 0x06, 0x00, 0x02, + 0x00, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x04, 0x16, 0x04, 0x19, 0x06, 0x0a, 0x11, 0x0a, 0x0a, + 0x06, 0x00, 0x02, 0x00, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x04, 0x12, 0x03, 0x17, 0x06, 0x29, 0x0a, + 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x00, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x07, 0x12, 0x03, 0x18, + 0x06, 0x0f, 0x0a, 0x30, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x01, 0x12, 0x04, 0x1d, 0x02, 0x22, 0x03, + 0x1a, 0x22, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x20, + 0x6f, 0x66, 0x20, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x1d, + 0x06, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x1d, 0x1a, 0x34, + 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x1d, 0x3f, 0x54, 0x0a, 0x0d, + 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x04, 0x12, 0x04, 0x1e, 0x04, 0x21, 0x06, 0x0a, 0x11, 0x0a, + 0x09, 0x06, 0x00, 0x02, 0x01, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x04, 0x1e, 0x04, 0x21, 0x06, + 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x01, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x04, 0x12, 0x03, + 0x1f, 0x06, 0x2b, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x01, 0x04, 0xb0, 0xca, 0xbc, 0x22, + 0x07, 0x12, 0x03, 0x20, 0x06, 0x0f, 0x0a, 0x50, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x02, 0x12, 0x04, + 0x25, 0x02, 0x2a, 0x03, 0x1a, 0x42, 0x20, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x61, 0x20, + 0x6e, 0x65, 0x77, 0x20, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2c, 0x20, + 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, + 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, + 0x01, 0x12, 0x03, 0x25, 0x06, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x02, 0x12, + 0x03, 0x25, 0x17, 0x2e, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x25, + 0x39, 0x4e, 0x0a, 0x0d, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x04, 0x12, 0x04, 0x26, 0x04, 0x29, + 0x06, 0x0a, 0x11, 0x0a, 0x09, 0x06, 0x00, 0x02, 0x02, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x04, + 0x26, 0x04, 0x29, 0x06, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x02, 0x04, 0xb0, 0xca, 0xbc, + 0x22, 0x04, 0x12, 0x03, 0x27, 0x06, 0x28, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x02, 0x04, + 0xb0, 0xca, 0xbc, 0x22, 0x07, 0x12, 0x03, 0x28, 0x06, 0x0f, 0x0a, 0x3f, 0x0a, 0x04, 0x06, 0x00, + 0x02, 0x03, 0x12, 0x04, 0x2d, 0x02, 0x32, 0x03, 0x1a, 0x31, 0x20, 0x47, 0x65, 0x74, 0x20, 0x6f, + 0x6e, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x50, + 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x20, 0x62, 0x79, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, + 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x2d, 0x06, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, + 0x03, 0x02, 0x12, 0x03, 0x2d, 0x17, 0x2e, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x03, 0x03, + 0x12, 0x03, 0x2d, 0x39, 0x51, 0x0a, 0x0d, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x03, 0x04, 0x12, 0x04, + 0x2e, 0x04, 0x31, 0x06, 0x0a, 0x11, 0x0a, 0x09, 0x06, 0x00, 0x02, 0x03, 0x04, 0xb0, 0xca, 0xbc, + 0x22, 0x12, 0x04, 0x2e, 0x04, 0x31, 0x06, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x03, 0x04, + 0xb0, 0xca, 0xbc, 0x22, 0x04, 0x12, 0x03, 0x2f, 0x06, 0x28, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, + 0x02, 0x03, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x07, 0x12, 0x03, 0x30, 0x06, 0x0f, 0x0a, 0x2b, 0x0a, + 0x04, 0x06, 0x00, 0x02, 0x04, 0x12, 0x04, 0x35, 0x02, 0x3a, 0x03, 0x1a, 0x1d, 0x20, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, + 0x02, 0x04, 0x01, 0x12, 0x03, 0x35, 0x06, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, + 0x02, 0x12, 0x03, 0x35, 0x19, 0x32, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x03, 0x12, + 0x03, 0x35, 0x3d, 0x57, 0x0a, 0x0d, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x04, 0x12, 0x04, 0x36, + 0x04, 0x39, 0x06, 0x0a, 0x11, 0x0a, 0x09, 0x06, 0x00, 0x02, 0x04, 0x04, 0xb0, 0xca, 0xbc, 0x22, + 0x12, 0x04, 0x36, 0x04, 0x39, 0x06, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x04, 0x04, 0xb0, + 0xca, 0xbc, 0x22, 0x04, 0x12, 0x03, 0x37, 0x06, 0x2a, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, + 0x04, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x07, 0x12, 0x03, 0x38, 0x06, 0x0f, 0x0a, 0x2b, 0x0a, 0x04, + 0x06, 0x00, 0x02, 0x05, 0x12, 0x04, 0x3d, 0x02, 0x42, 0x03, 0x1a, 0x1d, 0x20, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, + 0x05, 0x01, 0x12, 0x03, 0x3d, 0x06, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x05, 0x02, + 0x12, 0x03, 0x3d, 0x1b, 0x36, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x05, 0x03, 0x12, 0x03, + 0x3d, 0x41, 0x5d, 0x0a, 0x0d, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x05, 0x04, 0x12, 0x04, 0x3e, 0x04, + 0x41, 0x06, 0x0a, 0x11, 0x0a, 0x09, 0x06, 0x00, 0x02, 0x05, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, + 0x04, 0x3e, 0x04, 0x41, 0x06, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x05, 0x04, 0xb0, 0xca, + 0xbc, 0x22, 0x04, 0x12, 0x03, 0x3f, 0x06, 0x2c, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x05, + 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x07, 0x12, 0x03, 0x40, 0x06, 0x0f, 0x0a, 0x36, 0x0a, 0x04, 0x06, + 0x00, 0x02, 0x06, 0x12, 0x04, 0x45, 0x02, 0x4a, 0x03, 0x1a, 0x28, 0x20, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x20, 0x6f, 0x66, 0x20, + 0x6e, 0x65, 0x77, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x06, 0x01, 0x12, 0x03, 0x45, 0x06, + 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x06, 0x02, 0x12, 0x03, 0x45, 0x1d, 0x3a, 0x0a, + 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x06, 0x06, 0x12, 0x03, 0x45, 0x45, 0x4b, 0x0a, 0x0c, 0x0a, + 0x05, 0x06, 0x00, 0x02, 0x06, 0x03, 0x12, 0x03, 0x45, 0x4c, 0x58, 0x0a, 0x0d, 0x0a, 0x05, 0x06, + 0x00, 0x02, 0x06, 0x04, 0x12, 0x04, 0x46, 0x04, 0x49, 0x06, 0x0a, 0x11, 0x0a, 0x09, 0x06, 0x00, + 0x02, 0x06, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x04, 0x46, 0x04, 0x49, 0x06, 0x0a, 0x11, 0x0a, + 0x0a, 0x06, 0x00, 0x02, 0x06, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x04, 0x12, 0x03, 0x47, 0x06, 0x2e, + 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x06, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x07, 0x12, 0x03, + 0x48, 0x06, 0x0f, 0x0a, 0x38, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x07, 0x12, 0x04, 0x4d, 0x02, 0x52, + 0x03, 0x1a, 0x2a, 0x20, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x20, 0x73, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x20, 0x6f, 0x66, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x77, 0x65, 0x6c, 0x63, + 0x6f, 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, + 0x05, 0x06, 0x00, 0x02, 0x07, 0x01, 0x12, 0x03, 0x4d, 0x06, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x06, + 0x00, 0x02, 0x07, 0x02, 0x12, 0x03, 0x4d, 0x1f, 0x3e, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, + 0x07, 0x06, 0x12, 0x03, 0x4d, 0x49, 0x4f, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x07, 0x03, + 0x12, 0x03, 0x4d, 0x50, 0x5e, 0x0a, 0x0d, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x07, 0x04, 0x12, 0x04, + 0x4e, 0x04, 0x51, 0x06, 0x0a, 0x11, 0x0a, 0x09, 0x06, 0x00, 0x02, 0x07, 0x04, 0xb0, 0xca, 0xbc, + 0x22, 0x12, 0x04, 0x4e, 0x04, 0x51, 0x06, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x07, 0x04, + 0xb0, 0xca, 0xbc, 0x22, 0x04, 0x12, 0x03, 0x4f, 0x06, 0x30, 0x0a, 0x11, 0x0a, 0x0a, 0x06, 0x00, + 0x02, 0x07, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x07, 0x12, 0x03, 0x50, 0x06, 0x0f, 0x0a, 0x36, 0x0a, + 0x02, 0x04, 0x00, 0x12, 0x04, 0x56, 0x00, 0x63, 0x01, 0x1a, 0x2a, 0x20, 0x46, 0x75, 0x6c, 0x6c, + 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x6f, 0x66, 0x20, 0x61, 0x20, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x56, 0x08, + 0x16, 0x0a, 0x36, 0x0a, 0x04, 0x04, 0x00, 0x03, 0x00, 0x12, 0x04, 0x58, 0x02, 0x5e, 0x03, 0x1a, + 0x28, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, + 0x00, 0x01, 0x12, 0x03, 0x58, 0x0a, 0x0c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, + 0x00, 0x12, 0x03, 0x59, 0x04, 0x12, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, + 0x05, 0x12, 0x03, 0x59, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, + 0x01, 0x12, 0x03, 0x59, 0x0b, 0x0d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, + 0x03, 0x12, 0x03, 0x59, 0x10, 0x11, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, + 0x12, 0x03, 0x5a, 0x04, 0x1a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x05, + 0x12, 0x03, 0x5a, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x01, + 0x12, 0x03, 0x5a, 0x0b, 0x15, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x03, + 0x12, 0x03, 0x5a, 0x18, 0x19, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x02, 0x12, + 0x03, 0x5b, 0x04, 0x1f, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, + 0x03, 0x5b, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, + 0x03, 0x5b, 0x0a, 0x1a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, + 0x03, 0x5b, 0x1d, 0x1e, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x03, 0x12, 0x03, + 0x5c, 0x04, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x03, 0x05, 0x12, 0x03, + 0x5c, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, + 0x5c, 0x0a, 0x0e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x03, 0x03, 0x12, 0x03, + 0x5c, 0x11, 0x12, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x04, 0x12, 0x03, 0x5d, + 0x04, 0x1e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x04, 0x05, 0x12, 0x03, 0x5d, + 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x04, 0x01, 0x12, 0x03, 0x5d, + 0x0a, 0x19, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x04, 0x03, 0x12, 0x03, 0x5d, + 0x1c, 0x1d, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x00, 0x08, 0x00, 0x12, 0x04, 0x60, 0x02, 0x62, 0x03, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x08, 0x00, 0x01, 0x12, 0x03, 0x60, 0x08, 0x0f, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x61, 0x04, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x61, 0x04, 0x06, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, + 0x00, 0x01, 0x12, 0x03, 0x61, 0x07, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, + 0x12, 0x03, 0x61, 0x0c, 0x0d, 0x0a, 0x2e, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x66, 0x00, 0x71, + 0x01, 0x1a, 0x22, 0x20, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x61, 0x20, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x66, 0x08, + 0x1b, 0x0a, 0x3b, 0x0a, 0x04, 0x04, 0x01, 0x03, 0x00, 0x12, 0x04, 0x68, 0x02, 0x6c, 0x03, 0x1a, 0x2d, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x0a, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x01, 0x03, 0x00, 0x01, 0x12, 0x04, 0x84, 0x01, 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, - 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0x85, 0x01, 0x04, 0x1f, 0x0a, 0x0f, 0x0a, - 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0x85, 0x01, 0x04, 0x09, 0x0a, 0x0f, - 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0x85, 0x01, 0x0a, 0x1a, 0x0a, - 0x0f, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0x85, 0x01, 0x1d, 0x1e, - 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, 0x86, 0x01, 0x04, 0x13, - 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x04, 0x86, 0x01, 0x04, - 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0x86, 0x01, - 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, 0x86, - 0x01, 0x11, 0x12, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x12, 0x04, 0x87, - 0x01, 0x04, 0x1e, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, 0x04, - 0x87, 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, - 0x04, 0x87, 0x01, 0x0a, 0x19, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x03, - 0x12, 0x04, 0x87, 0x01, 0x1c, 0x1d, 0x0a, 0x0e, 0x0a, 0x04, 0x04, 0x01, 0x08, 0x00, 0x12, 0x06, - 0x8a, 0x01, 0x02, 0x8c, 0x01, 0x03, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x01, 0x08, 0x00, 0x01, 0x12, - 0x04, 0x8a, 0x01, 0x08, 0x0f, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x04, 0x8b, - 0x01, 0x04, 0x0e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, 0x04, 0x8b, 0x01, - 0x04, 0x06, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x04, 0x8b, 0x01, 0x07, - 0x09, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x04, 0x8b, 0x01, 0x0c, 0x0d, - 0x0a, 0x36, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x06, 0x90, 0x01, 0x00, 0x9d, 0x01, 0x01, 0x1a, 0x28, - 0x20, 0x46, 0x75, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, - 0x04, 0x90, 0x01, 0x08, 0x14, 0x0a, 0x36, 0x0a, 0x04, 0x04, 0x02, 0x03, 0x00, 0x12, 0x06, 0x92, - 0x01, 0x02, 0x98, 0x01, 0x03, 0x1a, 0x26, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, - 0x31, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x0a, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x02, 0x03, 0x00, 0x01, 0x12, 0x04, 0x92, 0x01, 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x06, - 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0x93, 0x01, 0x04, 0x12, 0x0a, 0x0f, 0x0a, 0x07, - 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0x93, 0x01, 0x04, 0x0a, 0x0a, 0x0f, 0x0a, - 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0x93, 0x01, 0x0b, 0x0d, 0x0a, 0x0f, - 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0x93, 0x01, 0x10, 0x11, 0x0a, - 0x0e, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, 0x94, 0x01, 0x04, 0x1a, 0x0a, - 0x0f, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x04, 0x94, 0x01, 0x04, 0x0a, - 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0x94, 0x01, 0x0b, - 0x15, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, 0x94, 0x01, - 0x18, 0x19, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x02, 0x12, 0x04, 0x95, 0x01, - 0x04, 0x17, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, 0x04, 0x95, - 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x04, - 0x95, 0x01, 0x0a, 0x12, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, - 0x04, 0x95, 0x01, 0x15, 0x16, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x03, 0x12, - 0x04, 0x96, 0x01, 0x04, 0x13, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x03, 0x05, - 0x12, 0x04, 0x96, 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x03, - 0x01, 0x12, 0x04, 0x96, 0x01, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, - 0x03, 0x03, 0x12, 0x04, 0x96, 0x01, 0x11, 0x12, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, - 0x02, 0x04, 0x12, 0x04, 0x97, 0x01, 0x04, 0x1a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, - 0x02, 0x04, 0x05, 0x12, 0x04, 0x97, 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x02, 0x03, - 0x00, 0x02, 0x04, 0x01, 0x12, 0x04, 0x97, 0x01, 0x0a, 0x15, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x02, - 0x03, 0x00, 0x02, 0x04, 0x03, 0x12, 0x04, 0x97, 0x01, 0x18, 0x19, 0x0a, 0x0e, 0x0a, 0x04, 0x04, - 0x02, 0x08, 0x00, 0x12, 0x06, 0x9a, 0x01, 0x02, 0x9c, 0x01, 0x03, 0x0a, 0x0d, 0x0a, 0x05, 0x04, - 0x02, 0x08, 0x00, 0x01, 0x12, 0x04, 0x9a, 0x01, 0x08, 0x0f, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x02, - 0x02, 0x00, 0x12, 0x04, 0x9b, 0x01, 0x04, 0x0e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, - 0x06, 0x12, 0x04, 0x9b, 0x01, 0x04, 0x06, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, - 0x12, 0x04, 0x9b, 0x01, 0x07, 0x09, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, - 0x04, 0x9b, 0x01, 0x0c, 0x0d, 0x0a, 0x2e, 0x0a, 0x02, 0x04, 0x03, 0x12, 0x06, 0xa0, 0x01, 0x00, - 0xaa, 0x01, 0x01, 0x1a, 0x20, 0x20, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, - 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x04, 0xa0, 0x01, - 0x08, 0x19, 0x0a, 0x43, 0x0a, 0x04, 0x04, 0x03, 0x03, 0x00, 0x12, 0x06, 0xa2, 0x01, 0x02, 0xa5, - 0x01, 0x03, 0x1a, 0x33, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x20, 0x6f, - 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x03, 0x03, 0x00, 0x01, - 0x12, 0x04, 0xa2, 0x01, 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, - 0x12, 0x04, 0xa3, 0x01, 0x04, 0x13, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, - 0x05, 0x12, 0x04, 0xa3, 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, - 0x00, 0x01, 0x12, 0x04, 0xa3, 0x01, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, - 0x02, 0x00, 0x03, 0x12, 0x04, 0xa3, 0x01, 0x11, 0x12, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x03, 0x03, - 0x00, 0x02, 0x01, 0x12, 0x04, 0xa4, 0x01, 0x04, 0x1a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x03, 0x03, - 0x00, 0x02, 0x01, 0x05, 0x12, 0x04, 0xa4, 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x03, - 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0xa4, 0x01, 0x0a, 0x15, 0x0a, 0x0f, 0x0a, 0x07, 0x04, - 0x03, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, 0xa4, 0x01, 0x18, 0x19, 0x0a, 0x0e, 0x0a, 0x04, - 0x04, 0x03, 0x08, 0x00, 0x12, 0x06, 0xa7, 0x01, 0x02, 0xa9, 0x01, 0x03, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x03, 0x08, 0x00, 0x01, 0x12, 0x04, 0xa7, 0x01, 0x08, 0x0f, 0x0a, 0x0c, 0x0a, 0x04, 0x04, - 0x03, 0x02, 0x00, 0x12, 0x04, 0xa8, 0x01, 0x04, 0x0e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x03, 0x02, - 0x00, 0x06, 0x12, 0x04, 0xa8, 0x01, 0x04, 0x06, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, - 0x01, 0x12, 0x04, 0xa8, 0x01, 0x07, 0x09, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, - 0x12, 0x04, 0xa8, 0x01, 0x0c, 0x0d, 0x0a, 0x2c, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x06, 0xad, 0x01, - 0x00, 0xaf, 0x01, 0x01, 0x1a, 0x1e, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x62, 0x61, - 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x4d, 0x4c, 0x53, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x04, 0x01, 0x12, 0x04, 0xad, 0x01, 0x08, - 0x20, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, 0x04, 0xae, 0x01, 0x02, 0x2a, 0x0a, - 0x0d, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x04, 0x12, 0x04, 0xae, 0x01, 0x02, 0x0a, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x06, 0x12, 0x04, 0xae, 0x01, 0x0b, 0x1c, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x04, 0xae, 0x01, 0x1d, 0x25, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x04, 0xae, 0x01, 0x28, 0x29, 0x0a, 0x30, 0x0a, 0x02, 0x04, - 0x05, 0x12, 0x06, 0xb2, 0x01, 0x00, 0xb4, 0x01, 0x01, 0x1a, 0x22, 0x20, 0x53, 0x65, 0x6e, 0x64, - 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x77, 0x65, 0x6c, 0x63, - 0x6f, 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, - 0x03, 0x04, 0x05, 0x01, 0x12, 0x04, 0xb2, 0x01, 0x08, 0x22, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x05, - 0x02, 0x00, 0x12, 0x04, 0xb3, 0x01, 0x02, 0x2c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, - 0x04, 0x12, 0x04, 0xb3, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x06, - 0x12, 0x04, 0xb3, 0x01, 0x0b, 0x1e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x01, 0x12, - 0x04, 0xb3, 0x01, 0x1f, 0x27, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x03, 0x12, 0x04, - 0xb3, 0x01, 0x2a, 0x2b, 0x0a, 0x9a, 0x01, 0x0a, 0x02, 0x04, 0x06, 0x12, 0x06, 0xb7, 0x01, 0x00, - 0xbe, 0x01, 0x01, 0x1a, 0x28, 0x20, 0x41, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, - 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x50, - 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x0a, 0x22, 0x62, 0x20, - 0x54, 0x68, 0x69, 0x73, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, - 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x4d, 0x4c, 0x53, 0x20, 0x6b, - 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x0a, 0x20, - 0x70, 0x61, 0x72, 0x73, 0x65, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2c, - 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, - 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x06, 0x01, 0x12, 0x04, 0xb7, 0x01, 0x08, 0x18, 0x0a, 0x97, - 0x01, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x00, 0x12, 0x04, 0xbd, 0x01, 0x02, 0x27, 0x1a, 0x88, 0x01, - 0x20, 0x54, 0x68, 0x65, 0x20, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x27, 0x73, 0x20, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x77, 0x6f, 0x75, 0x6c, - 0x64, 0x20, 0x62, 0x65, 0x20, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x65, 0x64, 0x20, 0x66, - 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x0a, 0x20, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x69, 0x6e, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2c, - 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, - 0x05, 0x12, 0x04, 0xbd, 0x01, 0x02, 0x07, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x01, - 0x12, 0x04, 0xbd, 0x01, 0x08, 0x22, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x03, 0x12, - 0x04, 0xbd, 0x01, 0x25, 0x26, 0x0a, 0x2b, 0x0a, 0x02, 0x04, 0x07, 0x12, 0x06, 0xc1, 0x01, 0x00, - 0xc5, 0x01, 0x01, 0x1a, 0x1d, 0x20, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x20, 0x61, - 0x20, 0x6e, 0x65, 0x77, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x07, 0x01, 0x12, 0x04, 0xc1, 0x01, 0x08, 0x23, 0x0a, - 0x5b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x00, 0x12, 0x04, 0xc3, 0x01, 0x02, 0x23, 0x1a, 0x4d, 0x20, - 0x54, 0x68, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x20, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x69, 0x6e, 0x66, - 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x65, 0x64, 0x20, - 0x74, 0x6f, 0x20, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x20, 0x61, 0x6e, 0x20, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x07, 0x02, 0x00, 0x06, 0x12, 0x04, 0xc3, 0x01, 0x02, 0x12, 0x0a, 0x0d, 0x0a, 0x05, 0x04, - 0x07, 0x02, 0x00, 0x01, 0x12, 0x04, 0xc3, 0x01, 0x13, 0x1e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x07, - 0x02, 0x00, 0x03, 0x12, 0x04, 0xc3, 0x01, 0x21, 0x22, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x07, 0x02, - 0x01, 0x12, 0x04, 0xc4, 0x01, 0x02, 0x21, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x05, - 0x12, 0x04, 0xc4, 0x01, 0x02, 0x06, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x01, 0x12, - 0x04, 0xc4, 0x01, 0x07, 0x1d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x03, 0x12, 0x04, - 0xc4, 0x01, 0x1f, 0x20, 0x0a, 0x3d, 0x0a, 0x02, 0x04, 0x08, 0x12, 0x06, 0xc8, 0x01, 0x00, 0xca, - 0x01, 0x01, 0x1a, 0x2f, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x49, - 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x08, 0x01, 0x12, 0x04, 0xc8, 0x01, 0x08, 0x24, - 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x08, 0x02, 0x00, 0x12, 0x04, 0xc9, 0x01, 0x02, 0x1d, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x05, 0x12, 0x04, 0xc9, 0x01, 0x02, 0x07, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x08, 0x02, 0x00, 0x01, 0x12, 0x04, 0xc9, 0x01, 0x08, 0x18, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x08, 0x02, 0x00, 0x03, 0x12, 0x04, 0xc9, 0x01, 0x1b, 0x1c, 0x0a, 0x29, 0x0a, 0x02, 0x04, - 0x09, 0x12, 0x06, 0xcd, 0x01, 0x00, 0xd1, 0x01, 0x01, 0x1a, 0x1b, 0x20, 0x55, 0x70, 0x6c, 0x6f, - 0x61, 0x64, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, - 0x6b, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x09, 0x01, 0x12, 0x04, 0xcd, - 0x01, 0x08, 0x1f, 0x0a, 0x38, 0x0a, 0x04, 0x04, 0x09, 0x02, 0x00, 0x12, 0x04, 0xcf, 0x01, 0x02, - 0x23, 0x1a, 0x2a, 0x20, 0x41, 0x6e, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, - 0x6c, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x20, 0x75, 0x70, - 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x09, 0x02, 0x00, 0x06, 0x12, 0x04, 0xcf, 0x01, 0x02, 0x12, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x09, 0x02, 0x00, 0x01, 0x12, 0x04, 0xcf, 0x01, 0x13, 0x1e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, - 0x09, 0x02, 0x00, 0x03, 0x12, 0x04, 0xcf, 0x01, 0x21, 0x22, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x09, - 0x02, 0x01, 0x12, 0x04, 0xd0, 0x01, 0x02, 0x22, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x01, - 0x05, 0x12, 0x04, 0xd0, 0x01, 0x02, 0x06, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x01, 0x01, - 0x12, 0x04, 0xd0, 0x01, 0x07, 0x1d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x01, 0x03, 0x12, - 0x04, 0xd0, 0x01, 0x20, 0x21, 0x0a, 0x2e, 0x0a, 0x02, 0x04, 0x0a, 0x12, 0x06, 0xd4, 0x01, 0x00, - 0xd9, 0x01, 0x01, 0x1a, 0x20, 0x20, 0x46, 0x65, 0x74, 0x63, 0x68, 0x20, 0x6f, 0x6e, 0x65, 0x20, - 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, - 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0a, 0x01, 0x12, 0x04, 0xd4, 0x01, - 0x08, 0x1f, 0x0a, 0xac, 0x01, 0x0a, 0x04, 0x04, 0x0a, 0x02, 0x00, 0x12, 0x04, 0xd8, 0x01, 0x02, - 0x27, 0x1a, 0x9d, 0x01, 0x20, 0x54, 0x68, 0x65, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x20, - 0x63, 0x61, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, - 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x41, 0x50, 0x49, 0x0a, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, - 0x61, 0x67, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, - 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x65, 0x61, 0x63, 0x68, 0x0a, 0x20, + 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x0a, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x01, 0x03, 0x00, 0x01, 0x12, 0x03, 0x68, 0x0a, 0x0c, 0x0a, 0x0d, 0x0a, 0x06, + 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x69, 0x04, 0x1f, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x01, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x69, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x69, 0x0a, 0x1a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x01, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x69, 0x1d, 0x1e, 0x0a, 0x0d, 0x0a, 0x06, 0x04, + 0x01, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x6a, 0x04, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, + 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x6a, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, + 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x6a, 0x0a, 0x0e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, + 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x6a, 0x11, 0x12, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x01, + 0x03, 0x00, 0x02, 0x02, 0x12, 0x03, 0x6b, 0x04, 0x1e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, + 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x6b, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, + 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x6b, 0x0a, 0x19, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, + 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x6b, 0x1c, 0x1d, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x01, 0x08, + 0x00, 0x12, 0x04, 0x6e, 0x02, 0x70, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x08, 0x00, 0x01, + 0x12, 0x03, 0x6e, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x6f, + 0x04, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, 0x6f, 0x04, 0x06, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x6f, 0x07, 0x09, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x6f, 0x0c, 0x0d, 0x0a, 0x35, 0x0a, 0x02, + 0x04, 0x02, 0x12, 0x05, 0x74, 0x00, 0x81, 0x01, 0x01, 0x1a, 0x28, 0x20, 0x46, 0x75, 0x6c, 0x6c, + 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x6f, 0x66, 0x20, 0x61, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x74, 0x08, 0x14, 0x0a, + 0x34, 0x0a, 0x04, 0x04, 0x02, 0x03, 0x00, 0x12, 0x04, 0x76, 0x02, 0x7c, 0x03, 0x1a, 0x26, 0x20, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x03, 0x00, 0x01, 0x12, 0x03, + 0x76, 0x0a, 0x0c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x77, + 0x04, 0x12, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x77, + 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x77, + 0x0b, 0x0d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x77, + 0x10, 0x11, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x78, 0x04, + 0x1a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x78, 0x04, + 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x78, 0x0b, + 0x15, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x78, 0x18, + 0x19, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x02, 0x12, 0x03, 0x79, 0x04, 0x17, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x79, 0x04, 0x09, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x79, 0x0a, 0x12, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x79, 0x15, 0x16, + 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x03, 0x12, 0x03, 0x7a, 0x04, 0x13, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x03, 0x05, 0x12, 0x03, 0x7a, 0x04, 0x09, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x7a, 0x0a, 0x0e, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x03, 0x03, 0x12, 0x03, 0x7a, 0x11, 0x12, 0x0a, + 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x04, 0x12, 0x03, 0x7b, 0x04, 0x1a, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x04, 0x05, 0x12, 0x03, 0x7b, 0x04, 0x09, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x04, 0x01, 0x12, 0x03, 0x7b, 0x0a, 0x15, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x04, 0x03, 0x12, 0x03, 0x7b, 0x18, 0x19, 0x0a, 0x0d, + 0x0a, 0x04, 0x04, 0x02, 0x08, 0x00, 0x12, 0x05, 0x7e, 0x02, 0x80, 0x01, 0x03, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x02, 0x08, 0x00, 0x01, 0x12, 0x03, 0x7e, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, + 0x02, 0x02, 0x00, 0x12, 0x03, 0x7f, 0x04, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, + 0x06, 0x12, 0x03, 0x7f, 0x04, 0x06, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, + 0x03, 0x7f, 0x07, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x7f, + 0x0c, 0x0d, 0x0a, 0x2e, 0x0a, 0x02, 0x04, 0x03, 0x12, 0x06, 0x84, 0x01, 0x00, 0x8e, 0x01, 0x01, + 0x1a, 0x20, 0x20, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x61, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x04, 0x84, 0x01, 0x08, 0x19, 0x0a, + 0x43, 0x0a, 0x04, 0x04, 0x03, 0x03, 0x00, 0x12, 0x06, 0x86, 0x01, 0x02, 0x89, 0x01, 0x03, 0x1a, + 0x33, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x03, 0x03, 0x00, 0x01, 0x12, 0x04, 0x86, + 0x01, 0x0a, 0x0c, 0x0a, 0x2f, 0x0a, 0x06, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0x87, + 0x01, 0x04, 0x13, 0x22, 0x1f, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, + 0x20, 0x4d, 0x6c, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, + 0x04, 0x87, 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x01, + 0x12, 0x04, 0x87, 0x01, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, + 0x03, 0x12, 0x04, 0x87, 0x01, 0x11, 0x12, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x03, 0x03, 0x00, 0x02, + 0x01, 0x12, 0x04, 0x88, 0x01, 0x04, 0x1a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, + 0x01, 0x05, 0x12, 0x04, 0x88, 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, + 0x02, 0x01, 0x01, 0x12, 0x04, 0x88, 0x01, 0x0a, 0x15, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x03, 0x03, + 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, 0x88, 0x01, 0x18, 0x19, 0x0a, 0x0e, 0x0a, 0x04, 0x04, 0x03, + 0x08, 0x00, 0x12, 0x06, 0x8b, 0x01, 0x02, 0x8d, 0x01, 0x03, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x03, + 0x08, 0x00, 0x01, 0x12, 0x04, 0x8b, 0x01, 0x08, 0x0f, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x03, 0x02, + 0x00, 0x12, 0x04, 0x8c, 0x01, 0x04, 0x0e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x06, + 0x12, 0x04, 0x8c, 0x01, 0x04, 0x06, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x01, 0x12, + 0x04, 0x8c, 0x01, 0x07, 0x09, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, 0x04, + 0x8c, 0x01, 0x0c, 0x0d, 0x0a, 0x2c, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x06, 0x91, 0x01, 0x00, 0x93, + 0x01, 0x01, 0x1a, 0x1e, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, 0x63, + 0x68, 0x20, 0x6f, 0x66, 0x20, 0x4d, 0x4c, 0x53, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x04, 0x01, 0x12, 0x04, 0x91, 0x01, 0x08, 0x20, 0x0a, + 0x0c, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, 0x04, 0x92, 0x01, 0x02, 0x2a, 0x0a, 0x0d, 0x0a, + 0x05, 0x04, 0x04, 0x02, 0x00, 0x04, 0x12, 0x04, 0x92, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, + 0x04, 0x04, 0x02, 0x00, 0x06, 0x12, 0x04, 0x92, 0x01, 0x0b, 0x1c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, + 0x04, 0x02, 0x00, 0x01, 0x12, 0x04, 0x92, 0x01, 0x1d, 0x25, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x04, + 0x02, 0x00, 0x03, 0x12, 0x04, 0x92, 0x01, 0x28, 0x29, 0x0a, 0x30, 0x0a, 0x02, 0x04, 0x05, 0x12, + 0x06, 0x96, 0x01, 0x00, 0x98, 0x01, 0x01, 0x1a, 0x22, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x20, 0x61, + 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, + 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, + 0x05, 0x01, 0x12, 0x04, 0x96, 0x01, 0x08, 0x22, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, + 0x12, 0x04, 0x97, 0x01, 0x02, 0x2c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x04, 0x12, + 0x04, 0x97, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x06, 0x12, 0x04, + 0x97, 0x01, 0x0b, 0x1e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x01, 0x12, 0x04, 0x97, + 0x01, 0x1f, 0x27, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x03, 0x12, 0x04, 0x97, 0x01, + 0x2a, 0x2b, 0x0a, 0x9a, 0x01, 0x0a, 0x02, 0x04, 0x06, 0x12, 0x06, 0x9b, 0x01, 0x00, 0xa2, 0x01, + 0x01, 0x1a, 0x28, 0x20, 0x41, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x61, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x50, 0x61, 0x63, + 0x6b, 0x61, 0x67, 0x65, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x0a, 0x22, 0x62, 0x20, 0x54, 0x68, + 0x69, 0x73, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x73, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x4d, 0x4c, 0x53, 0x20, 0x6b, 0x65, 0x79, + 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x0a, 0x20, 0x70, 0x61, + 0x72, 0x73, 0x65, 0x2c, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2c, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x0a, 0x0a, + 0x0b, 0x0a, 0x03, 0x04, 0x06, 0x01, 0x12, 0x04, 0x9b, 0x01, 0x08, 0x18, 0x0a, 0x97, 0x01, 0x0a, + 0x04, 0x04, 0x06, 0x02, 0x00, 0x12, 0x04, 0xa1, 0x01, 0x02, 0x27, 0x1a, 0x88, 0x01, 0x20, 0x54, + 0x68, 0x65, 0x20, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x27, 0x73, 0x20, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, + 0x62, 0x65, 0x20, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, + 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x0a, 0x20, + 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x05, 0x12, + 0x04, 0xa1, 0x01, 0x02, 0x07, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x01, 0x12, 0x04, + 0xa1, 0x01, 0x08, 0x22, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x03, 0x12, 0x04, 0xa1, + 0x01, 0x25, 0x26, 0x0a, 0x29, 0x0a, 0x02, 0x04, 0x07, 0x12, 0x06, 0xa5, 0x01, 0x00, 0xa9, 0x01, + 0x01, 0x1a, 0x1b, 0x20, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, + 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0b, + 0x0a, 0x03, 0x04, 0x07, 0x01, 0x12, 0x04, 0xa5, 0x01, 0x08, 0x1f, 0x0a, 0x38, 0x0a, 0x04, 0x04, + 0x07, 0x02, 0x00, 0x12, 0x04, 0xa7, 0x01, 0x02, 0x23, 0x1a, 0x2a, 0x20, 0x41, 0x6e, 0x20, 0x69, + 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, + 0x63, 0x6b, 0x61, 0x67, 0x65, 0x20, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x06, 0x12, 0x04, + 0xa7, 0x01, 0x02, 0x12, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x01, 0x12, 0x04, 0xa7, + 0x01, 0x13, 0x1e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x03, 0x12, 0x04, 0xa7, 0x01, + 0x21, 0x22, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x01, 0x12, 0x04, 0xa8, 0x01, 0x02, 0x22, + 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x05, 0x12, 0x04, 0xa8, 0x01, 0x02, 0x06, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x01, 0x12, 0x04, 0xa8, 0x01, 0x07, 0x1d, 0x0a, 0x0d, + 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x03, 0x12, 0x04, 0xa8, 0x01, 0x20, 0x21, 0x0a, 0x2e, 0x0a, + 0x02, 0x04, 0x08, 0x12, 0x06, 0xac, 0x01, 0x00, 0xb1, 0x01, 0x01, 0x1a, 0x20, 0x20, 0x46, 0x65, + 0x74, 0x63, 0x68, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, + 0x03, 0x04, 0x08, 0x01, 0x12, 0x04, 0xac, 0x01, 0x08, 0x1f, 0x0a, 0xac, 0x01, 0x0a, 0x04, 0x04, + 0x08, 0x02, 0x00, 0x12, 0x04, 0xb0, 0x01, 0x02, 0x27, 0x1a, 0x9d, 0x01, 0x20, 0x54, 0x68, 0x65, + 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6b, 0x65, 0x79, - 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x04, 0x12, 0x04, 0xd8, 0x01, 0x02, 0x0a, - 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x05, 0x12, 0x04, 0xd8, 0x01, 0x0b, 0x10, 0x0a, - 0x0d, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x01, 0x12, 0x04, 0xd8, 0x01, 0x11, 0x22, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x03, 0x12, 0x04, 0xd8, 0x01, 0x25, 0x26, 0x0a, 0x39, 0x0a, - 0x02, 0x04, 0x0b, 0x12, 0x06, 0xdc, 0x01, 0x00, 0xe6, 0x01, 0x01, 0x1a, 0x2b, 0x20, 0x54, 0x68, - 0x65, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, - 0x46, 0x65, 0x74, 0x63, 0x68, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0b, 0x01, 0x12, - 0x04, 0xdc, 0x01, 0x08, 0x20, 0x0a, 0x2b, 0x0a, 0x04, 0x04, 0x0b, 0x03, 0x00, 0x12, 0x06, 0xde, - 0x01, 0x02, 0xe0, 0x01, 0x03, 0x1a, 0x1b, 0x20, 0x41, 0x6e, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x76, - 0x69, 0x64, 0x75, 0x61, 0x6c, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, - 0x65, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x03, 0x00, 0x01, 0x12, 0x04, 0xde, 0x01, 0x0a, - 0x14, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0b, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0xdf, 0x01, 0x04, - 0x29, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0b, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0xdf, 0x01, - 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0b, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xdf, - 0x01, 0x0a, 0x24, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0b, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, - 0xdf, 0x01, 0x27, 0x28, 0x0a, 0xcf, 0x01, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x00, 0x12, 0x04, 0xe5, - 0x01, 0x02, 0x27, 0x1a, 0xc0, 0x01, 0x20, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x6f, - 0x6e, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x20, 0x70, - 0x65, 0x72, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x20, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x20, 0x49, 0x66, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, - 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, - 0x61, 0x67, 0x65, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x65, - 0x6e, 0x74, 0x72, 0x79, 0x20, 0x69, 0x73, 0x0a, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x69, 0x6e, - 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, - 0x65, 0x20, 0x73, 0x70, 0x6f, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, - 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x04, 0x12, - 0x04, 0xe5, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x06, 0x12, 0x04, - 0xe5, 0x01, 0x0b, 0x15, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x01, 0x12, 0x04, 0xe5, - 0x01, 0x16, 0x22, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x03, 0x12, 0x04, 0xe5, 0x01, - 0x25, 0x26, 0x0a, 0x26, 0x0a, 0x02, 0x04, 0x0c, 0x12, 0x06, 0xe9, 0x01, 0x00, 0xee, 0x01, 0x01, - 0x1a, 0x18, 0x20, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0c, - 0x01, 0x12, 0x04, 0xe9, 0x01, 0x08, 0x21, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0c, 0x02, 0x00, 0x12, - 0x04, 0xea, 0x01, 0x02, 0x1d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x05, 0x12, 0x04, - 0xea, 0x01, 0x02, 0x07, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x01, 0x12, 0x04, 0xea, - 0x01, 0x08, 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x03, 0x12, 0x04, 0xea, 0x01, - 0x1b, 0x1c, 0x0a, 0x97, 0x01, 0x0a, 0x04, 0x04, 0x0c, 0x02, 0x01, 0x12, 0x04, 0xed, 0x01, 0x02, - 0x37, 0x1a, 0x88, 0x01, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x6f, - 0x76, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x20, 0x62, 0x65, 0x69, 0x6e, 0x67, 0x20, 0x72, - 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x20, 0x28, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x6f, 0x6d, 0x65, - 0x20, 0x73, 0x6f, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, - 0x64, 0x20, 0x70, 0x72, 0x6f, 0x6c, 0x6f, 0x67, 0x75, 0x65, 0x29, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x0c, 0x02, 0x01, 0x06, 0x12, 0x04, 0xed, 0x01, 0x02, 0x21, 0x0a, 0x0d, 0x0a, 0x05, 0x04, - 0x0c, 0x02, 0x01, 0x01, 0x12, 0x04, 0xed, 0x01, 0x22, 0x32, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, - 0x02, 0x01, 0x03, 0x12, 0x04, 0xed, 0x01, 0x35, 0x36, 0x0a, 0x48, 0x0a, 0x02, 0x04, 0x0d, 0x12, - 0x06, 0xf1, 0x01, 0x00, 0xf4, 0x01, 0x01, 0x1a, 0x3a, 0x20, 0x47, 0x65, 0x74, 0x20, 0x61, 0x6c, - 0x6c, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x6e, - 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x74, 0x69, - 0x6d, 0x65, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0d, 0x01, 0x12, 0x04, 0xf1, 0x01, 0x08, 0x21, - 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0d, 0x02, 0x00, 0x12, 0x04, 0xf2, 0x01, 0x02, 0x28, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x00, 0x04, 0x12, 0x04, 0xf2, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x0d, 0x02, 0x00, 0x05, 0x12, 0x04, 0xf2, 0x01, 0x0b, 0x11, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x0d, 0x02, 0x00, 0x01, 0x12, 0x04, 0xf2, 0x01, 0x12, 0x23, 0x0a, 0x0d, 0x0a, 0x05, 0x04, - 0x0d, 0x02, 0x00, 0x03, 0x12, 0x04, 0xf2, 0x01, 0x26, 0x27, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0d, - 0x02, 0x01, 0x12, 0x04, 0xf3, 0x01, 0x02, 0x1b, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x01, - 0x05, 0x12, 0x04, 0xf3, 0x01, 0x02, 0x08, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x01, 0x01, - 0x12, 0x04, 0xf3, 0x01, 0x09, 0x16, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x01, 0x03, 0x12, - 0x04, 0xf3, 0x01, 0x19, 0x1a, 0x0a, 0x5b, 0x0a, 0x02, 0x04, 0x0e, 0x12, 0x06, 0xf7, 0x01, 0x00, - 0x94, 0x02, 0x01, 0x1a, 0x4d, 0x20, 0x55, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x67, 0x65, - 0x74, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x76, - 0x6f, 0x6b, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, - 0x20, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, - 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0e, 0x01, 0x12, 0x04, 0xf7, 0x01, 0x08, 0x22, 0x0a, - 0x51, 0x0a, 0x04, 0x04, 0x0e, 0x03, 0x00, 0x12, 0x06, 0xf9, 0x01, 0x02, 0xfc, 0x01, 0x03, 0x1a, - 0x41, 0x20, 0x41, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x77, 0x61, 0x73, 0x20, 0x73, 0x65, 0x65, - 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, - 0x74, 0x69, 0x6d, 0x65, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, - 0x73, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x03, 0x00, 0x01, 0x12, 0x04, 0xf9, 0x01, 0x0a, - 0x1f, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0xfa, 0x01, 0x04, - 0x1f, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0xfa, 0x01, - 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xfa, - 0x01, 0x0a, 0x1a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, - 0xfa, 0x01, 0x1d, 0x1e, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, - 0xfb, 0x01, 0x04, 0x22, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, - 0x04, 0xfb, 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x01, 0x01, - 0x12, 0x04, 0xfb, 0x01, 0x0a, 0x1d, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x00, 0x02, 0x01, - 0x03, 0x12, 0x04, 0xfb, 0x01, 0x20, 0x21, 0x0a, 0x2d, 0x0a, 0x04, 0x04, 0x0e, 0x03, 0x01, 0x12, - 0x06, 0xff, 0x01, 0x02, 0x81, 0x02, 0x03, 0x1a, 0x1d, 0x20, 0x41, 0x6e, 0x20, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x61, 0x73, 0x20, 0x72, 0x65, - 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x03, 0x01, 0x01, 0x12, - 0x04, 0xff, 0x01, 0x0a, 0x23, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0e, 0x03, 0x01, 0x02, 0x00, 0x12, - 0x04, 0x80, 0x02, 0x04, 0x1f, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x01, 0x02, 0x00, 0x05, - 0x12, 0x04, 0x80, 0x02, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x01, 0x02, 0x00, - 0x01, 0x12, 0x04, 0x80, 0x02, 0x0a, 0x1a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x01, 0x02, - 0x00, 0x03, 0x12, 0x04, 0x80, 0x02, 0x1d, 0x1e, 0x0a, 0x38, 0x0a, 0x04, 0x04, 0x0e, 0x03, 0x02, - 0x12, 0x06, 0x84, 0x02, 0x02, 0x8a, 0x02, 0x03, 0x1a, 0x28, 0x20, 0x41, 0x20, 0x77, 0x72, 0x61, - 0x70, 0x70, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x61, 0x6c, 0x6c, 0x65, - 0x74, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x03, 0x02, 0x01, 0x12, 0x04, 0x84, 0x02, 0x0a, - 0x10, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0e, 0x03, 0x02, 0x02, 0x00, 0x12, 0x04, 0x85, 0x02, 0x04, - 0x1c, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x02, 0x02, 0x00, 0x05, 0x12, 0x04, 0x85, 0x02, - 0x04, 0x0a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x02, 0x02, 0x00, 0x01, 0x12, 0x04, 0x85, - 0x02, 0x0b, 0x17, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x02, 0x02, 0x00, 0x03, 0x12, 0x04, - 0x85, 0x02, 0x1a, 0x1b, 0x0a, 0x10, 0x0a, 0x06, 0x04, 0x0e, 0x03, 0x02, 0x08, 0x00, 0x12, 0x06, - 0x86, 0x02, 0x04, 0x89, 0x02, 0x05, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x02, 0x08, 0x00, - 0x01, 0x12, 0x04, 0x86, 0x02, 0x0a, 0x0e, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0e, 0x03, 0x02, 0x02, - 0x01, 0x12, 0x04, 0x87, 0x02, 0x06, 0x31, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x02, 0x02, - 0x01, 0x06, 0x12, 0x04, 0x87, 0x02, 0x06, 0x1b, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x02, - 0x02, 0x01, 0x01, 0x12, 0x04, 0x87, 0x02, 0x1c, 0x2c, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, - 0x02, 0x02, 0x01, 0x03, 0x12, 0x04, 0x87, 0x02, 0x2f, 0x30, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0e, - 0x03, 0x02, 0x02, 0x02, 0x12, 0x04, 0x88, 0x02, 0x06, 0x39, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, - 0x03, 0x02, 0x02, 0x02, 0x06, 0x12, 0x04, 0x88, 0x02, 0x06, 0x1f, 0x0a, 0x0f, 0x0a, 0x07, 0x04, - 0x0e, 0x03, 0x02, 0x02, 0x02, 0x01, 0x12, 0x04, 0x88, 0x02, 0x20, 0x34, 0x0a, 0x0f, 0x0a, 0x07, - 0x04, 0x0e, 0x03, 0x02, 0x02, 0x02, 0x03, 0x12, 0x04, 0x88, 0x02, 0x37, 0x38, 0x0a, 0x3f, 0x0a, - 0x04, 0x04, 0x0e, 0x03, 0x03, 0x12, 0x06, 0x8d, 0x02, 0x02, 0x8f, 0x02, 0x03, 0x1a, 0x2f, 0x20, - 0x41, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, - 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x0a, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x0e, 0x03, 0x03, 0x01, 0x12, 0x04, 0x8d, 0x02, 0x0a, 0x17, 0x0a, 0x0e, 0x0a, - 0x06, 0x04, 0x0e, 0x03, 0x03, 0x02, 0x00, 0x12, 0x04, 0x8e, 0x02, 0x04, 0x20, 0x0a, 0x0f, 0x0a, - 0x07, 0x04, 0x0e, 0x03, 0x03, 0x02, 0x00, 0x04, 0x12, 0x04, 0x8e, 0x02, 0x04, 0x0c, 0x0a, 0x0f, - 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x03, 0x02, 0x00, 0x06, 0x12, 0x04, 0x8e, 0x02, 0x0d, 0x13, 0x0a, - 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x03, 0x02, 0x00, 0x01, 0x12, 0x04, 0x8e, 0x02, 0x14, 0x1b, - 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0e, 0x03, 0x03, 0x02, 0x00, 0x03, 0x12, 0x04, 0x8e, 0x02, 0x1e, - 0x1f, 0x0a, 0x68, 0x0a, 0x04, 0x04, 0x0e, 0x02, 0x00, 0x12, 0x04, 0x93, 0x02, 0x02, 0x25, 0x1a, - 0x5a, 0x20, 0x41, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x73, 0x20, 0x28, 0x6f, 0x72, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x6f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x73, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x20, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x73, 0x29, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x72, 0x69, 0x67, - 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x0a, 0x20, 0x6f, 0x66, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, - 0x0e, 0x02, 0x00, 0x04, 0x12, 0x04, 0x93, 0x02, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, - 0x02, 0x00, 0x06, 0x12, 0x04, 0x93, 0x02, 0x0b, 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, - 0x00, 0x01, 0x12, 0x04, 0x93, 0x02, 0x19, 0x20, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, - 0x03, 0x12, 0x04, 0x93, 0x02, 0x23, 0x24, 0x0a, 0x2a, 0x0a, 0x02, 0x05, 0x00, 0x12, 0x06, 0x97, - 0x02, 0x00, 0x9b, 0x02, 0x01, 0x1a, 0x1c, 0x20, 0x53, 0x6f, 0x72, 0x74, 0x20, 0x64, 0x69, 0x72, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x71, 0x75, 0x65, 0x72, 0x69, - 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x05, 0x00, 0x01, 0x12, 0x04, 0x97, 0x02, 0x05, 0x12, - 0x0a, 0x0c, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x00, 0x12, 0x04, 0x98, 0x02, 0x02, 0x21, 0x0a, 0x0d, - 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0x98, 0x02, 0x02, 0x1c, 0x0a, 0x0d, 0x0a, - 0x05, 0x05, 0x00, 0x02, 0x00, 0x02, 0x12, 0x04, 0x98, 0x02, 0x1f, 0x20, 0x0a, 0x0c, 0x0a, 0x04, - 0x05, 0x00, 0x02, 0x01, 0x12, 0x04, 0x99, 0x02, 0x02, 0x1f, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x00, - 0x02, 0x01, 0x01, 0x12, 0x04, 0x99, 0x02, 0x02, 0x1a, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x00, 0x02, - 0x01, 0x02, 0x12, 0x04, 0x99, 0x02, 0x1d, 0x1e, 0x0a, 0x0c, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x02, - 0x12, 0x04, 0x9a, 0x02, 0x02, 0x20, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x01, 0x12, - 0x04, 0x9a, 0x02, 0x02, 0x1b, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x02, 0x12, 0x04, - 0x9a, 0x02, 0x1e, 0x1f, 0x0a, 0x2d, 0x0a, 0x02, 0x04, 0x0f, 0x12, 0x06, 0x9e, 0x02, 0x00, 0xa2, - 0x02, 0x01, 0x1a, 0x1f, 0x20, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x71, 0x75, 0x65, 0x72, 0x69, - 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0f, 0x01, 0x12, 0x04, 0x9e, 0x02, 0x08, 0x12, - 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0f, 0x02, 0x00, 0x12, 0x04, 0x9f, 0x02, 0x02, 0x1e, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x00, 0x06, 0x12, 0x04, 0x9f, 0x02, 0x02, 0x0f, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x0f, 0x02, 0x00, 0x01, 0x12, 0x04, 0x9f, 0x02, 0x10, 0x19, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x0f, 0x02, 0x00, 0x03, 0x12, 0x04, 0x9f, 0x02, 0x1c, 0x1d, 0x0a, 0x0c, 0x0a, 0x04, 0x04, - 0x0f, 0x02, 0x01, 0x12, 0x04, 0xa0, 0x02, 0x02, 0x13, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, - 0x01, 0x05, 0x12, 0x04, 0xa0, 0x02, 0x02, 0x08, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x01, - 0x01, 0x12, 0x04, 0xa0, 0x02, 0x09, 0x0e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x01, 0x03, - 0x12, 0x04, 0xa0, 0x02, 0x11, 0x12, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0f, 0x02, 0x02, 0x12, 0x04, - 0xa1, 0x02, 0x02, 0x17, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x02, 0x05, 0x12, 0x04, 0xa1, - 0x02, 0x02, 0x08, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x02, 0x01, 0x12, 0x04, 0xa1, 0x02, - 0x09, 0x12, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x02, 0x03, 0x12, 0x04, 0xa1, 0x02, 0x15, - 0x16, 0x0a, 0x31, 0x0a, 0x02, 0x04, 0x10, 0x12, 0x06, 0xa5, 0x02, 0x00, 0xa8, 0x02, 0x01, 0x1a, - 0x23, 0x20, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x71, 0x75, 0x65, 0x72, - 0x69, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x10, 0x01, 0x12, 0x04, 0xa5, 0x02, 0x08, - 0x21, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x10, 0x02, 0x00, 0x12, 0x04, 0xa6, 0x02, 0x02, 0x15, 0x0a, - 0x0d, 0x0a, 0x05, 0x04, 0x10, 0x02, 0x00, 0x05, 0x12, 0x04, 0xa6, 0x02, 0x02, 0x07, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x10, 0x02, 0x00, 0x01, 0x12, 0x04, 0xa6, 0x02, 0x08, 0x10, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x10, 0x02, 0x00, 0x03, 0x12, 0x04, 0xa6, 0x02, 0x13, 0x14, 0x0a, 0x0c, 0x0a, 0x04, - 0x04, 0x10, 0x02, 0x01, 0x12, 0x04, 0xa7, 0x02, 0x02, 0x1d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x10, - 0x02, 0x01, 0x06, 0x12, 0x04, 0xa7, 0x02, 0x02, 0x0c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x10, 0x02, - 0x01, 0x01, 0x12, 0x04, 0xa7, 0x02, 0x0d, 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x10, 0x02, 0x01, - 0x03, 0x12, 0x04, 0xa7, 0x02, 0x1b, 0x1c, 0x0a, 0x32, 0x0a, 0x02, 0x04, 0x11, 0x12, 0x06, 0xab, - 0x02, 0x00, 0xae, 0x02, 0x01, 0x1a, 0x24, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x20, 0x66, 0x6f, 0x72, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x20, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, - 0x11, 0x01, 0x12, 0x04, 0xab, 0x02, 0x08, 0x22, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x11, 0x02, 0x00, - 0x12, 0x04, 0xac, 0x02, 0x02, 0x25, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x00, 0x04, 0x12, - 0x04, 0xac, 0x02, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x00, 0x06, 0x12, 0x04, - 0xac, 0x02, 0x0b, 0x17, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x00, 0x01, 0x12, 0x04, 0xac, - 0x02, 0x18, 0x20, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x00, 0x03, 0x12, 0x04, 0xac, 0x02, - 0x23, 0x24, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x11, 0x02, 0x01, 0x12, 0x04, 0xad, 0x02, 0x02, 0x1d, - 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x01, 0x06, 0x12, 0x04, 0xad, 0x02, 0x02, 0x0c, 0x0a, - 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x01, 0x01, 0x12, 0x04, 0xad, 0x02, 0x0d, 0x18, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x11, 0x02, 0x01, 0x03, 0x12, 0x04, 0xad, 0x02, 0x1b, 0x1c, 0x0a, 0x33, 0x0a, - 0x02, 0x04, 0x12, 0x12, 0x06, 0xb1, 0x02, 0x00, 0xb4, 0x02, 0x01, 0x1a, 0x25, 0x20, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, - 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, - 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x12, 0x01, 0x12, 0x04, 0xb1, 0x02, 0x08, 0x23, 0x0a, - 0x0c, 0x0a, 0x04, 0x04, 0x12, 0x02, 0x00, 0x12, 0x04, 0xb2, 0x02, 0x02, 0x1d, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x12, 0x02, 0x00, 0x05, 0x12, 0x04, 0xb2, 0x02, 0x02, 0x07, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x12, 0x02, 0x00, 0x01, 0x12, 0x04, 0xb2, 0x02, 0x08, 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, - 0x12, 0x02, 0x00, 0x03, 0x12, 0x04, 0xb2, 0x02, 0x1b, 0x1c, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x12, - 0x02, 0x01, 0x12, 0x04, 0xb3, 0x02, 0x02, 0x1d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x12, 0x02, 0x01, - 0x06, 0x12, 0x04, 0xb3, 0x02, 0x02, 0x0c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x12, 0x02, 0x01, 0x01, - 0x12, 0x04, 0xb3, 0x02, 0x0d, 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x12, 0x02, 0x01, 0x03, 0x12, - 0x04, 0xb3, 0x02, 0x1b, 0x1c, 0x0a, 0x34, 0x0a, 0x02, 0x04, 0x13, 0x12, 0x06, 0xb7, 0x02, 0x00, - 0xba, 0x02, 0x01, 0x1a, 0x26, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x66, - 0x6f, 0x72, 0x20, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x20, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, - 0x13, 0x01, 0x12, 0x04, 0xb7, 0x02, 0x08, 0x24, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x13, 0x02, 0x00, - 0x12, 0x04, 0xb8, 0x02, 0x02, 0x27, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x13, 0x02, 0x00, 0x04, 0x12, - 0x04, 0xb8, 0x02, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x13, 0x02, 0x00, 0x06, 0x12, 0x04, - 0xb8, 0x02, 0x0b, 0x19, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x13, 0x02, 0x00, 0x01, 0x12, 0x04, 0xb8, - 0x02, 0x1a, 0x22, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x13, 0x02, 0x00, 0x03, 0x12, 0x04, 0xb8, 0x02, - 0x25, 0x26, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x13, 0x02, 0x01, 0x12, 0x04, 0xb9, 0x02, 0x02, 0x1d, - 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x13, 0x02, 0x01, 0x06, 0x12, 0x04, 0xb9, 0x02, 0x02, 0x0c, 0x0a, - 0x0d, 0x0a, 0x05, 0x04, 0x13, 0x02, 0x01, 0x01, 0x12, 0x04, 0xb9, 0x02, 0x0d, 0x18, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x13, 0x02, 0x01, 0x03, 0x12, 0x04, 0xb9, 0x02, 0x1b, 0x1c, 0x0a, 0x39, 0x0a, - 0x02, 0x04, 0x14, 0x12, 0x06, 0xbd, 0x02, 0x00, 0xc4, 0x02, 0x01, 0x1a, 0x2b, 0x20, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x62, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x14, 0x01, 0x12, - 0x04, 0xbd, 0x02, 0x08, 0x25, 0x0a, 0x25, 0x0a, 0x04, 0x04, 0x14, 0x03, 0x00, 0x12, 0x06, 0xbf, - 0x02, 0x02, 0xc2, 0x02, 0x03, 0x1a, 0x15, 0x20, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x14, 0x03, 0x00, 0x01, 0x12, 0x04, 0xbf, 0x02, 0x0a, 0x10, 0x0a, 0x0e, 0x0a, 0x06, 0x04, - 0x14, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0xc0, 0x02, 0x04, 0x17, 0x0a, 0x0f, 0x0a, 0x07, 0x04, - 0x14, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0xc0, 0x02, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, - 0x04, 0x14, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xc0, 0x02, 0x0a, 0x12, 0x0a, 0x0f, 0x0a, - 0x07, 0x04, 0x14, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0xc0, 0x02, 0x15, 0x16, 0x0a, 0x0e, - 0x0a, 0x06, 0x04, 0x14, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, 0xc1, 0x02, 0x04, 0x19, 0x0a, 0x0f, - 0x0a, 0x07, 0x04, 0x14, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x04, 0xc1, 0x02, 0x04, 0x0a, 0x0a, - 0x0f, 0x0a, 0x07, 0x04, 0x14, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0xc1, 0x02, 0x0b, 0x14, - 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x14, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, 0xc1, 0x02, 0x17, - 0x18, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x14, 0x02, 0x00, 0x12, 0x04, 0xc3, 0x02, 0x02, 0x1e, 0x0a, - 0x0d, 0x0a, 0x05, 0x04, 0x14, 0x02, 0x00, 0x04, 0x12, 0x04, 0xc3, 0x02, 0x02, 0x0a, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x14, 0x02, 0x00, 0x06, 0x12, 0x04, 0xc3, 0x02, 0x0b, 0x11, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x14, 0x02, 0x00, 0x01, 0x12, 0x04, 0xc3, 0x02, 0x12, 0x19, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x14, 0x02, 0x00, 0x03, 0x12, 0x04, 0xc3, 0x02, 0x1c, 0x1d, 0x0a, 0x3b, 0x0a, 0x02, 0x04, - 0x15, 0x12, 0x06, 0xc7, 0x02, 0x00, 0xce, 0x02, 0x01, 0x1a, 0x2d, 0x20, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, - 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x15, 0x01, 0x12, - 0x04, 0xc7, 0x02, 0x08, 0x27, 0x0a, 0x25, 0x0a, 0x04, 0x04, 0x15, 0x03, 0x00, 0x12, 0x06, 0xc9, - 0x02, 0x02, 0xcc, 0x02, 0x03, 0x1a, 0x15, 0x20, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x15, 0x03, 0x00, 0x01, 0x12, 0x04, 0xc9, 0x02, 0x0a, 0x10, 0x0a, 0x0e, 0x0a, 0x06, 0x04, - 0x15, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0xca, 0x02, 0x04, 0x1f, 0x0a, 0x0f, 0x0a, 0x07, 0x04, - 0x15, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0xca, 0x02, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, - 0x04, 0x15, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xca, 0x02, 0x0a, 0x1a, 0x0a, 0x0f, 0x0a, - 0x07, 0x04, 0x15, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0xca, 0x02, 0x1d, 0x1e, 0x0a, 0x0e, - 0x0a, 0x06, 0x04, 0x15, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, 0xcb, 0x02, 0x04, 0x19, 0x0a, 0x0f, - 0x0a, 0x07, 0x04, 0x15, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x04, 0xcb, 0x02, 0x04, 0x0a, 0x0a, - 0x0f, 0x0a, 0x07, 0x04, 0x15, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0xcb, 0x02, 0x0b, 0x14, - 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x15, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, 0xcb, 0x02, 0x17, - 0x18, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x15, 0x02, 0x00, 0x12, 0x04, 0xcd, 0x02, 0x02, 0x1e, 0x0a, - 0x0d, 0x0a, 0x05, 0x04, 0x15, 0x02, 0x00, 0x04, 0x12, 0x04, 0xcd, 0x02, 0x02, 0x0a, 0x0a, 0x0d, - 0x0a, 0x05, 0x04, 0x15, 0x02, 0x00, 0x06, 0x12, 0x04, 0xcd, 0x02, 0x0b, 0x11, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x15, 0x02, 0x00, 0x01, 0x12, 0x04, 0xcd, 0x02, 0x12, 0x19, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x15, 0x02, 0x00, 0x03, 0x12, 0x04, 0xcd, 0x02, 0x1c, 0x1d, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x41, 0x50, 0x49, 0x0a, 0x20, + 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x6e, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x65, 0x61, 0x63, 0x68, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, + 0x68, 0x20, 0x65, 0x61, 0x63, 0x68, 0x0a, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6b, 0x65, 0x79, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x08, 0x02, + 0x00, 0x04, 0x12, 0x04, 0xb0, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, + 0x05, 0x12, 0x04, 0xb0, 0x01, 0x0b, 0x10, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x01, + 0x12, 0x04, 0xb0, 0x01, 0x11, 0x22, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x03, 0x12, + 0x04, 0xb0, 0x01, 0x25, 0x26, 0x0a, 0x39, 0x0a, 0x02, 0x04, 0x09, 0x12, 0x06, 0xb4, 0x01, 0x00, + 0xbe, 0x01, 0x01, 0x1a, 0x2b, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x46, 0x65, 0x74, 0x63, 0x68, 0x4b, 0x65, 0x79, + 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, + 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x09, 0x01, 0x12, 0x04, 0xb4, 0x01, 0x08, 0x20, 0x0a, 0x2b, 0x0a, + 0x04, 0x04, 0x09, 0x03, 0x00, 0x12, 0x06, 0xb6, 0x01, 0x02, 0xb8, 0x01, 0x03, 0x1a, 0x1b, 0x20, + 0x41, 0x6e, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x20, 0x6b, 0x65, + 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x09, + 0x03, 0x00, 0x01, 0x12, 0x04, 0xb6, 0x01, 0x0a, 0x14, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x09, 0x03, + 0x00, 0x02, 0x00, 0x12, 0x04, 0xb7, 0x01, 0x04, 0x29, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x09, 0x03, + 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0xb7, 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x09, + 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xb7, 0x01, 0x0a, 0x24, 0x0a, 0x0f, 0x0a, 0x07, 0x04, + 0x09, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0xb7, 0x01, 0x27, 0x28, 0x0a, 0xcf, 0x01, 0x0a, + 0x04, 0x04, 0x09, 0x02, 0x00, 0x12, 0x04, 0xbd, 0x01, 0x02, 0x27, 0x1a, 0xc0, 0x01, 0x20, 0x52, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, + 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x20, 0x70, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, + 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x20, 0x49, + 0x66, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, + 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x2c, 0x20, 0x61, 0x6e, + 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x69, 0x73, 0x0a, + 0x20, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x69, 0x72, 0x20, 0x72, + 0x65, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, 0x73, 0x70, 0x6f, 0x74, 0x73, 0x20, + 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x2e, 0x0a, 0x0a, 0x0d, + 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x04, 0x12, 0x04, 0xbd, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, + 0x05, 0x04, 0x09, 0x02, 0x00, 0x06, 0x12, 0x04, 0xbd, 0x01, 0x0b, 0x15, 0x0a, 0x0d, 0x0a, 0x05, + 0x04, 0x09, 0x02, 0x00, 0x01, 0x12, 0x04, 0xbd, 0x01, 0x16, 0x22, 0x0a, 0x0d, 0x0a, 0x05, 0x04, + 0x09, 0x02, 0x00, 0x03, 0x12, 0x04, 0xbd, 0x01, 0x25, 0x26, 0x0a, 0x2a, 0x0a, 0x02, 0x05, 0x00, + 0x12, 0x06, 0xc1, 0x01, 0x00, 0xc5, 0x01, 0x01, 0x1a, 0x1c, 0x20, 0x53, 0x6f, 0x72, 0x74, 0x20, + 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x71, 0x75, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x05, 0x00, 0x01, 0x12, 0x04, 0xc1, + 0x01, 0x05, 0x12, 0x0a, 0x0c, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x00, 0x12, 0x04, 0xc2, 0x01, 0x02, + 0x21, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xc2, 0x01, 0x02, 0x1c, + 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x02, 0x12, 0x04, 0xc2, 0x01, 0x1f, 0x20, 0x0a, + 0x0c, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x01, 0x12, 0x04, 0xc3, 0x01, 0x02, 0x1f, 0x0a, 0x0d, 0x0a, + 0x05, 0x05, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0xc3, 0x01, 0x02, 0x1a, 0x0a, 0x0d, 0x0a, 0x05, + 0x05, 0x00, 0x02, 0x01, 0x02, 0x12, 0x04, 0xc3, 0x01, 0x1d, 0x1e, 0x0a, 0x0c, 0x0a, 0x04, 0x05, + 0x00, 0x02, 0x02, 0x12, 0x04, 0xc4, 0x01, 0x02, 0x20, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x00, 0x02, + 0x02, 0x01, 0x12, 0x04, 0xc4, 0x01, 0x02, 0x1b, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, + 0x02, 0x12, 0x04, 0xc4, 0x01, 0x1e, 0x1f, 0x0a, 0x2d, 0x0a, 0x02, 0x04, 0x0a, 0x12, 0x06, 0xc8, + 0x01, 0x00, 0xcc, 0x01, 0x01, 0x1a, 0x1f, 0x20, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x71, 0x75, + 0x65, 0x72, 0x69, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0a, 0x01, 0x12, 0x04, 0xc8, + 0x01, 0x08, 0x12, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0a, 0x02, 0x00, 0x12, 0x04, 0xc9, 0x01, 0x02, + 0x1e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x06, 0x12, 0x04, 0xc9, 0x01, 0x02, 0x0f, + 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x01, 0x12, 0x04, 0xc9, 0x01, 0x10, 0x19, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x03, 0x12, 0x04, 0xc9, 0x01, 0x1c, 0x1d, 0x0a, 0x0c, + 0x0a, 0x04, 0x04, 0x0a, 0x02, 0x01, 0x12, 0x04, 0xca, 0x01, 0x02, 0x13, 0x0a, 0x0d, 0x0a, 0x05, + 0x04, 0x0a, 0x02, 0x01, 0x05, 0x12, 0x04, 0xca, 0x01, 0x02, 0x08, 0x0a, 0x0d, 0x0a, 0x05, 0x04, + 0x0a, 0x02, 0x01, 0x01, 0x12, 0x04, 0xca, 0x01, 0x09, 0x0e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0a, + 0x02, 0x01, 0x03, 0x12, 0x04, 0xca, 0x01, 0x11, 0x12, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0a, 0x02, + 0x02, 0x12, 0x04, 0xcb, 0x01, 0x02, 0x17, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x02, 0x05, + 0x12, 0x04, 0xcb, 0x01, 0x02, 0x08, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x02, 0x01, 0x12, + 0x04, 0xcb, 0x01, 0x09, 0x12, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x02, 0x03, 0x12, 0x04, + 0xcb, 0x01, 0x15, 0x16, 0x0a, 0x31, 0x0a, 0x02, 0x04, 0x0b, 0x12, 0x06, 0xcf, 0x01, 0x00, 0xd2, + 0x01, 0x01, 0x1a, 0x23, 0x20, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x71, + 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0b, 0x01, 0x12, 0x04, + 0xcf, 0x01, 0x08, 0x21, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x00, 0x12, 0x04, 0xd0, 0x01, + 0x02, 0x15, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x05, 0x12, 0x04, 0xd0, 0x01, 0x02, + 0x07, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x01, 0x12, 0x04, 0xd0, 0x01, 0x08, 0x10, + 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x03, 0x12, 0x04, 0xd0, 0x01, 0x13, 0x14, 0x0a, + 0x0c, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x01, 0x12, 0x04, 0xd1, 0x01, 0x02, 0x1d, 0x0a, 0x0d, 0x0a, + 0x05, 0x04, 0x0b, 0x02, 0x01, 0x06, 0x12, 0x04, 0xd1, 0x01, 0x02, 0x0c, 0x0a, 0x0d, 0x0a, 0x05, + 0x04, 0x0b, 0x02, 0x01, 0x01, 0x12, 0x04, 0xd1, 0x01, 0x0d, 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, + 0x0b, 0x02, 0x01, 0x03, 0x12, 0x04, 0xd1, 0x01, 0x1b, 0x1c, 0x0a, 0x32, 0x0a, 0x02, 0x04, 0x0c, + 0x12, 0x06, 0xd5, 0x01, 0x00, 0xd8, 0x01, 0x01, 0x1a, 0x24, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x0a, 0x0a, 0x0b, + 0x0a, 0x03, 0x04, 0x0c, 0x01, 0x12, 0x04, 0xd5, 0x01, 0x08, 0x22, 0x0a, 0x0c, 0x0a, 0x04, 0x04, + 0x0c, 0x02, 0x00, 0x12, 0x04, 0xd6, 0x01, 0x02, 0x25, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, + 0x00, 0x04, 0x12, 0x04, 0xd6, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, + 0x06, 0x12, 0x04, 0xd6, 0x01, 0x0b, 0x17, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x01, + 0x12, 0x04, 0xd6, 0x01, 0x18, 0x20, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x03, 0x12, + 0x04, 0xd6, 0x01, 0x23, 0x24, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0c, 0x02, 0x01, 0x12, 0x04, 0xd7, + 0x01, 0x02, 0x1d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x01, 0x06, 0x12, 0x04, 0xd7, 0x01, + 0x02, 0x0c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x01, 0x01, 0x12, 0x04, 0xd7, 0x01, 0x0d, + 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x01, 0x03, 0x12, 0x04, 0xd7, 0x01, 0x1b, 0x1c, + 0x0a, 0x33, 0x0a, 0x02, 0x04, 0x0d, 0x12, 0x06, 0xdb, 0x01, 0x00, 0xde, 0x01, 0x01, 0x1a, 0x25, + 0x20, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x77, 0x65, 0x6c, + 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x71, 0x75, 0x65, + 0x72, 0x69, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x0d, 0x01, 0x12, 0x04, 0xdb, 0x01, + 0x08, 0x23, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0d, 0x02, 0x00, 0x12, 0x04, 0xdc, 0x01, 0x02, 0x1d, + 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x00, 0x05, 0x12, 0x04, 0xdc, 0x01, 0x02, 0x07, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x00, 0x01, 0x12, 0x04, 0xdc, 0x01, 0x08, 0x18, 0x0a, 0x0d, + 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x00, 0x03, 0x12, 0x04, 0xdc, 0x01, 0x1b, 0x1c, 0x0a, 0x0c, 0x0a, + 0x04, 0x04, 0x0d, 0x02, 0x01, 0x12, 0x04, 0xdd, 0x01, 0x02, 0x1d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, + 0x0d, 0x02, 0x01, 0x06, 0x12, 0x04, 0xdd, 0x01, 0x02, 0x0c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0d, + 0x02, 0x01, 0x01, 0x12, 0x04, 0xdd, 0x01, 0x0d, 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0d, 0x02, + 0x01, 0x03, 0x12, 0x04, 0xdd, 0x01, 0x1b, 0x1c, 0x0a, 0x34, 0x0a, 0x02, 0x04, 0x0e, 0x12, 0x06, + 0xe1, 0x01, 0x00, 0xe4, 0x01, 0x01, 0x1a, 0x26, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x0a, 0x0a, 0x0b, + 0x0a, 0x03, 0x04, 0x0e, 0x01, 0x12, 0x04, 0xe1, 0x01, 0x08, 0x24, 0x0a, 0x0c, 0x0a, 0x04, 0x04, + 0x0e, 0x02, 0x00, 0x12, 0x04, 0xe2, 0x01, 0x02, 0x27, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, + 0x00, 0x04, 0x12, 0x04, 0xe2, 0x01, 0x02, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, + 0x06, 0x12, 0x04, 0xe2, 0x01, 0x0b, 0x19, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x01, + 0x12, 0x04, 0xe2, 0x01, 0x1a, 0x22, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x03, 0x12, + 0x04, 0xe2, 0x01, 0x25, 0x26, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0e, 0x02, 0x01, 0x12, 0x04, 0xe3, + 0x01, 0x02, 0x1d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x01, 0x06, 0x12, 0x04, 0xe3, 0x01, + 0x02, 0x0c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x01, 0x01, 0x12, 0x04, 0xe3, 0x01, 0x0d, + 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x01, 0x03, 0x12, 0x04, 0xe3, 0x01, 0x1b, 0x1c, + 0x0a, 0x39, 0x0a, 0x02, 0x04, 0x0f, 0x12, 0x06, 0xe7, 0x01, 0x00, 0xee, 0x01, 0x01, 0x1a, 0x2b, + 0x20, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x73, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, + 0x0f, 0x01, 0x12, 0x04, 0xe7, 0x01, 0x08, 0x25, 0x0a, 0x25, 0x0a, 0x04, 0x04, 0x0f, 0x03, 0x00, + 0x12, 0x06, 0xe9, 0x01, 0x02, 0xec, 0x01, 0x03, 0x1a, 0x15, 0x20, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x0a, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x03, 0x00, 0x01, 0x12, 0x04, 0xe9, 0x01, 0x0a, 0x10, 0x0a, 0x0e, + 0x0a, 0x06, 0x04, 0x0f, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0xea, 0x01, 0x04, 0x17, 0x0a, 0x0f, + 0x0a, 0x07, 0x04, 0x0f, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0xea, 0x01, 0x04, 0x09, 0x0a, + 0x0f, 0x0a, 0x07, 0x04, 0x0f, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xea, 0x01, 0x0a, 0x12, + 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0f, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0xea, 0x01, 0x15, + 0x16, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0f, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, 0xeb, 0x01, 0x04, + 0x19, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0f, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x04, 0xeb, 0x01, + 0x04, 0x0a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0f, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0xeb, + 0x01, 0x0b, 0x14, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0f, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, + 0xeb, 0x01, 0x17, 0x18, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x0f, 0x02, 0x00, 0x12, 0x04, 0xed, 0x01, + 0x02, 0x1e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x00, 0x04, 0x12, 0x04, 0xed, 0x01, 0x02, + 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x00, 0x06, 0x12, 0x04, 0xed, 0x01, 0x0b, 0x11, + 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x00, 0x01, 0x12, 0x04, 0xed, 0x01, 0x12, 0x19, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x00, 0x03, 0x12, 0x04, 0xed, 0x01, 0x1c, 0x1d, 0x0a, 0x3b, + 0x0a, 0x02, 0x04, 0x10, 0x12, 0x06, 0xf1, 0x01, 0x00, 0xf8, 0x01, 0x01, 0x1a, 0x2d, 0x20, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, + 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, 0x03, 0x04, + 0x10, 0x01, 0x12, 0x04, 0xf1, 0x01, 0x08, 0x27, 0x0a, 0x25, 0x0a, 0x04, 0x04, 0x10, 0x03, 0x00, + 0x12, 0x06, 0xf3, 0x01, 0x02, 0xf6, 0x01, 0x03, 0x1a, 0x15, 0x20, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x0a, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x10, 0x03, 0x00, 0x01, 0x12, 0x04, 0xf3, 0x01, 0x0a, 0x10, 0x0a, 0x0e, + 0x0a, 0x06, 0x04, 0x10, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0xf4, 0x01, 0x04, 0x1f, 0x0a, 0x0f, + 0x0a, 0x07, 0x04, 0x10, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0xf4, 0x01, 0x04, 0x09, 0x0a, + 0x0f, 0x0a, 0x07, 0x04, 0x10, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0xf4, 0x01, 0x0a, 0x1a, + 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x10, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0xf4, 0x01, 0x1d, + 0x1e, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x10, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, 0xf5, 0x01, 0x04, + 0x19, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x10, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x04, 0xf5, 0x01, + 0x04, 0x0a, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x10, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0xf5, + 0x01, 0x0b, 0x14, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x10, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, + 0xf5, 0x01, 0x17, 0x18, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x10, 0x02, 0x00, 0x12, 0x04, 0xf7, 0x01, + 0x02, 0x1e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x10, 0x02, 0x00, 0x04, 0x12, 0x04, 0xf7, 0x01, 0x02, + 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x10, 0x02, 0x00, 0x06, 0x12, 0x04, 0xf7, 0x01, 0x0b, 0x11, + 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x10, 0x02, 0x00, 0x01, 0x12, 0x04, 0xf7, 0x01, 0x12, 0x19, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x10, 0x02, 0x00, 0x03, 0x12, 0x04, 0xf7, 0x01, 0x1c, 0x1d, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, ]; include!("xmtp.mls.api.v1.serde.rs"); include!("xmtp.mls.api.v1.tonic.rs"); diff --git a/xmtp_proto/src/gen/xmtp.mls.api.v1.serde.rs b/xmtp_proto/src/gen/xmtp.mls.api.v1.serde.rs index 93a7ceaed..d30819a97 100644 --- a/xmtp_proto/src/gen/xmtp.mls.api.v1.serde.rs +++ b/xmtp_proto/src/gen/xmtp.mls.api.v1.serde.rs @@ -200,6 +200,7 @@ impl serde::Serialize for fetch_key_packages_response::KeyPackage { let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.FetchKeyPackagesResponse.KeyPackage", len)?; if !self.key_package_tls_serialized.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("keyPackageTlsSerialized", pbjson::private::base64::encode(&self.key_package_tls_serialized).as_str())?; } struct_ser.end() @@ -281,7 +282,7 @@ impl<'de> serde::Deserialize<'de> for fetch_key_packages_response::KeyPackage { deserializer.deserialize_struct("xmtp.mls.api.v1.FetchKeyPackagesResponse.KeyPackage", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for GetIdentityUpdatesRequest { +impl serde::Serialize for GroupMessage { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -289,40 +290,33 @@ impl serde::Serialize for GetIdentityUpdatesRequest { { use serde::ser::SerializeStruct; let mut len = 0; - if !self.account_addresses.is_empty() { - len += 1; - } - if self.start_time_ns != 0 { + if self.version.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.GetIdentityUpdatesRequest", len)?; - if !self.account_addresses.is_empty() { - struct_ser.serialize_field("accountAddresses", &self.account_addresses)?; - } - if self.start_time_ns != 0 { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("startTimeNs", ToString::to_string(&self.start_time_ns).as_str())?; + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.GroupMessage", len)?; + if let Some(v) = self.version.as_ref() { + match v { + group_message::Version::V1(v) => { + struct_ser.serialize_field("v1", v)?; + } + } } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for GetIdentityUpdatesRequest { +impl<'de> serde::Deserialize<'de> for GroupMessage { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "account_addresses", - "accountAddresses", - "start_time_ns", - "startTimeNs", + "v1", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - AccountAddresses, - StartTimeNs, + V1, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -344,8 +338,7 @@ impl<'de> serde::Deserialize<'de> for GetIdentityUpdatesRequest { E: serde::de::Error, { match value { - "accountAddresses" | "account_addresses" => Ok(GeneratedField::AccountAddresses), - "startTimeNs" | "start_time_ns" => Ok(GeneratedField::StartTimeNs), + "v1" => Ok(GeneratedField::V1), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -355,46 +348,37 @@ impl<'de> serde::Deserialize<'de> for GetIdentityUpdatesRequest { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GetIdentityUpdatesRequest; + type Value = GroupMessage; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.GetIdentityUpdatesRequest") + formatter.write_str("struct xmtp.mls.api.v1.GroupMessage") } - 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 account_addresses__ = None; - let mut start_time_ns__ = None; + let mut version__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::AccountAddresses => { - if account_addresses__.is_some() { - return Err(serde::de::Error::duplicate_field("accountAddresses")); - } - account_addresses__ = Some(map_.next_value()?); - } - GeneratedField::StartTimeNs => { - if start_time_ns__.is_some() { - return Err(serde::de::Error::duplicate_field("startTimeNs")); + GeneratedField::V1 => { + if version__.is_some() { + return Err(serde::de::Error::duplicate_field("v1")); } - start_time_ns__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; + version__ = map_.next_value::<::std::option::Option<_>>()?.map(group_message::Version::V1) +; } } } - Ok(GetIdentityUpdatesRequest { - account_addresses: account_addresses__.unwrap_or_default(), - start_time_ns: start_time_ns__.unwrap_or_default(), + Ok(GroupMessage { + version: version__, }) } } - deserializer.deserialize_struct("xmtp.mls.api.v1.GetIdentityUpdatesRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls.api.v1.GroupMessage", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for GetIdentityUpdatesResponse { +impl serde::Serialize for group_message::V1 { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -402,29 +386,74 @@ impl serde::Serialize for GetIdentityUpdatesResponse { { use serde::ser::SerializeStruct; let mut len = 0; - if !self.updates.is_empty() { + if self.id != 0 { + len += 1; + } + if self.created_ns != 0 { + len += 1; + } + if !self.group_id.is_empty() { + len += 1; + } + if !self.data.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.GetIdentityUpdatesResponse", len)?; - if !self.updates.is_empty() { - struct_ser.serialize_field("updates", &self.updates)?; + if !self.sender_hmac.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.GroupMessage.V1", len)?; + if self.id != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("id", ToString::to_string(&self.id).as_str())?; + } + if self.created_ns != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("createdNs", ToString::to_string(&self.created_ns).as_str())?; + } + if !self.group_id.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("groupId", pbjson::private::base64::encode(&self.group_id).as_str())?; + } + if !self.data.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("data", pbjson::private::base64::encode(&self.data).as_str())?; + } + if !self.sender_hmac.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("senderHmac", pbjson::private::base64::encode(&self.sender_hmac).as_str())?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for GetIdentityUpdatesResponse { +impl<'de> serde::Deserialize<'de> for group_message::V1 { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "updates", + "id", + "created_ns", + "createdNs", + "group_id", + "groupId", + "data", + "sender_hmac", + "senderHmac", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - Updates, + Id, + CreatedNs, + GroupId, + Data, + SenderHmac, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -446,7 +475,11 @@ impl<'de> serde::Deserialize<'de> for GetIdentityUpdatesResponse { E: serde::de::Error, { match value { - "updates" => Ok(GeneratedField::Updates), + "id" => Ok(GeneratedField::Id), + "createdNs" | "created_ns" => Ok(GeneratedField::CreatedNs), + "groupId" | "group_id" => Ok(GeneratedField::GroupId), + "data" => Ok(GeneratedField::Data), + "senderHmac" | "sender_hmac" => Ok(GeneratedField::SenderHmac), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -456,36 +489,78 @@ impl<'de> serde::Deserialize<'de> for GetIdentityUpdatesResponse { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GetIdentityUpdatesResponse; + type Value = group_message::V1; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.GetIdentityUpdatesResponse") + formatter.write_str("struct xmtp.mls.api.v1.GroupMessage.V1") } - 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 updates__ = None; + let mut id__ = None; + let mut created_ns__ = None; + let mut group_id__ = None; + let mut data__ = None; + let mut sender_hmac__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::Updates => { - if updates__.is_some() { - return Err(serde::de::Error::duplicate_field("updates")); + GeneratedField::Id => { + if id__.is_some() { + return Err(serde::de::Error::duplicate_field("id")); + } + id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::CreatedNs => { + if created_ns__.is_some() { + return Err(serde::de::Error::duplicate_field("createdNs")); + } + created_ns__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + 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::Data => { + if data__.is_some() { + return Err(serde::de::Error::duplicate_field("data")); + } + data__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::SenderHmac => { + if sender_hmac__.is_some() { + return Err(serde::de::Error::duplicate_field("senderHmac")); } - updates__ = Some(map_.next_value()?); + sender_hmac__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; } } } - Ok(GetIdentityUpdatesResponse { - updates: updates__.unwrap_or_default(), + Ok(group_message::V1 { + id: id__.unwrap_or_default(), + created_ns: created_ns__.unwrap_or_default(), + group_id: group_id__.unwrap_or_default(), + data: data__.unwrap_or_default(), + sender_hmac: sender_hmac__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("xmtp.mls.api.v1.GetIdentityUpdatesResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls.api.v1.GroupMessage.V1", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for get_identity_updates_response::NewInstallationUpdate { +impl serde::Serialize for GroupMessageInput { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -493,41 +568,33 @@ impl serde::Serialize for get_identity_updates_response::NewInstallationUpdate { { use serde::ser::SerializeStruct; let mut len = 0; - if !self.installation_key.is_empty() { - len += 1; - } - if !self.credential_identity.is_empty() { + if self.version.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.GetIdentityUpdatesResponse.NewInstallationUpdate", len)?; - if !self.installation_key.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("installationKey", pbjson::private::base64::encode(&self.installation_key).as_str())?; - } - if !self.credential_identity.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("credentialIdentity", pbjson::private::base64::encode(&self.credential_identity).as_str())?; + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.GroupMessageInput", len)?; + if let Some(v) = self.version.as_ref() { + match v { + group_message_input::Version::V1(v) => { + struct_ser.serialize_field("v1", v)?; + } + } } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for get_identity_updates_response::NewInstallationUpdate { +impl<'de> serde::Deserialize<'de> for GroupMessageInput { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "installation_key", - "installationKey", - "credential_identity", - "credentialIdentity", + "v1", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - InstallationKey, - CredentialIdentity, + V1, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -549,8 +616,7 @@ impl<'de> serde::Deserialize<'de> for get_identity_updates_response::NewInstalla E: serde::de::Error, { match value { - "installationKey" | "installation_key" => Ok(GeneratedField::InstallationKey), - "credentialIdentity" | "credential_identity" => Ok(GeneratedField::CredentialIdentity), + "v1" => Ok(GeneratedField::V1), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -560,48 +626,37 @@ impl<'de> serde::Deserialize<'de> for get_identity_updates_response::NewInstalla } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = get_identity_updates_response::NewInstallationUpdate; + type Value = GroupMessageInput; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.GetIdentityUpdatesResponse.NewInstallationUpdate") + formatter.write_str("struct xmtp.mls.api.v1.GroupMessageInput") } - 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 installation_key__ = None; - let mut credential_identity__ = None; + let mut version__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::InstallationKey => { - if installation_key__.is_some() { - return Err(serde::de::Error::duplicate_field("installationKey")); - } - installation_key__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } - GeneratedField::CredentialIdentity => { - if credential_identity__.is_some() { - return Err(serde::de::Error::duplicate_field("credentialIdentity")); + GeneratedField::V1 => { + if version__.is_some() { + return Err(serde::de::Error::duplicate_field("v1")); } - credential_identity__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; + version__ = map_.next_value::<::std::option::Option<_>>()?.map(group_message_input::Version::V1) +; } } } - Ok(get_identity_updates_response::NewInstallationUpdate { - installation_key: installation_key__.unwrap_or_default(), - credential_identity: credential_identity__.unwrap_or_default(), + Ok(GroupMessageInput { + version: version__, }) } } - deserializer.deserialize_struct("xmtp.mls.api.v1.GetIdentityUpdatesResponse.NewInstallationUpdate", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls.api.v1.GroupMessageInput", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for get_identity_updates_response::RevokedInstallationUpdate { +impl serde::Serialize for group_message_input::V1 { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -609,31 +664,42 @@ impl serde::Serialize for get_identity_updates_response::RevokedInstallationUpda { use serde::ser::SerializeStruct; let mut len = 0; - if !self.installation_key.is_empty() { + if !self.data.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.GetIdentityUpdatesResponse.RevokedInstallationUpdate", len)?; - if !self.installation_key.is_empty() { + if !self.sender_hmac.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.GroupMessageInput.V1", len)?; + if !self.data.is_empty() { #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("installationKey", pbjson::private::base64::encode(&self.installation_key).as_str())?; + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("data", pbjson::private::base64::encode(&self.data).as_str())?; + } + if !self.sender_hmac.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("senderHmac", pbjson::private::base64::encode(&self.sender_hmac).as_str())?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for get_identity_updates_response::RevokedInstallationUpdate { +impl<'de> serde::Deserialize<'de> for group_message_input::V1 { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "installation_key", - "installationKey", + "data", + "sender_hmac", + "senderHmac", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - InstallationKey, + Data, + SenderHmac, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -655,7 +721,8 @@ impl<'de> serde::Deserialize<'de> for get_identity_updates_response::RevokedInst E: serde::de::Error, { match value { - "installationKey" | "installation_key" => Ok(GeneratedField::InstallationKey), + "data" => Ok(GeneratedField::Data), + "senderHmac" | "sender_hmac" => Ok(GeneratedField::SenderHmac), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -665,38 +732,48 @@ impl<'de> serde::Deserialize<'de> for get_identity_updates_response::RevokedInst } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = get_identity_updates_response::RevokedInstallationUpdate; + type Value = group_message_input::V1; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.GetIdentityUpdatesResponse.RevokedInstallationUpdate") + formatter.write_str("struct xmtp.mls.api.v1.GroupMessageInput.V1") } - 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 installation_key__ = None; + let mut data__ = None; + let mut sender_hmac__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::InstallationKey => { - if installation_key__.is_some() { - return Err(serde::de::Error::duplicate_field("installationKey")); + GeneratedField::Data => { + if data__.is_some() { + return Err(serde::de::Error::duplicate_field("data")); } - installation_key__ = + data__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::SenderHmac => { + if sender_hmac__.is_some() { + return Err(serde::de::Error::duplicate_field("senderHmac")); + } + sender_hmac__ = Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) ; } } } - Ok(get_identity_updates_response::RevokedInstallationUpdate { - installation_key: installation_key__.unwrap_or_default(), + Ok(group_message_input::V1 { + data: data__.unwrap_or_default(), + sender_hmac: sender_hmac__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("xmtp.mls.api.v1.GetIdentityUpdatesResponse.RevokedInstallationUpdate", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls.api.v1.GroupMessageInput.V1", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for get_identity_updates_response::Update { +impl serde::Serialize for KeyPackageUpload { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -704,975 +781,32 @@ impl serde::Serialize for get_identity_updates_response::Update { { use serde::ser::SerializeStruct; let mut len = 0; - if self.timestamp_ns != 0 { - len += 1; - } - if self.kind.is_some() { + if !self.key_package_tls_serialized.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.GetIdentityUpdatesResponse.Update", len)?; - if self.timestamp_ns != 0 { + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.KeyPackageUpload", len)?; + if !self.key_package_tls_serialized.is_empty() { #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("timestampNs", ToString::to_string(&self.timestamp_ns).as_str())?; - } - if let Some(v) = self.kind.as_ref() { - match v { - get_identity_updates_response::update::Kind::NewInstallation(v) => { - struct_ser.serialize_field("newInstallation", v)?; - } - get_identity_updates_response::update::Kind::RevokedInstallation(v) => { - struct_ser.serialize_field("revokedInstallation", v)?; - } - } + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("keyPackageTlsSerialized", pbjson::private::base64::encode(&self.key_package_tls_serialized).as_str())?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for get_identity_updates_response::Update { +impl<'de> serde::Deserialize<'de> for KeyPackageUpload { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "timestamp_ns", - "timestampNs", - "new_installation", - "newInstallation", - "revoked_installation", - "revokedInstallation", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - TimestampNs, - NewInstallation, - RevokedInstallation, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "timestampNs" | "timestamp_ns" => Ok(GeneratedField::TimestampNs), - "newInstallation" | "new_installation" => Ok(GeneratedField::NewInstallation), - "revokedInstallation" | "revoked_installation" => Ok(GeneratedField::RevokedInstallation), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = get_identity_updates_response::Update; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.GetIdentityUpdatesResponse.Update") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut timestamp_ns__ = None; - let mut kind__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::TimestampNs => { - if timestamp_ns__.is_some() { - return Err(serde::de::Error::duplicate_field("timestampNs")); - } - timestamp_ns__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; - } - GeneratedField::NewInstallation => { - if kind__.is_some() { - return Err(serde::de::Error::duplicate_field("newInstallation")); - } - kind__ = map_.next_value::<::std::option::Option<_>>()?.map(get_identity_updates_response::update::Kind::NewInstallation) -; - } - GeneratedField::RevokedInstallation => { - if kind__.is_some() { - return Err(serde::de::Error::duplicate_field("revokedInstallation")); - } - kind__ = map_.next_value::<::std::option::Option<_>>()?.map(get_identity_updates_response::update::Kind::RevokedInstallation) -; - } - } - } - Ok(get_identity_updates_response::Update { - timestamp_ns: timestamp_ns__.unwrap_or_default(), - kind: kind__, - }) - } - } - deserializer.deserialize_struct("xmtp.mls.api.v1.GetIdentityUpdatesResponse.Update", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for get_identity_updates_response::WalletUpdates { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.updates.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.GetIdentityUpdatesResponse.WalletUpdates", len)?; - if !self.updates.is_empty() { - struct_ser.serialize_field("updates", &self.updates)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for get_identity_updates_response::WalletUpdates { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "updates", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Updates, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "updates" => Ok(GeneratedField::Updates), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = get_identity_updates_response::WalletUpdates; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.GetIdentityUpdatesResponse.WalletUpdates") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut updates__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Updates => { - if updates__.is_some() { - return Err(serde::de::Error::duplicate_field("updates")); - } - updates__ = Some(map_.next_value()?); - } - } - } - Ok(get_identity_updates_response::WalletUpdates { - updates: updates__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("xmtp.mls.api.v1.GetIdentityUpdatesResponse.WalletUpdates", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for GroupMessage { - #[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.GroupMessage", len)?; - if let Some(v) = self.version.as_ref() { - match v { - group_message::Version::V1(v) => { - struct_ser.serialize_field("v1", v)?; - } - } - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for GroupMessage { - #[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, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GroupMessage; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.GroupMessage") - } - - 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_message::Version::V1) -; - } - } - } - Ok(GroupMessage { - version: version__, - }) - } - } - deserializer.deserialize_struct("xmtp.mls.api.v1.GroupMessage", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for group_message::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.id != 0 { - len += 1; - } - if self.created_ns != 0 { - len += 1; - } - if !self.group_id.is_empty() { - len += 1; - } - if !self.data.is_empty() { - len += 1; - } - if !self.sender_hmac.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.GroupMessage.V1", len)?; - if self.id != 0 { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("id", ToString::to_string(&self.id).as_str())?; - } - if self.created_ns != 0 { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("createdNs", ToString::to_string(&self.created_ns).as_str())?; - } - if !self.group_id.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("groupId", pbjson::private::base64::encode(&self.group_id).as_str())?; - } - if !self.data.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("data", pbjson::private::base64::encode(&self.data).as_str())?; - } - if !self.sender_hmac.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("senderHmac", pbjson::private::base64::encode(&self.sender_hmac).as_str())?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for group_message::V1 { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "id", - "created_ns", - "createdNs", - "group_id", - "groupId", - "data", - "sender_hmac", - "senderHmac", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Id, - CreatedNs, - GroupId, - Data, - SenderHmac, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "id" => Ok(GeneratedField::Id), - "createdNs" | "created_ns" => Ok(GeneratedField::CreatedNs), - "groupId" | "group_id" => Ok(GeneratedField::GroupId), - "data" => Ok(GeneratedField::Data), - "senderHmac" | "sender_hmac" => Ok(GeneratedField::SenderHmac), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = group_message::V1; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.GroupMessage.V1") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut id__ = None; - let mut created_ns__ = None; - let mut group_id__ = None; - let mut data__ = None; - let mut sender_hmac__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Id => { - if id__.is_some() { - return Err(serde::de::Error::duplicate_field("id")); - } - id__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; - } - GeneratedField::CreatedNs => { - if created_ns__.is_some() { - return Err(serde::de::Error::duplicate_field("createdNs")); - } - created_ns__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; - } - 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::Data => { - if data__.is_some() { - return Err(serde::de::Error::duplicate_field("data")); - } - data__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } - GeneratedField::SenderHmac => { - if sender_hmac__.is_some() { - return Err(serde::de::Error::duplicate_field("senderHmac")); - } - sender_hmac__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } - } - } - Ok(group_message::V1 { - id: id__.unwrap_or_default(), - created_ns: created_ns__.unwrap_or_default(), - group_id: group_id__.unwrap_or_default(), - data: data__.unwrap_or_default(), - sender_hmac: sender_hmac__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("xmtp.mls.api.v1.GroupMessage.V1", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for GroupMessageInput { - #[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.GroupMessageInput", len)?; - if let Some(v) = self.version.as_ref() { - match v { - group_message_input::Version::V1(v) => { - struct_ser.serialize_field("v1", v)?; - } - } - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for GroupMessageInput { - #[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, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GroupMessageInput; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.GroupMessageInput") - } - - 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_message_input::Version::V1) -; - } - } - } - Ok(GroupMessageInput { - version: version__, - }) - } - } - deserializer.deserialize_struct("xmtp.mls.api.v1.GroupMessageInput", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for group_message_input::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.data.is_empty() { - len += 1; - } - if !self.sender_hmac.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.GroupMessageInput.V1", len)?; - if !self.data.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("data", pbjson::private::base64::encode(&self.data).as_str())?; - } - if !self.sender_hmac.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("senderHmac", pbjson::private::base64::encode(&self.sender_hmac).as_str())?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for group_message_input::V1 { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "data", - "sender_hmac", - "senderHmac", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Data, - SenderHmac, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "data" => Ok(GeneratedField::Data), - "senderHmac" | "sender_hmac" => Ok(GeneratedField::SenderHmac), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = group_message_input::V1; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.GroupMessageInput.V1") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut data__ = None; - let mut sender_hmac__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Data => { - if data__.is_some() { - return Err(serde::de::Error::duplicate_field("data")); - } - data__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } - GeneratedField::SenderHmac => { - if sender_hmac__.is_some() { - return Err(serde::de::Error::duplicate_field("senderHmac")); - } - sender_hmac__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } - } - } - Ok(group_message_input::V1 { - data: data__.unwrap_or_default(), - sender_hmac: sender_hmac__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("xmtp.mls.api.v1.GroupMessageInput.V1", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for KeyPackageUpload { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.key_package_tls_serialized.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.KeyPackageUpload", len)?; - if !self.key_package_tls_serialized.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("keyPackageTlsSerialized", pbjson::private::base64::encode(&self.key_package_tls_serialized).as_str())?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for KeyPackageUpload { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "key_package_tls_serialized", - "keyPackageTlsSerialized", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - KeyPackageTlsSerialized, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "keyPackageTlsSerialized" | "key_package_tls_serialized" => Ok(GeneratedField::KeyPackageTlsSerialized), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = KeyPackageUpload; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.KeyPackageUpload") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut key_package_tls_serialized__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::KeyPackageTlsSerialized => { - if key_package_tls_serialized__.is_some() { - return Err(serde::de::Error::duplicate_field("keyPackageTlsSerialized")); - } - key_package_tls_serialized__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } - } - } - Ok(KeyPackageUpload { - key_package_tls_serialized: key_package_tls_serialized__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("xmtp.mls.api.v1.KeyPackageUpload", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for PagingInfo { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.direction != 0 { - len += 1; - } - if self.limit != 0 { - len += 1; - } - if self.id_cursor != 0 { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.PagingInfo", len)?; - if self.direction != 0 { - let v = SortDirection::try_from(self.direction) - .map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", self.direction)))?; - struct_ser.serialize_field("direction", &v)?; - } - if self.limit != 0 { - struct_ser.serialize_field("limit", &self.limit)?; - } - if self.id_cursor != 0 { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("idCursor", ToString::to_string(&self.id_cursor).as_str())?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for PagingInfo { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "direction", - "limit", - "id_cursor", - "idCursor", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Direction, - Limit, - IdCursor, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "direction" => Ok(GeneratedField::Direction), - "limit" => Ok(GeneratedField::Limit), - "idCursor" | "id_cursor" => Ok(GeneratedField::IdCursor), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = PagingInfo; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.PagingInfo") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut direction__ = None; - let mut limit__ = None; - let mut id_cursor__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Direction => { - if direction__.is_some() { - return Err(serde::de::Error::duplicate_field("direction")); - } - direction__ = Some(map_.next_value::()? as i32); - } - GeneratedField::Limit => { - if limit__.is_some() { - return Err(serde::de::Error::duplicate_field("limit")); - } - limit__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.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) - ; - } - } - } - Ok(PagingInfo { - direction: direction__.unwrap_or_default(), - limit: limit__.unwrap_or_default(), - id_cursor: id_cursor__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("xmtp.mls.api.v1.PagingInfo", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for QueryGroupMessagesRequest { - #[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; - } - if self.paging_info.is_some() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.QueryGroupMessagesRequest", len)?; - if !self.group_id.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("groupId", pbjson::private::base64::encode(&self.group_id).as_str())?; - } - if let Some(v) = self.paging_info.as_ref() { - struct_ser.serialize_field("pagingInfo", v)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for QueryGroupMessagesRequest { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "group_id", - "groupId", - "paging_info", - "pagingInfo", + "key_package_tls_serialized", + "keyPackageTlsSerialized", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - GroupId, - PagingInfo, + KeyPackageTlsSerialized, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -1694,8 +828,7 @@ impl<'de> serde::Deserialize<'de> for QueryGroupMessagesRequest { E: serde::de::Error, { match value { - "groupId" | "group_id" => Ok(GeneratedField::GroupId), - "pagingInfo" | "paging_info" => Ok(GeneratedField::PagingInfo), + "keyPackageTlsSerialized" | "key_package_tls_serialized" => Ok(GeneratedField::KeyPackageTlsSerialized), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -1705,46 +838,38 @@ impl<'de> serde::Deserialize<'de> for QueryGroupMessagesRequest { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = QueryGroupMessagesRequest; + type Value = KeyPackageUpload; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.QueryGroupMessagesRequest") + formatter.write_str("struct xmtp.mls.api.v1.KeyPackageUpload") } - 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 group_id__ = None; - let mut paging_info__ = None; + let mut key_package_tls_serialized__ = 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")); + GeneratedField::KeyPackageTlsSerialized => { + if key_package_tls_serialized__.is_some() { + return Err(serde::de::Error::duplicate_field("keyPackageTlsSerialized")); } - group_id__ = + key_package_tls_serialized__ = Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) ; } - GeneratedField::PagingInfo => { - if paging_info__.is_some() { - return Err(serde::de::Error::duplicate_field("pagingInfo")); - } - paging_info__ = map_.next_value()?; - } } } - Ok(QueryGroupMessagesRequest { - group_id: group_id__.unwrap_or_default(), - paging_info: paging_info__, + Ok(KeyPackageUpload { + key_package_tls_serialized: key_package_tls_serialized__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("xmtp.mls.api.v1.QueryGroupMessagesRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls.api.v1.KeyPackageUpload", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for QueryGroupMessagesResponse { +impl serde::Serialize for PagingInfo { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -1752,149 +877,50 @@ impl serde::Serialize for QueryGroupMessagesResponse { { use serde::ser::SerializeStruct; let mut len = 0; - if !self.messages.is_empty() { + if self.direction != 0 { len += 1; } - if self.paging_info.is_some() { + if self.limit != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.QueryGroupMessagesResponse", len)?; - if !self.messages.is_empty() { - struct_ser.serialize_field("messages", &self.messages)?; - } - if let Some(v) = self.paging_info.as_ref() { - struct_ser.serialize_field("pagingInfo", v)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for QueryGroupMessagesResponse { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "messages", - "paging_info", - "pagingInfo", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Messages, - PagingInfo, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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), - "pagingInfo" | "paging_info" => Ok(GeneratedField::PagingInfo), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = QueryGroupMessagesResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.QueryGroupMessagesResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut messages__ = None; - let mut paging_info__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Messages => { - if messages__.is_some() { - return Err(serde::de::Error::duplicate_field("messages")); - } - messages__ = Some(map_.next_value()?); - } - GeneratedField::PagingInfo => { - if paging_info__.is_some() { - return Err(serde::de::Error::duplicate_field("pagingInfo")); - } - paging_info__ = map_.next_value()?; - } - } - } - Ok(QueryGroupMessagesResponse { - messages: messages__.unwrap_or_default(), - paging_info: paging_info__, - }) - } - } - deserializer.deserialize_struct("xmtp.mls.api.v1.QueryGroupMessagesResponse", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for QueryWelcomeMessagesRequest { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.installation_key.is_empty() { + if self.id_cursor != 0 { len += 1; } - if self.paging_info.is_some() { - len += 1; + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.PagingInfo", len)?; + if self.direction != 0 { + let v = SortDirection::try_from(self.direction) + .map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", self.direction)))?; + struct_ser.serialize_field("direction", &v)?; + } + if self.limit != 0 { + struct_ser.serialize_field("limit", &self.limit)?; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.QueryWelcomeMessagesRequest", len)?; - if !self.installation_key.is_empty() { + if self.id_cursor != 0 { #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("installationKey", pbjson::private::base64::encode(&self.installation_key).as_str())?; - } - if let Some(v) = self.paging_info.as_ref() { - struct_ser.serialize_field("pagingInfo", v)?; + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("idCursor", ToString::to_string(&self.id_cursor).as_str())?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for QueryWelcomeMessagesRequest { +impl<'de> serde::Deserialize<'de> for PagingInfo { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "installation_key", - "installationKey", - "paging_info", - "pagingInfo", + "direction", + "limit", + "id_cursor", + "idCursor", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - InstallationKey, - PagingInfo, + Direction, + Limit, + IdCursor, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -1916,8 +942,9 @@ impl<'de> serde::Deserialize<'de> for QueryWelcomeMessagesRequest { E: serde::de::Error, { match value { - "installationKey" | "installation_key" => Ok(GeneratedField::InstallationKey), - "pagingInfo" | "paging_info" => Ok(GeneratedField::PagingInfo), + "direction" => Ok(GeneratedField::Direction), + "limit" => Ok(GeneratedField::Limit), + "idCursor" | "id_cursor" => Ok(GeneratedField::IdCursor), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -1927,46 +954,56 @@ impl<'de> serde::Deserialize<'de> for QueryWelcomeMessagesRequest { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = QueryWelcomeMessagesRequest; + type Value = PagingInfo; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.QueryWelcomeMessagesRequest") + formatter.write_str("struct xmtp.mls.api.v1.PagingInfo") } - 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 installation_key__ = None; - let mut paging_info__ = None; + let mut direction__ = None; + let mut limit__ = None; + let mut id_cursor__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::InstallationKey => { - if installation_key__.is_some() { - return Err(serde::de::Error::duplicate_field("installationKey")); + GeneratedField::Direction => { + if direction__.is_some() { + return Err(serde::de::Error::duplicate_field("direction")); } - installation_key__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + direction__ = Some(map_.next_value::()? as i32); + } + GeneratedField::Limit => { + if limit__.is_some() { + return Err(serde::de::Error::duplicate_field("limit")); + } + limit__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) ; } - GeneratedField::PagingInfo => { - if paging_info__.is_some() { - return Err(serde::de::Error::duplicate_field("pagingInfo")); + GeneratedField::IdCursor => { + if id_cursor__.is_some() { + return Err(serde::de::Error::duplicate_field("idCursor")); } - paging_info__ = map_.next_value()?; + id_cursor__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; } } } - Ok(QueryWelcomeMessagesRequest { - installation_key: installation_key__.unwrap_or_default(), - paging_info: paging_info__, + Ok(PagingInfo { + direction: direction__.unwrap_or_default(), + limit: limit__.unwrap_or_default(), + id_cursor: id_cursor__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("xmtp.mls.api.v1.QueryWelcomeMessagesRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls.api.v1.PagingInfo", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for QueryWelcomeMessagesResponse { +impl serde::Serialize for QueryGroupMessagesRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -1974,15 +1011,17 @@ impl serde::Serialize for QueryWelcomeMessagesResponse { { use serde::ser::SerializeStruct; let mut len = 0; - if !self.messages.is_empty() { + if !self.group_id.is_empty() { len += 1; } if self.paging_info.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.QueryWelcomeMessagesResponse", len)?; - if !self.messages.is_empty() { - struct_ser.serialize_field("messages", &self.messages)?; + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.QueryGroupMessagesRequest", len)?; + if !self.group_id.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("groupId", pbjson::private::base64::encode(&self.group_id).as_str())?; } if let Some(v) = self.paging_info.as_ref() { struct_ser.serialize_field("pagingInfo", v)?; @@ -1990,21 +1029,22 @@ impl serde::Serialize for QueryWelcomeMessagesResponse { struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for QueryWelcomeMessagesResponse { +impl<'de> serde::Deserialize<'de> for QueryGroupMessagesRequest { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "messages", + "group_id", + "groupId", "paging_info", "pagingInfo", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - Messages, + GroupId, PagingInfo, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -2027,7 +1067,7 @@ impl<'de> serde::Deserialize<'de> for QueryWelcomeMessagesResponse { E: serde::de::Error, { match value { - "messages" => Ok(GeneratedField::Messages), + "groupId" | "group_id" => Ok(GeneratedField::GroupId), "pagingInfo" | "paging_info" => Ok(GeneratedField::PagingInfo), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } @@ -2038,25 +1078,27 @@ impl<'de> serde::Deserialize<'de> for QueryWelcomeMessagesResponse { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = QueryWelcomeMessagesResponse; + type Value = QueryGroupMessagesRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.QueryWelcomeMessagesResponse") + formatter.write_str("struct xmtp.mls.api.v1.QueryGroupMessagesRequest") } - 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 messages__ = None; + let mut group_id__ = None; let mut paging_info__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::Messages => { - if messages__.is_some() { - return Err(serde::de::Error::duplicate_field("messages")); + GeneratedField::GroupId => { + if group_id__.is_some() { + return Err(serde::de::Error::duplicate_field("groupId")); } - messages__ = Some(map_.next_value()?); + group_id__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; } GeneratedField::PagingInfo => { if paging_info__.is_some() { @@ -2066,16 +1108,16 @@ impl<'de> serde::Deserialize<'de> for QueryWelcomeMessagesResponse { } } } - Ok(QueryWelcomeMessagesResponse { - messages: messages__.unwrap_or_default(), + Ok(QueryGroupMessagesRequest { + group_id: group_id__.unwrap_or_default(), paging_info: paging_info__, }) } } - deserializer.deserialize_struct("xmtp.mls.api.v1.QueryWelcomeMessagesResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls.api.v1.QueryGroupMessagesRequest", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for RegisterInstallationRequest { +impl serde::Serialize for QueryGroupMessagesResponse { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -2083,39 +1125,38 @@ impl serde::Serialize for RegisterInstallationRequest { { use serde::ser::SerializeStruct; let mut len = 0; - if self.key_package.is_some() { + if !self.messages.is_empty() { len += 1; } - if self.is_inbox_id_credential { + if self.paging_info.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.RegisterInstallationRequest", len)?; - if let Some(v) = self.key_package.as_ref() { - struct_ser.serialize_field("keyPackage", v)?; + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.QueryGroupMessagesResponse", len)?; + if !self.messages.is_empty() { + struct_ser.serialize_field("messages", &self.messages)?; } - if self.is_inbox_id_credential { - struct_ser.serialize_field("isInboxIdCredential", &self.is_inbox_id_credential)?; + if let Some(v) = self.paging_info.as_ref() { + struct_ser.serialize_field("pagingInfo", v)?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for RegisterInstallationRequest { +impl<'de> serde::Deserialize<'de> for QueryGroupMessagesResponse { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "key_package", - "keyPackage", - "is_inbox_id_credential", - "isInboxIdCredential", + "messages", + "paging_info", + "pagingInfo", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - KeyPackage, - IsInboxIdCredential, + Messages, + PagingInfo, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -2137,8 +1178,8 @@ impl<'de> serde::Deserialize<'de> for RegisterInstallationRequest { E: serde::de::Error, { match value { - "keyPackage" | "key_package" => Ok(GeneratedField::KeyPackage), - "isInboxIdCredential" | "is_inbox_id_credential" => Ok(GeneratedField::IsInboxIdCredential), + "messages" => Ok(GeneratedField::Messages), + "pagingInfo" | "paging_info" => Ok(GeneratedField::PagingInfo), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -2148,44 +1189,44 @@ impl<'de> serde::Deserialize<'de> for RegisterInstallationRequest { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = RegisterInstallationRequest; + type Value = QueryGroupMessagesResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.RegisterInstallationRequest") + formatter.write_str("struct xmtp.mls.api.v1.QueryGroupMessagesResponse") } - 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 key_package__ = None; - let mut is_inbox_id_credential__ = None; + let mut messages__ = None; + let mut paging_info__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::KeyPackage => { - if key_package__.is_some() { - return Err(serde::de::Error::duplicate_field("keyPackage")); + GeneratedField::Messages => { + if messages__.is_some() { + return Err(serde::de::Error::duplicate_field("messages")); } - key_package__ = map_.next_value()?; + messages__ = Some(map_.next_value()?); } - GeneratedField::IsInboxIdCredential => { - if is_inbox_id_credential__.is_some() { - return Err(serde::de::Error::duplicate_field("isInboxIdCredential")); + GeneratedField::PagingInfo => { + if paging_info__.is_some() { + return Err(serde::de::Error::duplicate_field("pagingInfo")); } - is_inbox_id_credential__ = Some(map_.next_value()?); + paging_info__ = map_.next_value()?; } } } - Ok(RegisterInstallationRequest { - key_package: key_package__, - is_inbox_id_credential: is_inbox_id_credential__.unwrap_or_default(), + Ok(QueryGroupMessagesResponse { + messages: messages__.unwrap_or_default(), + paging_info: paging_info__, }) } } - deserializer.deserialize_struct("xmtp.mls.api.v1.RegisterInstallationRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls.api.v1.QueryGroupMessagesResponse", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for RegisterInstallationResponse { +impl serde::Serialize for QueryWelcomeMessagesRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -2196,15 +1237,22 @@ impl serde::Serialize for RegisterInstallationResponse { if !self.installation_key.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.RegisterInstallationResponse", len)?; + if self.paging_info.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.QueryWelcomeMessagesRequest", len)?; if !self.installation_key.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("installationKey", pbjson::private::base64::encode(&self.installation_key).as_str())?; } + if let Some(v) = self.paging_info.as_ref() { + struct_ser.serialize_field("pagingInfo", v)?; + } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for RegisterInstallationResponse { +impl<'de> serde::Deserialize<'de> for QueryWelcomeMessagesRequest { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -2213,11 +1261,14 @@ impl<'de> serde::Deserialize<'de> for RegisterInstallationResponse { const FIELDS: &[&str] = &[ "installation_key", "installationKey", + "paging_info", + "pagingInfo", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { InstallationKey, + PagingInfo, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -2240,6 +1291,7 @@ impl<'de> serde::Deserialize<'de> for RegisterInstallationResponse { { match value { "installationKey" | "installation_key" => Ok(GeneratedField::InstallationKey), + "pagingInfo" | "paging_info" => Ok(GeneratedField::PagingInfo), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -2249,17 +1301,18 @@ impl<'de> serde::Deserialize<'de> for RegisterInstallationResponse { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = RegisterInstallationResponse; + type Value = QueryWelcomeMessagesRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.RegisterInstallationResponse") + formatter.write_str("struct xmtp.mls.api.v1.QueryWelcomeMessagesRequest") } - 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 installation_key__ = None; + let mut paging_info__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::InstallationKey => { @@ -2270,17 +1323,24 @@ impl<'de> serde::Deserialize<'de> for RegisterInstallationResponse { Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) ; } + GeneratedField::PagingInfo => { + if paging_info__.is_some() { + return Err(serde::de::Error::duplicate_field("pagingInfo")); + } + paging_info__ = map_.next_value()?; + } } } - Ok(RegisterInstallationResponse { + Ok(QueryWelcomeMessagesRequest { installation_key: installation_key__.unwrap_or_default(), + paging_info: paging_info__, }) } } - deserializer.deserialize_struct("xmtp.mls.api.v1.RegisterInstallationResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls.api.v1.QueryWelcomeMessagesRequest", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for RevokeInstallationRequest { +impl serde::Serialize for QueryWelcomeMessagesResponse { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -2288,40 +1348,38 @@ impl serde::Serialize for RevokeInstallationRequest { { use serde::ser::SerializeStruct; let mut len = 0; - if !self.installation_key.is_empty() { + if !self.messages.is_empty() { len += 1; } - if self.wallet_signature.is_some() { + if self.paging_info.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.RevokeInstallationRequest", len)?; - if !self.installation_key.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("installationKey", pbjson::private::base64::encode(&self.installation_key).as_str())?; + let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.QueryWelcomeMessagesResponse", len)?; + if !self.messages.is_empty() { + struct_ser.serialize_field("messages", &self.messages)?; } - if let Some(v) = self.wallet_signature.as_ref() { - struct_ser.serialize_field("walletSignature", v)?; + if let Some(v) = self.paging_info.as_ref() { + struct_ser.serialize_field("pagingInfo", v)?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for RevokeInstallationRequest { +impl<'de> serde::Deserialize<'de> for QueryWelcomeMessagesResponse { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "installation_key", - "installationKey", - "wallet_signature", - "walletSignature", + "messages", + "paging_info", + "pagingInfo", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - InstallationKey, - WalletSignature, + Messages, + PagingInfo, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -2343,8 +1401,8 @@ impl<'de> serde::Deserialize<'de> for RevokeInstallationRequest { E: serde::de::Error, { match value { - "installationKey" | "installation_key" => Ok(GeneratedField::InstallationKey), - "walletSignature" | "wallet_signature" => Ok(GeneratedField::WalletSignature), + "messages" => Ok(GeneratedField::Messages), + "pagingInfo" | "paging_info" => Ok(GeneratedField::PagingInfo), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -2354,43 +1412,41 @@ impl<'de> serde::Deserialize<'de> for RevokeInstallationRequest { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = RevokeInstallationRequest; + type Value = QueryWelcomeMessagesResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.api.v1.RevokeInstallationRequest") + formatter.write_str("struct xmtp.mls.api.v1.QueryWelcomeMessagesResponse") } - 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 installation_key__ = None; - let mut wallet_signature__ = None; + let mut messages__ = None; + let mut paging_info__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::InstallationKey => { - if installation_key__.is_some() { - return Err(serde::de::Error::duplicate_field("installationKey")); + GeneratedField::Messages => { + if messages__.is_some() { + return Err(serde::de::Error::duplicate_field("messages")); } - installation_key__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; + messages__ = Some(map_.next_value()?); } - GeneratedField::WalletSignature => { - if wallet_signature__.is_some() { - return Err(serde::de::Error::duplicate_field("walletSignature")); + GeneratedField::PagingInfo => { + if paging_info__.is_some() { + return Err(serde::de::Error::duplicate_field("pagingInfo")); } - wallet_signature__ = map_.next_value()?; + paging_info__ = map_.next_value()?; } } } - Ok(RevokeInstallationRequest { - installation_key: installation_key__.unwrap_or_default(), - wallet_signature: wallet_signature__, + Ok(QueryWelcomeMessagesResponse { + messages: messages__.unwrap_or_default(), + paging_info: paging_info__, }) } } - deserializer.deserialize_struct("xmtp.mls.api.v1.RevokeInstallationRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls.api.v1.QueryWelcomeMessagesResponse", FIELDS, GeneratedVisitor) } } impl serde::Serialize for SendGroupMessagesRequest { @@ -2757,10 +1813,12 @@ impl serde::Serialize for subscribe_group_messages_request::Filter { let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.SubscribeGroupMessagesRequest.Filter", len)?; if !self.group_id.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("groupId", pbjson::private::base64::encode(&self.group_id).as_str())?; } if self.id_cursor != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("idCursor", ToString::to_string(&self.id_cursor).as_str())?; } struct_ser.end() @@ -2964,10 +2022,12 @@ impl serde::Serialize for subscribe_welcome_messages_request::Filter { let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.SubscribeWelcomeMessagesRequest.Filter", len)?; if !self.installation_key.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("installationKey", pbjson::private::base64::encode(&self.installation_key).as_str())?; } if self.id_cursor != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("idCursor", ToString::to_string(&self.id_cursor).as_str())?; } struct_ser.end() @@ -3295,22 +2355,27 @@ impl serde::Serialize for welcome_message::V1 { let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.WelcomeMessage.V1", len)?; if self.id != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("id", ToString::to_string(&self.id).as_str())?; } if self.created_ns != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("createdNs", ToString::to_string(&self.created_ns).as_str())?; } if !self.installation_key.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("installationKey", pbjson::private::base64::encode(&self.installation_key).as_str())?; } if !self.data.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("data", pbjson::private::base64::encode(&self.data).as_str())?; } if !self.hpke_public_key.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("hpkePublicKey", pbjson::private::base64::encode(&self.hpke_public_key).as_str())?; } struct_ser.end() @@ -3562,14 +2627,17 @@ impl serde::Serialize for welcome_message_input::V1 { let mut struct_ser = serializer.serialize_struct("xmtp.mls.api.v1.WelcomeMessageInput.V1", len)?; if !self.installation_key.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("installationKey", pbjson::private::base64::encode(&self.installation_key).as_str())?; } if !self.data.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("data", pbjson::private::base64::encode(&self.data).as_str())?; } if !self.hpke_public_key.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("hpkePublicKey", pbjson::private::base64::encode(&self.hpke_public_key).as_str())?; } struct_ser.end() diff --git a/xmtp_proto/src/gen/xmtp.mls.api.v1.tonic.rs b/xmtp_proto/src/gen/xmtp.mls.api.v1.tonic.rs index 9afd5d202..a0b3f5d45 100644 --- a/xmtp_proto/src/gen/xmtp.mls.api.v1.tonic.rs +++ b/xmtp_proto/src/gen/xmtp.mls.api.v1.tonic.rs @@ -1,6 +1,6 @@ // @generated /// Generated client implementations. -#[cfg(feature = "tonic")] +#[cfg(not(target_arch = "wasm32"))] pub mod mls_api_client { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] use tonic::codegen::*; @@ -131,33 +131,6 @@ pub mod mls_api_client { ); self.inner.unary(req, path, codec).await } - pub async fn register_installation( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/xmtp.mls.api.v1.MlsApi/RegisterInstallation", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new("xmtp.mls.api.v1.MlsApi", "RegisterInstallation"), - ); - self.inner.unary(req, path, codec).await - } pub async fn upload_key_package( &mut self, request: impl tonic::IntoRequest, @@ -205,53 +178,6 @@ pub mod mls_api_client { .insert(GrpcMethod::new("xmtp.mls.api.v1.MlsApi", "FetchKeyPackages")); self.inner.unary(req, path, codec).await } - pub async fn revoke_installation( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result, tonic::Status> { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/xmtp.mls.api.v1.MlsApi/RevokeInstallation", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert(GrpcMethod::new("xmtp.mls.api.v1.MlsApi", "RevokeInstallation")); - self.inner.unary(req, path, codec).await - } - pub async fn get_identity_updates( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/xmtp.mls.api.v1.MlsApi/GetIdentityUpdates", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert(GrpcMethod::new("xmtp.mls.api.v1.MlsApi", "GetIdentityUpdates")); - self.inner.unary(req, path, codec).await - } pub async fn query_group_messages( &mut self, request: impl tonic::IntoRequest, @@ -361,7 +287,7 @@ pub mod mls_api_client { } } /// Generated server implementations. -#[cfg(feature = "tonic")] +#[cfg(not(target_arch = "wasm32"))] pub mod mls_api_server { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] use tonic::codegen::*; @@ -376,13 +302,6 @@ pub mod mls_api_server { &self, request: tonic::Request, ) -> std::result::Result, tonic::Status>; - async fn register_installation( - &self, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; async fn upload_key_package( &self, request: tonic::Request, @@ -394,17 +313,6 @@ pub mod mls_api_server { tonic::Response, tonic::Status, >; - async fn revoke_installation( - &self, - request: tonic::Request, - ) -> std::result::Result, tonic::Status>; - async fn get_identity_updates( - &self, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; async fn query_group_messages( &self, request: tonic::Request, @@ -448,19 +356,17 @@ pub mod mls_api_server { } #[derive(Debug)] pub struct MlsApiServer { - inner: _Inner, + inner: Arc, accept_compression_encodings: EnabledCompressionEncodings, send_compression_encodings: EnabledCompressionEncodings, max_decoding_message_size: Option, max_encoding_message_size: Option, } - struct _Inner(Arc); impl MlsApiServer { pub fn new(inner: T) -> Self { Self::from_arc(Arc::new(inner)) } pub fn from_arc(inner: Arc) -> Self { - let inner = _Inner(inner); Self { inner, accept_compression_encodings: Default::default(), @@ -523,7 +429,6 @@ pub mod mls_api_server { Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { - let inner = self.inner.clone(); match req.uri().path() { "/xmtp.mls.api.v1.MlsApi/SendGroupMessages" => { #[allow(non_camel_case_types)] @@ -554,7 +459,6 @@ pub mod mls_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = SendGroupMessagesSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -600,7 +504,6 @@ pub mod mls_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = SendWelcomeMessagesSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -617,52 +520,6 @@ pub mod mls_api_server { }; Box::pin(fut) } - "/xmtp.mls.api.v1.MlsApi/RegisterInstallation" => { - #[allow(non_camel_case_types)] - struct RegisterInstallationSvc(pub Arc); - impl< - T: MlsApi, - > tonic::server::UnaryService - for RegisterInstallationSvc { - type Response = super::RegisterInstallationResponse; - 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 { - ::register_installation(&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 inner = inner.0; - let method = RegisterInstallationSvc(inner); - let codec = tonic::codec::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.mls.api.v1.MlsApi/UploadKeyPackage" => { #[allow(non_camel_case_types)] struct UploadKeyPackageSvc(pub Arc); @@ -692,7 +549,6 @@ pub mod mls_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = UploadKeyPackageSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -738,7 +594,6 @@ pub mod mls_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = FetchKeyPackagesSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -755,98 +610,6 @@ pub mod mls_api_server { }; Box::pin(fut) } - "/xmtp.mls.api.v1.MlsApi/RevokeInstallation" => { - #[allow(non_camel_case_types)] - struct RevokeInstallationSvc(pub Arc); - impl< - T: MlsApi, - > tonic::server::UnaryService - for RevokeInstallationSvc { - type Response = ::pbjson_types::Empty; - 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 { - ::revoke_installation(&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 inner = inner.0; - let method = RevokeInstallationSvc(inner); - let codec = tonic::codec::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.mls.api.v1.MlsApi/GetIdentityUpdates" => { - #[allow(non_camel_case_types)] - struct GetIdentityUpdatesSvc(pub Arc); - impl< - T: MlsApi, - > tonic::server::UnaryService - for GetIdentityUpdatesSvc { - type Response = super::GetIdentityUpdatesResponse; - 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_identity_updates(&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 inner = inner.0; - let method = GetIdentityUpdatesSvc(inner); - let codec = tonic::codec::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.mls.api.v1.MlsApi/QueryGroupMessages" => { #[allow(non_camel_case_types)] struct QueryGroupMessagesSvc(pub Arc); @@ -876,7 +639,6 @@ pub mod mls_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = QueryGroupMessagesSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -922,7 +684,6 @@ pub mod mls_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = QueryWelcomeMessagesSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -971,7 +732,6 @@ pub mod mls_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = SubscribeGroupMessagesSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -1022,7 +782,6 @@ pub mod mls_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = SubscribeWelcomeMessagesSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -1044,8 +803,11 @@ pub mod mls_api_server { Ok( http::Response::builder() .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") + .header("grpc-status", tonic::Code::Unimplemented as i32) + .header( + http::header::CONTENT_TYPE, + tonic::metadata::GRPC_CONTENT_TYPE, + ) .body(empty_body()) .unwrap(), ) @@ -1066,16 +828,6 @@ pub mod mls_api_server { } } } - impl Clone for _Inner { - fn clone(&self) -> Self { - Self(Arc::clone(&self.0)) - } - } - impl std::fmt::Debug for _Inner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - } - } impl tonic::server::NamedService for MlsApiServer { const NAME: &'static str = "xmtp.mls.api.v1.MlsApi"; } diff --git a/xmtp_proto/src/gen/xmtp.mls.database.rs b/xmtp_proto/src/gen/xmtp.mls.database.rs index f32b8ea36..d78b25e87 100644 --- a/xmtp_proto/src/gen/xmtp.mls.database.rs +++ b/xmtp_proto/src/gen/xmtp.mls.database.rs @@ -1,4 +1,5 @@ // @generated +// This file is @generated by prost-build. /// The data required to publish a message #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -178,6 +179,34 @@ pub mod update_admin_lists_data { V1(V1), } } +/// The data required to update permissions +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UpdatePermissionData { + #[prost(oneof="update_permission_data::Version", tags="1")] + pub version: ::core::option::Option, +} +/// Nested message and enum types in `UpdatePermissionData`. +pub mod update_permission_data { + /// V1 of UpdatePermissionData + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct V1 { + #[prost(enumeration="super::PermissionUpdateType", tag="1")] + pub permission_update_type: i32, + #[prost(enumeration="super::PermissionPolicyOption", tag="2")] + pub permission_policy_option: i32, + /// Metadata permissions update specify which field permission they are updating + #[prost(string, optional, tag="3")] + pub metadata_field_name: ::core::option::Option<::prost::alloc::string::String>, + } + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Version { + #[prost(message, tag="1")] + V1(V1), + } +} /// Generic data-type for all post-commit actions #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -248,9 +277,84 @@ impl AdminListUpdateType { } } } +/// Type of Permission to Update +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum PermissionUpdateType { + Unspecified = 0, + AddMember = 1, + RemoveMember = 2, + AddAdmin = 3, + RemoveAdmin = 4, + UpdateMetadata = 5, +} +impl PermissionUpdateType { + /// 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 { + PermissionUpdateType::Unspecified => "PERMISSION_UPDATE_TYPE_UNSPECIFIED", + PermissionUpdateType::AddMember => "PERMISSION_UPDATE_TYPE_ADD_MEMBER", + PermissionUpdateType::RemoveMember => "PERMISSION_UPDATE_TYPE_REMOVE_MEMBER", + PermissionUpdateType::AddAdmin => "PERMISSION_UPDATE_TYPE_ADD_ADMIN", + PermissionUpdateType::RemoveAdmin => "PERMISSION_UPDATE_TYPE_REMOVE_ADMIN", + PermissionUpdateType::UpdateMetadata => "PERMISSION_UPDATE_TYPE_UPDATE_METADATA", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "PERMISSION_UPDATE_TYPE_UNSPECIFIED" => Some(Self::Unspecified), + "PERMISSION_UPDATE_TYPE_ADD_MEMBER" => Some(Self::AddMember), + "PERMISSION_UPDATE_TYPE_REMOVE_MEMBER" => Some(Self::RemoveMember), + "PERMISSION_UPDATE_TYPE_ADD_ADMIN" => Some(Self::AddAdmin), + "PERMISSION_UPDATE_TYPE_REMOVE_ADMIN" => Some(Self::RemoveAdmin), + "PERMISSION_UPDATE_TYPE_UPDATE_METADATA" => Some(Self::UpdateMetadata), + _ => None, + } + } +} +/// Permission Policy +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum PermissionPolicyOption { + Unspecified = 0, + Allow = 1, + Deny = 2, + AdminOnly = 3, + SuperAdminOnly = 4, +} +impl PermissionPolicyOption { + /// 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 { + PermissionPolicyOption::Unspecified => "PERMISSION_POLICY_OPTION_UNSPECIFIED", + PermissionPolicyOption::Allow => "PERMISSION_POLICY_OPTION_ALLOW", + PermissionPolicyOption::Deny => "PERMISSION_POLICY_OPTION_DENY", + PermissionPolicyOption::AdminOnly => "PERMISSION_POLICY_OPTION_ADMIN_ONLY", + PermissionPolicyOption::SuperAdminOnly => "PERMISSION_POLICY_OPTION_SUPER_ADMIN_ONLY", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "PERMISSION_POLICY_OPTION_UNSPECIFIED" => Some(Self::Unspecified), + "PERMISSION_POLICY_OPTION_ALLOW" => Some(Self::Allow), + "PERMISSION_POLICY_OPTION_DENY" => Some(Self::Deny), + "PERMISSION_POLICY_OPTION_ADMIN_ONLY" => Some(Self::AdminOnly), + "PERMISSION_POLICY_OPTION_SUPER_ADMIN_ONLY" => Some(Self::SuperAdminOnly), + _ => None, + } + } +} /// Encoded file descriptor set for the `xmtp.mls.database` package pub const FILE_DESCRIPTOR_SET: &[u8] = &[ - 0x0a, 0xaf, 0x2e, 0x0a, 0x1a, 0x6d, 0x6c, 0x73, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, + 0x0a, 0xee, 0x3d, 0x0a, 0x1a, 0x6d, 0x6c, 0x73, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x22, 0x80, 0x01, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, @@ -355,273 +459,397 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[ 0x64, 0x6d, 0x69, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x42, 0x09, 0x0a, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xe8, 0x02, 0x0a, 0x10, 0x50, 0x6f, 0x73, - 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x57, 0x0a, - 0x0d, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, - 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, - 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x57, 0x65, - 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x73, 0x1a, 0x61, 0x0a, 0x0c, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, - 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x68, 0x70, 0x6b, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x68, 0x70, 0x6b, 0x65, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x1a, 0x8f, 0x01, 0x0a, 0x0c, 0x53, 0x65, - 0x6e, 0x64, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x73, 0x12, 0x56, 0x0a, 0x0d, 0x69, 0x6e, - 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x30, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x64, 0x61, 0x74, - 0x61, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, - 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x5f, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x77, 0x65, 0x6c, - 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x6b, - 0x69, 0x6e, 0x64, 0x2a, 0xe7, 0x01, 0x0a, 0x13, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x4c, 0x69, 0x73, - 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x22, 0x41, - 0x44, 0x4d, 0x49, 0x4e, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x24, 0x0a, 0x20, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x5f, 0x4c, 0x49, 0x53, - 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x44, - 0x44, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x27, 0x0a, 0x23, 0x41, 0x44, 0x4d, - 0x49, 0x4e, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, - 0x10, 0x02, 0x12, 0x2a, 0x0a, 0x26, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x5f, 0x4c, 0x49, 0x53, 0x54, - 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x44, 0x44, - 0x5f, 0x53, 0x55, 0x50, 0x45, 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x03, 0x12, 0x2d, - 0x0a, 0x29, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x55, 0x50, 0x44, - 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x5f, - 0x53, 0x55, 0x50, 0x45, 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x04, 0x42, 0xb5, 0x01, - 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x64, - 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x42, 0x0c, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, - 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, - 0x65, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x44, 0xaa, 0x02, 0x11, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, - 0x6c, 0x73, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0xca, 0x02, 0x11, 0x58, 0x6d, - 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0xe2, - 0x02, 0x1d, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x44, 0x61, 0x74, 0x61, 0x62, - 0x61, 0x73, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, - 0x02, 0x13, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x44, 0x61, 0x74, - 0x61, 0x62, 0x61, 0x73, 0x65, 0x4a, 0x82, 0x1b, 0x0a, 0x07, 0x12, 0x05, 0x02, 0x00, 0x82, 0x01, - 0x01, 0x0a, 0x27, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x02, 0x00, 0x12, 0x32, 0x1d, 0x20, 0x56, 0x33, - 0x20, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, - 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, - 0x03, 0x04, 0x00, 0x1a, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x06, 0x00, 0x3f, 0x0a, 0x09, - 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x06, 0x00, 0x3f, 0x0a, 0x34, 0x0a, 0x02, 0x04, 0x00, 0x12, - 0x04, 0x0a, 0x00, 0x13, 0x01, 0x1a, 0x28, 0x20, 0x54, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, - 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x73, 0x68, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, - 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x0a, 0x08, 0x17, 0x0a, 0x2c, 0x0a, 0x04, 0x04, - 0x00, 0x03, 0x00, 0x12, 0x04, 0x0c, 0x02, 0x0e, 0x03, 0x1a, 0x1e, 0x20, 0x56, 0x31, 0x20, 0x6f, - 0x66, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x75, 0x62, - 0x6c, 0x69, 0x73, 0x68, 0x44, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, - 0x00, 0x01, 0x12, 0x03, 0x0c, 0x0a, 0x0c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, - 0x00, 0x12, 0x03, 0x0d, 0x04, 0x1c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, - 0x05, 0x12, 0x03, 0x0d, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, - 0x01, 0x12, 0x03, 0x0d, 0x0a, 0x17, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, - 0x03, 0x12, 0x03, 0x0d, 0x1a, 0x1b, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x00, 0x08, 0x00, 0x12, 0x04, - 0x10, 0x02, 0x12, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x08, 0x00, 0x01, 0x12, 0x03, 0x10, - 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x11, 0x04, 0x0e, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x11, 0x04, 0x06, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x11, 0x07, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x11, 0x0c, 0x0d, 0x0a, 0x45, 0x0a, 0x02, 0x04, 0x01, 0x12, - 0x04, 0x16, 0x00, 0x18, 0x01, 0x1a, 0x39, 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, - 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x61, 0x66, - 0x20, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x45, 0x56, 0x4d, 0x20, 0x41, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x0a, - 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x16, 0x08, 0x18, 0x0a, 0x0b, 0x0a, 0x04, - 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x17, 0x02, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, - 0x00, 0x04, 0x12, 0x03, 0x17, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x05, - 0x12, 0x03, 0x17, 0x0b, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, - 0x17, 0x12, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x17, 0x26, - 0x27, 0x0a, 0x40, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x1b, 0x00, 0x1d, 0x01, 0x1a, 0x34, 0x20, - 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x61, - 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, - 0x64, 0x20, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x49, - 0x44, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x1b, 0x08, 0x17, 0x0a, - 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x1c, 0x02, 0x26, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x02, 0x02, 0x00, 0x04, 0x12, 0x03, 0x1c, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, - 0x02, 0x00, 0x05, 0x12, 0x03, 0x1c, 0x0b, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, - 0x01, 0x12, 0x03, 0x1c, 0x11, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, - 0x03, 0x1c, 0x24, 0x25, 0x0a, 0x3e, 0x0a, 0x02, 0x04, 0x03, 0x12, 0x04, 0x20, 0x00, 0x25, 0x01, - 0x1a, 0x32, 0x20, 0x4f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x45, 0x56, 0x4d, - 0x20, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x20, 0x6f, 0x72, 0x20, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x49, 0x44, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x03, 0x20, 0x08, 0x22, - 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x03, 0x08, 0x00, 0x12, 0x04, 0x21, 0x02, 0x24, 0x03, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x03, 0x08, 0x00, 0x01, 0x12, 0x03, 0x21, 0x08, 0x25, 0x0a, 0x0b, 0x0a, 0x04, - 0x04, 0x03, 0x02, 0x00, 0x12, 0x03, 0x22, 0x04, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, - 0x00, 0x06, 0x12, 0x03, 0x22, 0x04, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x01, - 0x12, 0x03, 0x22, 0x15, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, - 0x22, 0x29, 0x2a, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x01, 0x12, 0x03, 0x23, 0x04, 0x29, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x06, 0x12, 0x03, 0x23, 0x04, 0x13, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x12, 0x03, 0x23, 0x14, 0x24, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x03, 0x02, 0x01, 0x03, 0x12, 0x03, 0x23, 0x27, 0x28, 0x0a, 0x39, 0x0a, 0x02, 0x04, 0x04, - 0x12, 0x04, 0x28, 0x00, 0x31, 0x01, 0x1a, 0x2d, 0x20, 0x54, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, - 0x61, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x64, - 0x64, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x04, 0x01, 0x12, 0x03, 0x28, 0x08, - 0x16, 0x0a, 0x2b, 0x0a, 0x04, 0x04, 0x04, 0x03, 0x00, 0x12, 0x04, 0x2a, 0x02, 0x2c, 0x03, 0x1a, - 0x1d, 0x20, 0x56, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x44, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x04, 0x03, 0x00, 0x01, 0x12, 0x03, 0x2a, 0x0a, 0x0c, 0x0a, 0x0d, 0x0a, 0x06, - 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x2b, 0x04, 0x41, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x04, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x2b, 0x04, 0x1e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x04, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2b, 0x1f, 0x3c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x04, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2b, 0x3f, 0x40, 0x0a, 0x0c, 0x0a, 0x04, 0x04, - 0x04, 0x08, 0x00, 0x12, 0x04, 0x2e, 0x02, 0x30, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x08, - 0x00, 0x01, 0x12, 0x03, 0x2e, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, - 0x03, 0x2f, 0x04, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x06, 0x12, 0x03, 0x2f, - 0x04, 0x06, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2f, 0x07, 0x09, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2f, 0x0c, 0x0d, 0x0a, 0x3e, - 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04, 0x34, 0x00, 0x3d, 0x01, 0x1a, 0x32, 0x20, 0x54, 0x68, 0x65, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xf7, 0x02, 0x0a, 0x14, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x3c, 0x0a, 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, + 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, + 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x56, 0x31, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x1a, + 0x95, 0x02, 0x0a, 0x02, 0x56, 0x31, 0x12, 0x5d, 0x0a, 0x16, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, + 0x73, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, + 0x14, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x63, 0x0a, 0x18, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, + 0x6c, 0x73, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x50, 0x65, 0x72, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x16, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x13, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x11, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, + 0x16, 0x0a, 0x14, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x22, 0xe8, 0x02, 0x0a, 0x10, 0x50, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x57, 0x0a, 0x0d, 0x73, 0x65, 0x6e, 0x64, 0x5f, + 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, + 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, + 0x73, 0x65, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x73, + 0x48, 0x00, 0x52, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x73, + 0x1a, 0x61, 0x0a, 0x0c, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x68, + 0x70, 0x6b, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x68, 0x70, 0x6b, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x1a, 0x8f, 0x01, 0x0a, 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x6c, 0x63, + 0x6f, 0x6d, 0x65, 0x73, 0x12, 0x56, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x78, 0x6d, + 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x2e, + 0x50, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x0a, 0x0f, + 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x2a, 0xe7, 0x01, + 0x0a, 0x13, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x22, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x5f, 0x4c, + 0x49, 0x53, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x24, 0x0a, + 0x20, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, + 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x44, 0x44, 0x5f, 0x41, 0x44, 0x4d, 0x49, + 0x4e, 0x10, 0x01, 0x12, 0x27, 0x0a, 0x23, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x5f, 0x4c, 0x49, 0x53, + 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, + 0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x2a, 0x0a, 0x26, + 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, + 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x44, 0x44, 0x5f, 0x53, 0x55, 0x50, 0x45, 0x52, + 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x03, 0x12, 0x2d, 0x0a, 0x29, 0x41, 0x44, 0x4d, 0x49, + 0x4e, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x5f, 0x53, 0x55, 0x50, 0x45, 0x52, 0x5f, + 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x04, 0x2a, 0x8a, 0x02, 0x0a, 0x14, 0x50, 0x65, 0x72, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x26, 0x0a, 0x22, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x55, + 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, + 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x25, 0x0a, 0x21, 0x50, 0x45, 0x52, 0x4d, + 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x41, 0x44, 0x44, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x01, 0x12, + 0x28, 0x0a, 0x24, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x50, + 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, + 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x02, 0x12, 0x24, 0x0a, 0x20, 0x50, 0x45, 0x52, + 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x41, 0x44, 0x44, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x03, 0x12, + 0x27, 0x0a, 0x23, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x50, + 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, + 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x04, 0x12, 0x2a, 0x0a, 0x26, 0x50, 0x45, 0x52, 0x4d, + 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, + 0x54, 0x41, 0x10, 0x05, 0x2a, 0xe1, 0x01, 0x0a, 0x16, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x28, 0x0a, 0x24, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x4f, + 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, + 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x22, 0x0a, 0x1e, 0x50, 0x45, 0x52, + 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x4f, + 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x10, 0x01, 0x12, 0x21, 0x0a, + 0x1d, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x4f, 0x4c, 0x49, + 0x43, 0x59, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x4e, 0x59, 0x10, 0x02, + 0x12, 0x27, 0x0a, 0x23, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x50, + 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x44, 0x4d, + 0x49, 0x4e, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x03, 0x12, 0x2d, 0x0a, 0x29, 0x50, 0x45, 0x52, + 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x4f, + 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x55, 0x50, 0x45, 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, + 0x4e, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x04, 0x42, 0xb5, 0x01, 0x0a, 0x15, 0x63, 0x6f, 0x6d, + 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, + 0x73, 0x65, 0x42, 0x0c, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x50, 0x01, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, + 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, + 0x6d, 0x6c, 0x73, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0xa2, 0x02, 0x03, 0x58, + 0x4d, 0x44, 0xaa, 0x02, 0x11, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x6c, 0x73, 0x2e, 0x44, 0x61, + 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0xca, 0x02, 0x11, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, + 0x73, 0x5c, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0xe2, 0x02, 0x1d, 0x58, 0x6d, 0x74, + 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x5c, 0x47, + 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x13, 0x58, 0x6d, 0x74, + 0x70, 0x3a, 0x3a, 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, + 0x4a, 0xd6, 0x23, 0x0a, 0x07, 0x12, 0x05, 0x02, 0x00, 0xa4, 0x01, 0x01, 0x0a, 0x27, 0x0a, 0x01, + 0x0c, 0x12, 0x03, 0x02, 0x00, 0x12, 0x32, 0x1d, 0x20, 0x56, 0x33, 0x20, 0x69, 0x6e, 0x76, 0x69, + 0x74, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x75, 0x72, 0x65, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x04, 0x00, 0x1a, 0x0a, + 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x06, 0x00, 0x3f, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, + 0x03, 0x06, 0x00, 0x3f, 0x0a, 0x34, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x0a, 0x00, 0x13, 0x01, + 0x1a, 0x28, 0x20, 0x54, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x72, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x20, + 0x61, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, + 0x01, 0x12, 0x03, 0x0a, 0x08, 0x17, 0x0a, 0x2c, 0x0a, 0x04, 0x04, 0x00, 0x03, 0x00, 0x12, 0x04, + 0x0c, 0x02, 0x0e, 0x03, 0x1a, 0x1e, 0x20, 0x56, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x53, 0x65, 0x6e, + 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x44, + 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, 0x00, 0x01, 0x12, 0x03, 0x0c, + 0x0a, 0x0c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0d, 0x04, + 0x1c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x0d, 0x04, + 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0d, 0x0a, + 0x17, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0d, 0x1a, + 0x1b, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x00, 0x08, 0x00, 0x12, 0x04, 0x10, 0x02, 0x12, 0x03, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x08, 0x00, 0x01, 0x12, 0x03, 0x10, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, + 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x11, 0x04, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, + 0x02, 0x00, 0x06, 0x12, 0x03, 0x11, 0x04, 0x06, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, + 0x01, 0x12, 0x03, 0x11, 0x07, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, + 0x03, 0x11, 0x0c, 0x0d, 0x0a, 0x45, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x16, 0x00, 0x18, 0x01, + 0x1a, 0x39, 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, + 0x64, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x61, 0x66, 0x20, 0x72, 0x65, 0x70, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x20, 0x45, 0x56, 0x4d, 0x20, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x20, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, + 0x01, 0x01, 0x12, 0x03, 0x16, 0x08, 0x18, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, + 0x03, 0x17, 0x02, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x04, 0x12, 0x03, 0x17, + 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x05, 0x12, 0x03, 0x17, 0x0b, 0x11, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x17, 0x12, 0x23, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x17, 0x26, 0x27, 0x0a, 0x40, 0x0a, 0x02, + 0x04, 0x02, 0x12, 0x04, 0x1b, 0x00, 0x1d, 0x01, 0x1a, 0x34, 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, + 0x65, 0x72, 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, + 0x20, 0x6f, 0x66, 0x20, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x49, 0x6e, 0x73, + 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x49, 0x44, 0x73, 0x0a, 0x0a, 0x0a, + 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x1b, 0x08, 0x17, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, + 0x02, 0x00, 0x12, 0x03, 0x1c, 0x02, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x04, + 0x12, 0x03, 0x1c, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x05, 0x12, 0x03, + 0x1c, 0x0b, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1c, 0x11, + 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1c, 0x24, 0x25, 0x0a, + 0x3e, 0x0a, 0x02, 0x04, 0x03, 0x12, 0x04, 0x20, 0x00, 0x25, 0x01, 0x1a, 0x32, 0x20, 0x4f, 0x6e, + 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x45, 0x56, 0x4d, 0x20, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x6f, 0x72, 0x20, 0x49, + 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x49, 0x44, 0x0a, 0x0a, + 0x0a, 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x03, 0x20, 0x08, 0x22, 0x0a, 0x0c, 0x0a, 0x04, 0x04, + 0x03, 0x08, 0x00, 0x12, 0x04, 0x21, 0x02, 0x24, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x08, + 0x00, 0x01, 0x12, 0x03, 0x21, 0x08, 0x25, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x00, 0x12, + 0x03, 0x22, 0x04, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x06, 0x12, 0x03, 0x22, + 0x04, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x01, 0x12, 0x03, 0x22, 0x15, 0x26, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, 0x22, 0x29, 0x2a, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x03, 0x02, 0x01, 0x12, 0x03, 0x23, 0x04, 0x29, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x03, 0x02, 0x01, 0x06, 0x12, 0x03, 0x23, 0x04, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, + 0x01, 0x01, 0x12, 0x03, 0x23, 0x14, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x03, + 0x12, 0x03, 0x23, 0x27, 0x28, 0x0a, 0x39, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x28, 0x00, 0x31, + 0x01, 0x1a, 0x2d, 0x20, 0x54, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x64, 0x64, 0x20, 0x6d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x0a, + 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x04, 0x01, 0x12, 0x03, 0x28, 0x08, 0x16, 0x0a, 0x2b, 0x0a, 0x04, + 0x04, 0x04, 0x03, 0x00, 0x12, 0x04, 0x2a, 0x02, 0x2c, 0x03, 0x1a, 0x1d, 0x20, 0x56, 0x31, 0x20, + 0x6f, 0x66, 0x20, 0x41, 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x73, 0x68, 0x44, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x03, + 0x00, 0x01, 0x12, 0x03, 0x2a, 0x0a, 0x0c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x03, 0x00, 0x02, + 0x00, 0x12, 0x03, 0x2b, 0x04, 0x41, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, + 0x06, 0x12, 0x03, 0x2b, 0x04, 0x1e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, + 0x01, 0x12, 0x03, 0x2b, 0x1f, 0x3c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, + 0x03, 0x12, 0x03, 0x2b, 0x3f, 0x40, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x04, 0x08, 0x00, 0x12, 0x04, + 0x2e, 0x02, 0x30, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x08, 0x00, 0x01, 0x12, 0x03, 0x2e, + 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, 0x03, 0x2f, 0x04, 0x0e, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x06, 0x12, 0x03, 0x2f, 0x04, 0x06, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2f, 0x07, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2f, 0x0c, 0x0d, 0x0a, 0x3e, 0x0a, 0x02, 0x04, 0x05, 0x12, + 0x04, 0x34, 0x00, 0x3d, 0x01, 0x1a, 0x32, 0x20, 0x54, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, + 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, 0x66, 0x72, 0x6f, 0x6d, + 0x20, 0x61, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x05, 0x01, + 0x12, 0x03, 0x34, 0x08, 0x19, 0x0a, 0x2e, 0x0a, 0x04, 0x04, 0x05, 0x03, 0x00, 0x12, 0x04, 0x36, + 0x02, 0x38, 0x03, 0x1a, 0x20, 0x20, 0x56, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x52, 0x65, 0x6d, 0x6f, + 0x76, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, + 0x44, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x03, 0x00, 0x01, 0x12, 0x03, + 0x36, 0x0a, 0x0c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x05, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x37, + 0x04, 0x41, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x37, + 0x04, 0x1e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x37, + 0x1f, 0x3c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x37, + 0x3f, 0x40, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x05, 0x08, 0x00, 0x12, 0x04, 0x3a, 0x02, 0x3c, 0x03, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x08, 0x00, 0x01, 0x12, 0x03, 0x3a, 0x08, 0x0f, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, 0x12, 0x03, 0x3b, 0x04, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x05, 0x02, 0x00, 0x06, 0x12, 0x03, 0x3b, 0x04, 0x06, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, + 0x00, 0x01, 0x12, 0x03, 0x3b, 0x07, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x03, + 0x12, 0x03, 0x3b, 0x0c, 0x0d, 0x0a, 0x73, 0x0a, 0x02, 0x04, 0x06, 0x12, 0x04, 0x41, 0x00, 0x4d, + 0x01, 0x1a, 0x67, 0x20, 0x54, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x20, 0x61, 0x20, + 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x73, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x73, 0x68, 0x69, 0x70, 0x0a, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x20, 0x62, 0x6f, + 0x74, 0x68, 0x20, 0x41, 0x64, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x06, + 0x01, 0x12, 0x03, 0x41, 0x08, 0x21, 0x0a, 0x36, 0x0a, 0x04, 0x04, 0x06, 0x03, 0x00, 0x12, 0x04, + 0x43, 0x02, 0x48, 0x03, 0x1a, 0x28, 0x20, 0x56, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, + 0x69, 0x70, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x44, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x06, 0x03, 0x00, 0x01, 0x12, 0x03, 0x43, 0x0a, 0x0c, 0x0a, 0x4d, 0x0a, 0x06, + 0x04, 0x06, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x45, 0x04, 0x2f, 0x1a, 0x3e, 0x20, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x20, 0x6f, 0x66, 0x20, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, + 0x62, 0x65, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x06, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x45, 0x04, 0x17, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x06, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x45, 0x18, 0x2a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x06, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x45, 0x2d, 0x2e, 0x0a, 0x42, 0x0a, 0x06, 0x04, + 0x06, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x47, 0x04, 0x28, 0x1a, 0x33, 0x20, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, + 0x66, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, + 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x0a, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, 0x01, 0x04, 0x12, 0x03, 0x47, 0x04, 0x0c, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x47, 0x0d, 0x13, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x47, 0x14, 0x23, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x47, 0x26, 0x27, 0x0a, + 0x0c, 0x0a, 0x04, 0x04, 0x06, 0x08, 0x00, 0x12, 0x04, 0x4a, 0x02, 0x4c, 0x03, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x06, 0x08, 0x00, 0x01, 0x12, 0x03, 0x4a, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, + 0x06, 0x02, 0x00, 0x12, 0x03, 0x4b, 0x04, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, + 0x06, 0x12, 0x03, 0x4b, 0x04, 0x06, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x01, 0x12, + 0x03, 0x4b, 0x07, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x03, 0x12, 0x03, 0x4b, + 0x0c, 0x0d, 0x0a, 0x38, 0x0a, 0x02, 0x04, 0x07, 0x12, 0x04, 0x50, 0x00, 0x5a, 0x01, 0x1a, 0x2c, + 0x20, 0x54, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, + 0x04, 0x07, 0x01, 0x12, 0x03, 0x50, 0x08, 0x1a, 0x0a, 0x2f, 0x0a, 0x04, 0x04, 0x07, 0x03, 0x00, + 0x12, 0x04, 0x52, 0x02, 0x55, 0x03, 0x1a, 0x21, 0x20, 0x56, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x73, 0x68, 0x44, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x03, + 0x00, 0x01, 0x12, 0x03, 0x52, 0x0a, 0x0c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x07, 0x03, 0x00, 0x02, + 0x00, 0x12, 0x03, 0x53, 0x04, 0x1a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, + 0x05, 0x12, 0x03, 0x53, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, + 0x01, 0x12, 0x03, 0x53, 0x0b, 0x15, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, + 0x03, 0x12, 0x03, 0x53, 0x18, 0x19, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x07, 0x03, 0x00, 0x02, 0x01, + 0x12, 0x03, 0x54, 0x04, 0x1b, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x01, 0x05, + 0x12, 0x03, 0x54, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x01, 0x01, + 0x12, 0x03, 0x54, 0x0b, 0x16, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x01, 0x03, + 0x12, 0x03, 0x54, 0x19, 0x1a, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x07, 0x08, 0x00, 0x12, 0x04, 0x57, + 0x02, 0x59, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x08, 0x00, 0x01, 0x12, 0x03, 0x57, 0x08, + 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x00, 0x12, 0x03, 0x58, 0x04, 0x0e, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x06, 0x12, 0x03, 0x58, 0x04, 0x06, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x07, 0x02, 0x00, 0x01, 0x12, 0x03, 0x58, 0x07, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, + 0x02, 0x00, 0x03, 0x12, 0x03, 0x58, 0x0c, 0x0d, 0x0a, 0x2b, 0x0a, 0x02, 0x05, 0x00, 0x12, 0x04, + 0x5d, 0x00, 0x63, 0x01, 0x1a, 0x1f, 0x20, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x20, 0x6c, + 0x69, 0x73, 0x74, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x00, 0x01, 0x12, 0x03, 0x5d, 0x05, + 0x18, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x00, 0x12, 0x03, 0x5e, 0x02, 0x29, 0x0a, 0x0c, + 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x5e, 0x02, 0x24, 0x0a, 0x0c, 0x0a, 0x05, + 0x05, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x5e, 0x27, 0x28, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, + 0x02, 0x01, 0x12, 0x03, 0x5f, 0x02, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x01, + 0x12, 0x03, 0x5f, 0x02, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, + 0x5f, 0x25, 0x26, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x02, 0x12, 0x03, 0x60, 0x02, 0x2a, + 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x60, 0x02, 0x25, 0x0a, 0x0c, + 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x60, 0x28, 0x29, 0x0a, 0x0b, 0x0a, 0x04, + 0x05, 0x00, 0x02, 0x03, 0x12, 0x03, 0x61, 0x02, 0x2d, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, + 0x03, 0x01, 0x12, 0x03, 0x61, 0x02, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x03, 0x02, + 0x12, 0x03, 0x61, 0x2b, 0x2c, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x04, 0x12, 0x03, 0x62, + 0x02, 0x30, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x04, 0x01, 0x12, 0x03, 0x62, 0x02, 0x2b, + 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x04, 0x02, 0x12, 0x03, 0x62, 0x2e, 0x2f, 0x0a, 0x47, + 0x0a, 0x02, 0x04, 0x08, 0x12, 0x04, 0x66, 0x00, 0x70, 0x01, 0x1a, 0x3b, 0x20, 0x54, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, - 0x6f, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, - 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x0a, 0x0a, 0x0a, - 0x0a, 0x03, 0x04, 0x05, 0x01, 0x12, 0x03, 0x34, 0x08, 0x19, 0x0a, 0x2e, 0x0a, 0x04, 0x04, 0x05, - 0x03, 0x00, 0x12, 0x04, 0x36, 0x02, 0x38, 0x03, 0x1a, 0x20, 0x20, 0x56, 0x31, 0x20, 0x6f, 0x66, - 0x20, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x50, 0x75, - 0x62, 0x6c, 0x69, 0x73, 0x68, 0x44, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, - 0x03, 0x00, 0x01, 0x12, 0x03, 0x36, 0x0a, 0x0c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x05, 0x03, 0x00, - 0x02, 0x00, 0x12, 0x03, 0x37, 0x04, 0x41, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, - 0x00, 0x06, 0x12, 0x03, 0x37, 0x04, 0x1e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, - 0x00, 0x01, 0x12, 0x03, 0x37, 0x1f, 0x3c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x05, 0x03, 0x00, 0x02, - 0x00, 0x03, 0x12, 0x03, 0x37, 0x3f, 0x40, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x05, 0x08, 0x00, 0x12, - 0x04, 0x3a, 0x02, 0x3c, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x08, 0x00, 0x01, 0x12, 0x03, - 0x3a, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, 0x12, 0x03, 0x3b, 0x04, 0x0e, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x06, 0x12, 0x03, 0x3b, 0x04, 0x06, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x01, 0x12, 0x03, 0x3b, 0x07, 0x09, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x05, 0x02, 0x00, 0x03, 0x12, 0x03, 0x3b, 0x0c, 0x0d, 0x0a, 0x73, 0x0a, 0x02, 0x04, 0x06, - 0x12, 0x04, 0x41, 0x00, 0x4d, 0x01, 0x1a, 0x67, 0x20, 0x54, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, - 0x61, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x61, - 0x6b, 0x65, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x74, 0x68, 0x61, 0x74, - 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x0a, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x6c, - 0x65, 0x73, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x20, 0x41, 0x64, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, - 0x0a, 0x0a, 0x03, 0x04, 0x06, 0x01, 0x12, 0x03, 0x41, 0x08, 0x21, 0x0a, 0x36, 0x0a, 0x04, 0x04, - 0x06, 0x03, 0x00, 0x12, 0x04, 0x43, 0x02, 0x48, 0x03, 0x1a, 0x28, 0x20, 0x56, 0x31, 0x20, 0x6f, - 0x66, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x44, 0x61, - 0x74, 0x61, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x03, 0x00, 0x01, 0x12, 0x03, 0x43, 0x0a, - 0x0c, 0x0a, 0x4d, 0x0a, 0x06, 0x04, 0x06, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x45, 0x04, 0x2f, - 0x1a, 0x3e, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x64, 0x65, 0x6c, 0x74, - 0x61, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x20, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6e, 0x65, 0x65, - 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x0a, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x45, 0x04, 0x17, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x45, 0x18, 0x2a, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x45, 0x2d, 0x2e, - 0x0a, 0x42, 0x0a, 0x06, 0x04, 0x06, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x47, 0x04, 0x28, 0x1a, - 0x33, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, - 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, 0x74, - 0x68, 0x61, 0x74, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x64, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, 0x01, 0x04, 0x12, - 0x03, 0x47, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, - 0x03, 0x47, 0x0d, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, - 0x03, 0x47, 0x14, 0x23, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x06, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, - 0x03, 0x47, 0x26, 0x27, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x06, 0x08, 0x00, 0x12, 0x04, 0x4a, 0x02, - 0x4c, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x08, 0x00, 0x01, 0x12, 0x03, 0x4a, 0x08, 0x0f, - 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x00, 0x12, 0x03, 0x4b, 0x04, 0x0e, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x06, 0x02, 0x00, 0x06, 0x12, 0x03, 0x4b, 0x04, 0x06, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x06, 0x02, 0x00, 0x01, 0x12, 0x03, 0x4b, 0x07, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, - 0x00, 0x03, 0x12, 0x03, 0x4b, 0x0c, 0x0d, 0x0a, 0x38, 0x0a, 0x02, 0x04, 0x07, 0x12, 0x04, 0x50, - 0x00, 0x5a, 0x01, 0x1a, 0x2c, 0x20, 0x54, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x07, 0x01, 0x12, 0x03, 0x50, 0x08, 0x1a, 0x0a, 0x2f, 0x0a, - 0x04, 0x04, 0x07, 0x03, 0x00, 0x12, 0x04, 0x52, 0x02, 0x55, 0x03, 0x1a, 0x21, 0x20, 0x56, 0x31, - 0x20, 0x6f, 0x66, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x44, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x07, 0x03, 0x00, 0x01, 0x12, 0x03, 0x52, 0x0a, 0x0c, 0x0a, 0x0d, 0x0a, 0x06, - 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x53, 0x04, 0x1a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x07, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x53, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x07, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x53, 0x0b, 0x15, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x07, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x53, 0x18, 0x19, 0x0a, 0x0d, 0x0a, 0x06, 0x04, - 0x07, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x54, 0x04, 0x1b, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, - 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x54, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, - 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x54, 0x0b, 0x16, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, - 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x54, 0x19, 0x1a, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x07, - 0x08, 0x00, 0x12, 0x04, 0x57, 0x02, 0x59, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x08, 0x00, - 0x01, 0x12, 0x03, 0x57, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x00, 0x12, 0x03, - 0x58, 0x04, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x06, 0x12, 0x03, 0x58, 0x04, - 0x06, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x01, 0x12, 0x03, 0x58, 0x07, 0x09, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x03, 0x12, 0x03, 0x58, 0x0c, 0x0d, 0x0a, 0x2b, 0x0a, - 0x02, 0x05, 0x00, 0x12, 0x04, 0x5d, 0x00, 0x63, 0x01, 0x1a, 0x1f, 0x20, 0x54, 0x79, 0x70, 0x65, - 0x20, 0x6f, 0x66, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x64, - 0x6d, 0x69, 0x6e, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x00, - 0x01, 0x12, 0x03, 0x5d, 0x05, 0x18, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x00, 0x12, 0x03, - 0x5e, 0x02, 0x29, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x5e, 0x02, - 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x5e, 0x27, 0x28, 0x0a, - 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x01, 0x12, 0x03, 0x5f, 0x02, 0x27, 0x0a, 0x0c, 0x0a, 0x05, - 0x05, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x5f, 0x02, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, - 0x02, 0x01, 0x02, 0x12, 0x03, 0x5f, 0x25, 0x26, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x02, - 0x12, 0x03, 0x60, 0x02, 0x2a, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, - 0x60, 0x02, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x60, 0x28, - 0x29, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x03, 0x12, 0x03, 0x61, 0x02, 0x2d, 0x0a, 0x0c, - 0x0a, 0x05, 0x05, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x61, 0x02, 0x28, 0x0a, 0x0c, 0x0a, 0x05, - 0x05, 0x00, 0x02, 0x03, 0x02, 0x12, 0x03, 0x61, 0x2b, 0x2c, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, - 0x02, 0x04, 0x12, 0x03, 0x62, 0x02, 0x30, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x04, 0x01, - 0x12, 0x03, 0x62, 0x02, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x04, 0x02, 0x12, 0x03, - 0x62, 0x2e, 0x2f, 0x0a, 0x47, 0x0a, 0x02, 0x04, 0x08, 0x12, 0x04, 0x66, 0x00, 0x70, 0x01, 0x1a, - 0x3b, 0x20, 0x54, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x20, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x20, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, - 0x04, 0x08, 0x01, 0x12, 0x03, 0x66, 0x08, 0x1c, 0x0a, 0x31, 0x0a, 0x04, 0x04, 0x08, 0x03, 0x00, - 0x12, 0x04, 0x68, 0x02, 0x6b, 0x03, 0x1a, 0x23, 0x20, 0x56, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x50, - 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x44, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x08, 0x03, 0x00, 0x01, 0x12, 0x03, 0x68, 0x0a, 0x0c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x08, 0x03, - 0x00, 0x02, 0x00, 0x12, 0x03, 0x69, 0x04, 0x33, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, - 0x02, 0x00, 0x06, 0x12, 0x03, 0x69, 0x04, 0x17, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, - 0x02, 0x00, 0x01, 0x12, 0x03, 0x69, 0x18, 0x2e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, - 0x02, 0x00, 0x03, 0x12, 0x03, 0x69, 0x31, 0x32, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x08, 0x03, 0x00, - 0x02, 0x01, 0x12, 0x03, 0x6a, 0x04, 0x18, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, - 0x01, 0x05, 0x12, 0x03, 0x6a, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, - 0x01, 0x01, 0x12, 0x03, 0x6a, 0x0b, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, - 0x01, 0x03, 0x12, 0x03, 0x6a, 0x16, 0x17, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x08, 0x08, 0x00, 0x12, - 0x04, 0x6d, 0x02, 0x6f, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x08, 0x00, 0x01, 0x12, 0x03, - 0x6d, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x08, 0x02, 0x00, 0x12, 0x03, 0x6e, 0x04, 0x0e, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x06, 0x12, 0x03, 0x6e, 0x04, 0x06, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x01, 0x12, 0x03, 0x6e, 0x07, 0x09, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x08, 0x02, 0x00, 0x03, 0x12, 0x03, 0x6e, 0x0c, 0x0d, 0x0a, 0x3c, 0x0a, 0x02, 0x04, 0x09, - 0x12, 0x05, 0x73, 0x00, 0x82, 0x01, 0x01, 0x1a, 0x2f, 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, - 0x61, 0x6c, 0x6c, 0x20, 0x70, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x20, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x09, 0x01, 0x12, - 0x03, 0x73, 0x08, 0x18, 0x0a, 0x1f, 0x0a, 0x04, 0x04, 0x09, 0x03, 0x00, 0x12, 0x04, 0x75, 0x02, - 0x78, 0x03, 0x1a, 0x11, 0x20, 0x41, 0x6e, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x03, 0x00, 0x01, 0x12, 0x03, - 0x75, 0x0a, 0x16, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x09, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x76, - 0x04, 0x1f, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x76, - 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x76, - 0x0a, 0x1a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x76, - 0x1d, 0x1e, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x09, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x77, 0x04, - 0x1e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x77, 0x04, - 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x77, 0x0a, - 0x19, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x77, 0x1c, - 0x1d, 0x0a, 0x23, 0x0a, 0x04, 0x04, 0x09, 0x03, 0x01, 0x12, 0x04, 0x7a, 0x02, 0x7d, 0x03, 0x1a, - 0x15, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x03, 0x01, 0x01, 0x12, - 0x03, 0x7a, 0x0a, 0x16, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x09, 0x03, 0x01, 0x02, 0x00, 0x12, 0x03, - 0x7b, 0x04, 0x2c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x01, 0x02, 0x00, 0x04, 0x12, 0x03, - 0x7b, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, - 0x7b, 0x0d, 0x19, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, - 0x7b, 0x1a, 0x27, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, - 0x7b, 0x2a, 0x2b, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x09, 0x03, 0x01, 0x02, 0x01, 0x12, 0x03, 0x7c, - 0x04, 0x1e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x01, 0x02, 0x01, 0x05, 0x12, 0x03, 0x7c, - 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x7c, - 0x0a, 0x19, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x7c, - 0x1c, 0x1d, 0x0a, 0x0d, 0x0a, 0x04, 0x04, 0x09, 0x08, 0x00, 0x12, 0x05, 0x7f, 0x02, 0x81, 0x01, - 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x08, 0x00, 0x01, 0x12, 0x03, 0x7f, 0x08, 0x0c, 0x0a, - 0x0c, 0x0a, 0x04, 0x04, 0x09, 0x02, 0x00, 0x12, 0x04, 0x80, 0x01, 0x04, 0x23, 0x0a, 0x0d, 0x0a, - 0x05, 0x04, 0x09, 0x02, 0x00, 0x06, 0x12, 0x04, 0x80, 0x01, 0x04, 0x10, 0x0a, 0x0d, 0x0a, 0x05, - 0x04, 0x09, 0x02, 0x00, 0x01, 0x12, 0x04, 0x80, 0x01, 0x11, 0x1e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, - 0x09, 0x02, 0x00, 0x03, 0x12, 0x04, 0x80, 0x01, 0x21, 0x22, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x6f, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x2f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x20, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x20, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x08, 0x01, 0x12, 0x03, + 0x66, 0x08, 0x1c, 0x0a, 0x31, 0x0a, 0x04, 0x04, 0x08, 0x03, 0x00, 0x12, 0x04, 0x68, 0x02, 0x6b, + 0x03, 0x1a, 0x23, 0x20, 0x56, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, + 0x68, 0x44, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x03, 0x00, 0x01, 0x12, + 0x03, 0x68, 0x0a, 0x0c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x08, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, + 0x69, 0x04, 0x33, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, + 0x69, 0x04, 0x17, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, + 0x69, 0x18, 0x2e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, + 0x69, 0x31, 0x32, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x6a, + 0x04, 0x18, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x6a, + 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x6a, + 0x0b, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x6a, + 0x16, 0x17, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x08, 0x08, 0x00, 0x12, 0x04, 0x6d, 0x02, 0x6f, 0x03, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x08, 0x00, 0x01, 0x12, 0x03, 0x6d, 0x08, 0x0f, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x08, 0x02, 0x00, 0x12, 0x03, 0x6e, 0x04, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x08, 0x02, 0x00, 0x06, 0x12, 0x03, 0x6e, 0x04, 0x06, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, + 0x00, 0x01, 0x12, 0x03, 0x6e, 0x07, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x03, + 0x12, 0x03, 0x6e, 0x0c, 0x0d, 0x0a, 0x2a, 0x0a, 0x02, 0x05, 0x01, 0x12, 0x04, 0x73, 0x00, 0x7a, + 0x01, 0x1a, 0x1e, 0x20, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x50, 0x65, 0x72, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x01, 0x01, 0x12, 0x03, 0x73, 0x05, 0x19, 0x0a, 0x0b, 0x0a, + 0x04, 0x05, 0x01, 0x02, 0x00, 0x12, 0x03, 0x74, 0x02, 0x29, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x01, + 0x02, 0x00, 0x01, 0x12, 0x03, 0x74, 0x02, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x00, + 0x02, 0x12, 0x03, 0x74, 0x27, 0x28, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x01, 0x02, 0x01, 0x12, 0x03, + 0x75, 0x02, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x75, 0x02, + 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x01, 0x02, 0x12, 0x03, 0x75, 0x26, 0x27, 0x0a, + 0x0b, 0x0a, 0x04, 0x05, 0x01, 0x02, 0x02, 0x12, 0x03, 0x76, 0x02, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, + 0x05, 0x01, 0x02, 0x02, 0x01, 0x12, 0x03, 0x76, 0x02, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x01, + 0x02, 0x02, 0x02, 0x12, 0x03, 0x76, 0x29, 0x2a, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x01, 0x02, 0x03, + 0x12, 0x03, 0x77, 0x02, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x03, 0x01, 0x12, 0x03, + 0x77, 0x02, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x03, 0x02, 0x12, 0x03, 0x77, 0x25, + 0x26, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x01, 0x02, 0x04, 0x12, 0x03, 0x78, 0x02, 0x2a, 0x0a, 0x0c, + 0x0a, 0x05, 0x05, 0x01, 0x02, 0x04, 0x01, 0x12, 0x03, 0x78, 0x02, 0x25, 0x0a, 0x0c, 0x0a, 0x05, + 0x05, 0x01, 0x02, 0x04, 0x02, 0x12, 0x03, 0x78, 0x28, 0x29, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x01, + 0x02, 0x05, 0x12, 0x03, 0x79, 0x02, 0x2d, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x05, 0x01, + 0x12, 0x03, 0x79, 0x02, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x05, 0x02, 0x12, 0x03, + 0x79, 0x2b, 0x2c, 0x0a, 0x20, 0x0a, 0x02, 0x05, 0x02, 0x12, 0x05, 0x7d, 0x00, 0x83, 0x01, 0x01, + 0x1a, 0x13, 0x20, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x02, 0x01, 0x12, 0x03, 0x7d, 0x05, + 0x1b, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x02, 0x02, 0x00, 0x12, 0x03, 0x7e, 0x02, 0x2b, 0x0a, 0x0c, + 0x0a, 0x05, 0x05, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x7e, 0x02, 0x26, 0x0a, 0x0c, 0x0a, 0x05, + 0x05, 0x02, 0x02, 0x00, 0x02, 0x12, 0x03, 0x7e, 0x29, 0x2a, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x02, + 0x02, 0x01, 0x12, 0x03, 0x7f, 0x02, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x02, 0x02, 0x01, 0x01, + 0x12, 0x03, 0x7f, 0x02, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x02, 0x02, 0x01, 0x02, 0x12, 0x03, + 0x7f, 0x23, 0x24, 0x0a, 0x0c, 0x0a, 0x04, 0x05, 0x02, 0x02, 0x02, 0x12, 0x04, 0x80, 0x01, 0x02, + 0x24, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x02, 0x02, 0x02, 0x01, 0x12, 0x04, 0x80, 0x01, 0x02, 0x1f, + 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x02, 0x02, 0x02, 0x02, 0x12, 0x04, 0x80, 0x01, 0x22, 0x23, 0x0a, + 0x0c, 0x0a, 0x04, 0x05, 0x02, 0x02, 0x03, 0x12, 0x04, 0x81, 0x01, 0x02, 0x2a, 0x0a, 0x0d, 0x0a, + 0x05, 0x05, 0x02, 0x02, 0x03, 0x01, 0x12, 0x04, 0x81, 0x01, 0x02, 0x25, 0x0a, 0x0d, 0x0a, 0x05, + 0x05, 0x02, 0x02, 0x03, 0x02, 0x12, 0x04, 0x81, 0x01, 0x28, 0x29, 0x0a, 0x0c, 0x0a, 0x04, 0x05, + 0x02, 0x02, 0x04, 0x12, 0x04, 0x82, 0x01, 0x02, 0x30, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x02, 0x02, + 0x04, 0x01, 0x12, 0x04, 0x82, 0x01, 0x02, 0x2b, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x02, 0x02, 0x04, + 0x02, 0x12, 0x04, 0x82, 0x01, 0x2e, 0x2f, 0x0a, 0x37, 0x0a, 0x02, 0x04, 0x09, 0x12, 0x06, 0x86, + 0x01, 0x00, 0x92, 0x01, 0x01, 0x1a, 0x29, 0x20, 0x54, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, + 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x0a, + 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x09, 0x01, 0x12, 0x04, 0x86, 0x01, 0x08, 0x1c, 0x0a, 0x2c, 0x0a, + 0x04, 0x04, 0x09, 0x03, 0x00, 0x12, 0x06, 0x88, 0x01, 0x02, 0x8d, 0x01, 0x03, 0x1a, 0x1c, 0x20, + 0x56, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x72, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, + 0x09, 0x03, 0x00, 0x01, 0x12, 0x04, 0x88, 0x01, 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x09, + 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0x89, 0x01, 0x04, 0x34, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x09, + 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x04, 0x89, 0x01, 0x04, 0x18, 0x0a, 0x0f, 0x0a, 0x07, 0x04, + 0x09, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0x89, 0x01, 0x19, 0x2f, 0x0a, 0x0f, 0x0a, 0x07, + 0x04, 0x09, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0x89, 0x01, 0x32, 0x33, 0x0a, 0x0e, 0x0a, + 0x06, 0x04, 0x09, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, 0x8a, 0x01, 0x04, 0x38, 0x0a, 0x0f, 0x0a, + 0x07, 0x04, 0x09, 0x03, 0x00, 0x02, 0x01, 0x06, 0x12, 0x04, 0x8a, 0x01, 0x04, 0x1a, 0x0a, 0x0f, + 0x0a, 0x07, 0x04, 0x09, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0x8a, 0x01, 0x1b, 0x33, 0x0a, + 0x0f, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, 0x8a, 0x01, 0x36, 0x37, + 0x0a, 0x5e, 0x0a, 0x06, 0x04, 0x09, 0x03, 0x00, 0x02, 0x02, 0x12, 0x04, 0x8c, 0x01, 0x04, 0x2c, + 0x1a, 0x4e, 0x20, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x70, 0x65, 0x72, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x73, + 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x79, 0x20, 0x61, 0x72, 0x65, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x0a, + 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x00, 0x02, 0x02, 0x04, 0x12, 0x04, 0x8c, 0x01, 0x04, + 0x0c, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, 0x04, 0x8c, 0x01, + 0x0d, 0x13, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x04, 0x8c, + 0x01, 0x14, 0x27, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x09, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, 0x04, + 0x8c, 0x01, 0x2a, 0x2b, 0x0a, 0x0e, 0x0a, 0x04, 0x04, 0x09, 0x08, 0x00, 0x12, 0x06, 0x8f, 0x01, + 0x02, 0x91, 0x01, 0x03, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x09, 0x08, 0x00, 0x01, 0x12, 0x04, 0x8f, + 0x01, 0x08, 0x0f, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x09, 0x02, 0x00, 0x12, 0x04, 0x90, 0x01, 0x04, + 0x0e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x06, 0x12, 0x04, 0x90, 0x01, 0x04, 0x06, + 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x01, 0x12, 0x04, 0x90, 0x01, 0x07, 0x09, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x03, 0x12, 0x04, 0x90, 0x01, 0x0c, 0x0d, 0x0a, 0x3d, + 0x0a, 0x02, 0x04, 0x0a, 0x12, 0x06, 0x95, 0x01, 0x00, 0xa4, 0x01, 0x01, 0x1a, 0x2f, 0x20, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x74, 0x79, 0x70, 0x65, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x70, 0x6f, 0x73, 0x74, 0x2d, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x0b, 0x0a, + 0x03, 0x04, 0x0a, 0x01, 0x12, 0x04, 0x95, 0x01, 0x08, 0x18, 0x0a, 0x21, 0x0a, 0x04, 0x04, 0x0a, + 0x03, 0x00, 0x12, 0x06, 0x97, 0x01, 0x02, 0x9a, 0x01, 0x03, 0x1a, 0x11, 0x20, 0x41, 0x6e, 0x20, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x0a, 0x0d, 0x0a, + 0x05, 0x04, 0x0a, 0x03, 0x00, 0x01, 0x12, 0x04, 0x97, 0x01, 0x0a, 0x16, 0x0a, 0x0e, 0x0a, 0x06, + 0x04, 0x0a, 0x03, 0x00, 0x02, 0x00, 0x12, 0x04, 0x98, 0x01, 0x04, 0x1f, 0x0a, 0x0f, 0x0a, 0x07, + 0x04, 0x0a, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x04, 0x98, 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, + 0x07, 0x04, 0x0a, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x04, 0x98, 0x01, 0x0a, 0x1a, 0x0a, 0x0f, + 0x0a, 0x07, 0x04, 0x0a, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x04, 0x98, 0x01, 0x1d, 0x1e, 0x0a, + 0x0e, 0x0a, 0x06, 0x04, 0x0a, 0x03, 0x00, 0x02, 0x01, 0x12, 0x04, 0x99, 0x01, 0x04, 0x1e, 0x0a, + 0x0f, 0x0a, 0x07, 0x04, 0x0a, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x04, 0x99, 0x01, 0x04, 0x09, + 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0a, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0x99, 0x01, 0x0a, + 0x19, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0a, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, 0x99, 0x01, + 0x1c, 0x1d, 0x0a, 0x25, 0x0a, 0x04, 0x04, 0x0a, 0x03, 0x01, 0x12, 0x06, 0x9c, 0x01, 0x02, 0x9f, + 0x01, 0x03, 0x1a, 0x15, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, + 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0a, 0x03, + 0x01, 0x01, 0x12, 0x04, 0x9c, 0x01, 0x0a, 0x16, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x0a, 0x03, 0x01, + 0x02, 0x00, 0x12, 0x04, 0x9d, 0x01, 0x04, 0x2c, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0a, 0x03, 0x01, + 0x02, 0x00, 0x04, 0x12, 0x04, 0x9d, 0x01, 0x04, 0x0c, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0a, 0x03, + 0x01, 0x02, 0x00, 0x06, 0x12, 0x04, 0x9d, 0x01, 0x0d, 0x19, 0x0a, 0x0f, 0x0a, 0x07, 0x04, 0x0a, + 0x03, 0x01, 0x02, 0x00, 0x01, 0x12, 0x04, 0x9d, 0x01, 0x1a, 0x27, 0x0a, 0x0f, 0x0a, 0x07, 0x04, + 0x0a, 0x03, 0x01, 0x02, 0x00, 0x03, 0x12, 0x04, 0x9d, 0x01, 0x2a, 0x2b, 0x0a, 0x0e, 0x0a, 0x06, + 0x04, 0x0a, 0x03, 0x01, 0x02, 0x01, 0x12, 0x04, 0x9e, 0x01, 0x04, 0x1e, 0x0a, 0x0f, 0x0a, 0x07, + 0x04, 0x0a, 0x03, 0x01, 0x02, 0x01, 0x05, 0x12, 0x04, 0x9e, 0x01, 0x04, 0x09, 0x0a, 0x0f, 0x0a, + 0x07, 0x04, 0x0a, 0x03, 0x01, 0x02, 0x01, 0x01, 0x12, 0x04, 0x9e, 0x01, 0x0a, 0x19, 0x0a, 0x0f, + 0x0a, 0x07, 0x04, 0x0a, 0x03, 0x01, 0x02, 0x01, 0x03, 0x12, 0x04, 0x9e, 0x01, 0x1c, 0x1d, 0x0a, + 0x0e, 0x0a, 0x04, 0x04, 0x0a, 0x08, 0x00, 0x12, 0x06, 0xa1, 0x01, 0x02, 0xa3, 0x01, 0x03, 0x0a, + 0x0d, 0x0a, 0x05, 0x04, 0x0a, 0x08, 0x00, 0x01, 0x12, 0x04, 0xa1, 0x01, 0x08, 0x0c, 0x0a, 0x0c, + 0x0a, 0x04, 0x04, 0x0a, 0x02, 0x00, 0x12, 0x04, 0xa2, 0x01, 0x04, 0x23, 0x0a, 0x0d, 0x0a, 0x05, + 0x04, 0x0a, 0x02, 0x00, 0x06, 0x12, 0x04, 0xa2, 0x01, 0x04, 0x10, 0x0a, 0x0d, 0x0a, 0x05, 0x04, + 0x0a, 0x02, 0x00, 0x01, 0x12, 0x04, 0xa2, 0x01, 0x11, 0x1e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x0a, + 0x02, 0x00, 0x03, 0x12, 0x04, 0xa2, 0x01, 0x21, 0x22, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, ]; include!("xmtp.mls.database.serde.rs"); // @@protoc_insertion_point(module) \ No newline at end of file diff --git a/xmtp_proto/src/gen/xmtp.mls.database.serde.rs b/xmtp_proto/src/gen/xmtp.mls.database.serde.rs index fe85cb34c..53757c57c 100644 --- a/xmtp_proto/src/gen/xmtp.mls.database.serde.rs +++ b/xmtp_proto/src/gen/xmtp.mls.database.serde.rs @@ -565,6 +565,169 @@ impl<'de> serde::Deserialize<'de> for InstallationIds { deserializer.deserialize_struct("xmtp.mls.database.InstallationIds", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for PermissionPolicyOption { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + let variant = match self { + Self::Unspecified => "PERMISSION_POLICY_OPTION_UNSPECIFIED", + Self::Allow => "PERMISSION_POLICY_OPTION_ALLOW", + Self::Deny => "PERMISSION_POLICY_OPTION_DENY", + Self::AdminOnly => "PERMISSION_POLICY_OPTION_ADMIN_ONLY", + Self::SuperAdminOnly => "PERMISSION_POLICY_OPTION_SUPER_ADMIN_ONLY", + }; + serializer.serialize_str(variant) + } +} +impl<'de> serde::Deserialize<'de> for PermissionPolicyOption { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "PERMISSION_POLICY_OPTION_UNSPECIFIED", + "PERMISSION_POLICY_OPTION_ALLOW", + "PERMISSION_POLICY_OPTION_DENY", + "PERMISSION_POLICY_OPTION_ADMIN_ONLY", + "PERMISSION_POLICY_OPTION_SUPER_ADMIN_ONLY", + ]; + + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = PermissionPolicyOption; + + 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 { + "PERMISSION_POLICY_OPTION_UNSPECIFIED" => Ok(PermissionPolicyOption::Unspecified), + "PERMISSION_POLICY_OPTION_ALLOW" => Ok(PermissionPolicyOption::Allow), + "PERMISSION_POLICY_OPTION_DENY" => Ok(PermissionPolicyOption::Deny), + "PERMISSION_POLICY_OPTION_ADMIN_ONLY" => Ok(PermissionPolicyOption::AdminOnly), + "PERMISSION_POLICY_OPTION_SUPER_ADMIN_ONLY" => Ok(PermissionPolicyOption::SuperAdminOnly), + _ => Err(serde::de::Error::unknown_variant(value, FIELDS)), + } + } + } + deserializer.deserialize_any(GeneratedVisitor) + } +} +impl serde::Serialize for PermissionUpdateType { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + let variant = match self { + Self::Unspecified => "PERMISSION_UPDATE_TYPE_UNSPECIFIED", + Self::AddMember => "PERMISSION_UPDATE_TYPE_ADD_MEMBER", + Self::RemoveMember => "PERMISSION_UPDATE_TYPE_REMOVE_MEMBER", + Self::AddAdmin => "PERMISSION_UPDATE_TYPE_ADD_ADMIN", + Self::RemoveAdmin => "PERMISSION_UPDATE_TYPE_REMOVE_ADMIN", + Self::UpdateMetadata => "PERMISSION_UPDATE_TYPE_UPDATE_METADATA", + }; + serializer.serialize_str(variant) + } +} +impl<'de> serde::Deserialize<'de> for PermissionUpdateType { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "PERMISSION_UPDATE_TYPE_UNSPECIFIED", + "PERMISSION_UPDATE_TYPE_ADD_MEMBER", + "PERMISSION_UPDATE_TYPE_REMOVE_MEMBER", + "PERMISSION_UPDATE_TYPE_ADD_ADMIN", + "PERMISSION_UPDATE_TYPE_REMOVE_ADMIN", + "PERMISSION_UPDATE_TYPE_UPDATE_METADATA", + ]; + + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = PermissionUpdateType; + + 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 { + "PERMISSION_UPDATE_TYPE_UNSPECIFIED" => Ok(PermissionUpdateType::Unspecified), + "PERMISSION_UPDATE_TYPE_ADD_MEMBER" => Ok(PermissionUpdateType::AddMember), + "PERMISSION_UPDATE_TYPE_REMOVE_MEMBER" => Ok(PermissionUpdateType::RemoveMember), + "PERMISSION_UPDATE_TYPE_ADD_ADMIN" => Ok(PermissionUpdateType::AddAdmin), + "PERMISSION_UPDATE_TYPE_REMOVE_ADMIN" => Ok(PermissionUpdateType::RemoveAdmin), + "PERMISSION_UPDATE_TYPE_UPDATE_METADATA" => Ok(PermissionUpdateType::UpdateMetadata), + _ => Err(serde::de::Error::unknown_variant(value, FIELDS)), + } + } + } + deserializer.deserialize_any(GeneratedVisitor) + } +} impl serde::Serialize for PostCommitAction { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -679,10 +842,12 @@ impl serde::Serialize for post_commit_action::Installation { let mut struct_ser = serializer.serialize_struct("xmtp.mls.database.PostCommitAction.Installation", len)?; if !self.installation_key.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("installationKey", pbjson::private::base64::encode(&self.installation_key).as_str())?; } if !self.hpke_public_key.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("hpkePublicKey", pbjson::private::base64::encode(&self.hpke_public_key).as_str())?; } struct_ser.end() @@ -798,6 +963,7 @@ impl serde::Serialize for post_commit_action::SendWelcomes { } if !self.welcome_message.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("welcomeMessage", pbjson::private::base64::encode(&self.welcome_message).as_str())?; } struct_ser.end() @@ -1188,6 +1354,7 @@ impl serde::Serialize for send_message_data::V1 { let mut struct_ser = serializer.serialize_struct("xmtp.mls.database.SendMessageData.V1", len)?; if !self.payload_bytes.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("payloadBytes", pbjson::private::base64::encode(&self.payload_bytes).as_str())?; } struct_ser.end() @@ -1894,3 +2061,231 @@ impl<'de> serde::Deserialize<'de> for update_metadata_data::V1 { deserializer.deserialize_struct("xmtp.mls.database.UpdateMetadataData.V1", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for UpdatePermissionData { + #[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.UpdatePermissionData", len)?; + if let Some(v) = self.version.as_ref() { + match v { + update_permission_data::Version::V1(v) => { + struct_ser.serialize_field("v1", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for UpdatePermissionData { + #[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, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = UpdatePermissionData; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.database.UpdatePermissionData") + } + + 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(update_permission_data::Version::V1) +; + } + } + } + Ok(UpdatePermissionData { + version: version__, + }) + } + } + deserializer.deserialize_struct("xmtp.mls.database.UpdatePermissionData", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for update_permission_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.permission_update_type != 0 { + len += 1; + } + if self.permission_policy_option != 0 { + len += 1; + } + if self.metadata_field_name.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.database.UpdatePermissionData.V1", len)?; + if self.permission_update_type != 0 { + let v = PermissionUpdateType::try_from(self.permission_update_type) + .map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", self.permission_update_type)))?; + struct_ser.serialize_field("permissionUpdateType", &v)?; + } + if self.permission_policy_option != 0 { + let v = PermissionPolicyOption::try_from(self.permission_policy_option) + .map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", self.permission_policy_option)))?; + struct_ser.serialize_field("permissionPolicyOption", &v)?; + } + if let Some(v) = self.metadata_field_name.as_ref() { + struct_ser.serialize_field("metadataFieldName", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for update_permission_data::V1 { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "permission_update_type", + "permissionUpdateType", + "permission_policy_option", + "permissionPolicyOption", + "metadata_field_name", + "metadataFieldName", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + PermissionUpdateType, + PermissionPolicyOption, + MetadataFieldName, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "permissionUpdateType" | "permission_update_type" => Ok(GeneratedField::PermissionUpdateType), + "permissionPolicyOption" | "permission_policy_option" => Ok(GeneratedField::PermissionPolicyOption), + "metadataFieldName" | "metadata_field_name" => Ok(GeneratedField::MetadataFieldName), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = update_permission_data::V1; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.mls.database.UpdatePermissionData.V1") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut permission_update_type__ = None; + let mut permission_policy_option__ = None; + let mut metadata_field_name__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::PermissionUpdateType => { + if permission_update_type__.is_some() { + return Err(serde::de::Error::duplicate_field("permissionUpdateType")); + } + permission_update_type__ = Some(map_.next_value::()? as i32); + } + GeneratedField::PermissionPolicyOption => { + if permission_policy_option__.is_some() { + return Err(serde::de::Error::duplicate_field("permissionPolicyOption")); + } + permission_policy_option__ = Some(map_.next_value::()? as i32); + } + GeneratedField::MetadataFieldName => { + if metadata_field_name__.is_some() { + return Err(serde::de::Error::duplicate_field("metadataFieldName")); + } + metadata_field_name__ = map_.next_value()?; + } + } + } + Ok(update_permission_data::V1 { + permission_update_type: permission_update_type__.unwrap_or_default(), + permission_policy_option: permission_policy_option__.unwrap_or_default(), + metadata_field_name: metadata_field_name__, + }) + } + } + deserializer.deserialize_struct("xmtp.mls.database.UpdatePermissionData.V1", FIELDS, GeneratedVisitor) + } +} diff --git a/xmtp_proto/src/gen/xmtp.mls.message_contents.rs b/xmtp_proto/src/gen/xmtp.mls.message_contents.rs index a54015412..0e4185a94 100644 --- a/xmtp_proto/src/gen/xmtp.mls.message_contents.rs +++ b/xmtp_proto/src/gen/xmtp.mls.message_contents.rs @@ -1,89 +1,5 @@ // @generated -/// Used for "Grant Messaging Access" associations -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GrantMessagingAccessAssociation { - #[prost(enumeration="AssociationTextVersion", tag="1")] - pub association_text_version: i32, - /// EIP-191 signature - #[prost(message, optional, tag="2")] - pub signature: ::core::option::Option, - #[prost(string, tag="3")] - pub account_address: ::prost::alloc::string::String, - #[prost(uint64, tag="4")] - pub created_ns: u64, -} -/// Used for "Revoke Messaging Access" associations -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct RevokeMessagingAccessAssociation { - #[prost(enumeration="AssociationTextVersion", tag="1")] - pub association_text_version: i32, - /// EIP-191 signature - #[prost(message, optional, tag="2")] - pub signature: ::core::option::Option, - #[prost(string, tag="3")] - pub account_address: ::prost::alloc::string::String, - #[prost(uint64, tag="4")] - pub created_ns: u64, -} -/// LegacyCreateIdentityAssociation is used when a v3 installation key -/// is signed by a v2 identity key, which in turn is signed via a -/// 'CreateIdentity' wallet signature -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct LegacyCreateIdentityAssociation { - /// Signs SHA-256 hash of installation key - #[prost(message, optional, tag="1")] - pub signature: ::core::option::Option, - /// created_ns is encoded inside serialized key, account_address is recoverable - /// from the SignedPublicKey signature - #[prost(message, optional, tag="2")] - pub signed_legacy_create_identity_key: ::core::option::Option, -} -/// RecoverableEcdsaSignature -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct RecoverableEcdsaSignature { - /// 65-bytes \[ R || S || V \], with recovery id as the last byte - #[prost(bytes="vec", tag="1")] - pub bytes: ::prost::alloc::vec::Vec, -} -/// EdDSA signature bytes matching RFC 8032 -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct EdDsaSignature { - #[prost(bytes="vec", tag="1")] - pub bytes: ::prost::alloc::vec::Vec, -} -/// Allows for us to update the format of the association text without -/// incrementing the entire proto -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] -#[repr(i32)] -pub enum AssociationTextVersion { - Unspecified = 0, - AssociationTextVersion1 = 1, -} -impl AssociationTextVersion { - /// 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 { - AssociationTextVersion::Unspecified => "ASSOCIATION_TEXT_VERSION_UNSPECIFIED", - AssociationTextVersion::AssociationTextVersion1 => "ASSOCIATION_TEXT_VERSION_1", - } - } - /// Creates an enum from field names used in the ProtoBuf definition. - pub fn from_str_name(value: &str) -> ::core::option::Option { - match value { - "ASSOCIATION_TEXT_VERSION_UNSPECIFIED" => Some(Self::Unspecified), - "ASSOCIATION_TEXT_VERSION_1" => Some(Self::AssociationTextVersion1), - _ => None, - } - } -} +// This file is @generated by prost-build. /// ContentTypeId is used to identify the type of content stored in a Message. #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -210,14 +126,8 @@ pub struct MessageHistoryReply { /// Generated input 'secret' for the AES Key used to encrypt the message-bundle #[prost(message, optional, tag="3")] pub encryption_key: ::core::option::Option, - /// Generated input 'secret' for the HMAC Key used to sign the bundle_hash - #[prost(message, optional, tag="4")] - pub signing_key: ::core::option::Option, - /// HMAC Signature of the message-bundle - #[prost(bytes="vec", tag="5")] - pub bundle_hash: ::prost::alloc::vec::Vec, } -/// Key used to encrypt or sign the message-bundle +/// Key used to encrypt the message-bundle #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct MessageHistoryKeyType { @@ -261,54 +171,6 @@ impl Compression { } } } -/// A credential that can be used in MLS leaf nodes -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct MlsCredential { - #[prost(bytes="vec", tag="1")] - pub installation_public_key: ::prost::alloc::vec::Vec, - #[prost(oneof="mls_credential::Association", tags="2, 3")] - pub association: ::core::option::Option, -} -/// Nested message and enum types in `MlsCredential`. -pub mod mls_credential { - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Association { - #[prost(message, tag="2")] - MessagingAccess(super::GrantMessagingAccessAssociation), - #[prost(message, tag="3")] - LegacyCreateIdentity(super::LegacyCreateIdentityAssociation), - } -} -/// A declaration and proof that a credential is no longer valid -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct CredentialRevocation { - #[prost(oneof="credential_revocation::PublicKey", tags="1, 2")] - pub public_key: ::core::option::Option, - #[prost(oneof="credential_revocation::Association", tags="3")] - pub association: ::core::option::Option, -} -/// Nested message and enum types in `CredentialRevocation`. -pub mod credential_revocation { - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum PublicKey { - /// The 'installation_public_key' field of the MlsCredential proto - #[prost(bytes, tag="1")] - InstallationKey(::prost::alloc::vec::Vec), - /// The 'key_bytes' field of the legacy SignedPublicKey proto - #[prost(bytes, tag="2")] - UnsignedLegacyCreateIdentityKey(::prost::alloc::vec::Vec), - } - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Association { - #[prost(message, tag="3")] - MessagingAccess(super::RevokeMessagingAccessAssociation), - } -} /// Contains a mapping of `inbox_id` -> `sequence_id` for all members of a group. /// Designed to be stored in the group context extension of the MLS group #[allow(clippy::derive_partial_eq_without_eq)] @@ -328,6 +190,25 @@ pub struct GroupMetadataV1 { pub creator_account_address: ::prost::alloc::string::String, #[prost(string, tag="3")] pub creator_inbox_id: ::prost::alloc::string::String, + /// Should only be present for CONVERSATION_TYPE_DM + #[prost(message, optional, tag="4")] + pub dm_members: ::core::option::Option, +} +/// Wrapper around an Inbox Id +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Inbox { + #[prost(string, tag="1")] + pub inbox_id: ::prost::alloc::string::String, +} +/// Ordering does not matter here +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DmMembers { + #[prost(message, optional, tag="1")] + pub dm_member_one: ::core::option::Option, + #[prost(message, optional, tag="2")] + pub dm_member_two: ::core::option::Option, } /// Defines the type of conversation #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] @@ -687,697 +568,102 @@ pub mod group_updated { } /// Encoded file descriptor set for the `xmtp.mls.message_contents` package pub const FILE_DESCRIPTOR_SET: &[u8] = &[ - 0x0a, 0xf0, 0x17, 0x0a, 0x26, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x78, 0x6d, 0x74, - 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x21, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, - 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xaa, 0x02, 0x0a, 0x1f, 0x47, 0x72, - 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x41, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x6b, 0x0a, - 0x18, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x65, 0x78, - 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x31, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x6f, - 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x78, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x16, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, - 0x65, 0x78, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x09, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, - 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x63, 0x64, 0x73, 0x61, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x27, - 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x64, 0x4e, 0x73, 0x22, 0xab, 0x02, 0x0a, 0x20, 0x52, 0x65, 0x76, 0x6f, 0x6b, - 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x6b, 0x0a, 0x18, 0x61, - 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x5f, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x31, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x78, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x52, 0x16, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x78, - 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x78, 0x6d, - 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, - 0x62, 0x6c, 0x65, 0x45, 0x63, 0x64, 0x73, 0x61, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x27, 0x0a, 0x0f, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, - 0x5f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x64, 0x4e, 0x73, 0x22, 0xe7, 0x01, 0x0a, 0x1f, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x73, 0x73, - 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x78, 0x6d, + 0x0a, 0xec, 0x2b, 0x0a, 0x22, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, + 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x22, 0x95, 0x01, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x79, 0x70, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x79, 0x70, 0x65, 0x49, 0x64, + 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6a, 0x6f, + 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x5f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x22, 0x8f, 0x03, 0x0a, 0x0e, 0x45, + 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, - 0x62, 0x6c, 0x65, 0x45, 0x63, 0x64, 0x73, 0x61, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x70, 0x0a, 0x21, - 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, - 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, - 0x1d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4b, 0x65, 0x79, 0x22, 0x31, - 0x0a, 0x19, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x63, 0x64, - 0x73, 0x61, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x62, - 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x22, 0x26, 0x0a, 0x0e, 0x45, 0x64, 0x44, 0x73, 0x61, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x2a, 0x62, 0x0a, 0x16, 0x41, 0x73, 0x73, - 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x78, 0x74, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x24, 0x41, 0x53, 0x53, 0x4f, 0x43, 0x49, 0x41, 0x54, 0x49, - 0x4f, 0x4e, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, - 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, - 0x1a, 0x41, 0x53, 0x53, 0x4f, 0x43, 0x49, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x45, 0x58, - 0x54, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x31, 0x10, 0x01, 0x42, 0xe5, 0x01, - 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, - 0x10, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, - 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x4d, 0xaa, 0x02, 0x18, 0x58, 0x6d, - 0x74, 0x70, 0x2e, 0x4d, 0x6c, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, 0x18, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, - 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x73, 0xe2, 0x02, 0x24, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x3a, - 0x3a, 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0x93, 0x0d, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x35, 0x01, - 0x0a, 0x27, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x1d, 0x20, 0x41, 0x73, 0x73, - 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, - 0x03, 0x00, 0x22, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x05, 0x00, 0x2b, 0x0a, 0x08, - 0x0a, 0x01, 0x08, 0x12, 0x03, 0x07, 0x00, 0x47, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, - 0x07, 0x00, 0x47, 0x0a, 0x6f, 0x0a, 0x02, 0x05, 0x00, 0x12, 0x04, 0x0c, 0x00, 0x0f, 0x01, 0x1a, - 0x63, 0x20, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x75, 0x73, 0x20, - 0x74, 0x6f, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x6f, - 0x72, 0x6d, 0x61, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x73, 0x73, 0x6f, - 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x77, 0x69, 0x74, - 0x68, 0x6f, 0x75, 0x74, 0x0a, 0x20, 0x69, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x69, - 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x74, 0x69, 0x72, 0x65, 0x20, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x00, 0x01, 0x12, 0x03, 0x0c, 0x05, 0x1b, - 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0d, 0x02, 0x2b, 0x0a, 0x0c, 0x0a, - 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0d, 0x02, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x05, - 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x0d, 0x29, 0x2a, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, - 0x01, 0x12, 0x03, 0x0e, 0x02, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x01, 0x12, - 0x03, 0x0e, 0x02, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x0e, - 0x1f, 0x20, 0x0a, 0x3c, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x12, 0x00, 0x17, 0x01, 0x1a, 0x30, - 0x20, 0x55, 0x73, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x22, 0x47, 0x72, 0x61, 0x6e, 0x74, - 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x20, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x22, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, - 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x12, 0x08, 0x27, 0x0a, 0x0b, 0x0a, 0x04, - 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x13, 0x02, 0x36, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, - 0x00, 0x06, 0x12, 0x03, 0x13, 0x02, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, - 0x12, 0x03, 0x13, 0x19, 0x31, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, - 0x13, 0x34, 0x35, 0x0a, 0x20, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x14, 0x02, 0x2a, - 0x22, 0x13, 0x20, 0x45, 0x49, 0x50, 0x2d, 0x31, 0x39, 0x31, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, - 0x14, 0x02, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x14, 0x1c, - 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x14, 0x28, 0x29, 0x0a, - 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x15, 0x02, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x15, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, - 0x02, 0x02, 0x01, 0x12, 0x03, 0x15, 0x09, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, - 0x03, 0x12, 0x03, 0x15, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x03, 0x12, 0x03, - 0x16, 0x02, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x05, 0x12, 0x03, 0x16, 0x02, - 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x16, 0x09, 0x13, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x03, 0x12, 0x03, 0x16, 0x16, 0x17, 0x0a, 0x3d, 0x0a, - 0x02, 0x04, 0x01, 0x12, 0x04, 0x1a, 0x00, 0x1f, 0x01, 0x1a, 0x31, 0x20, 0x55, 0x73, 0x65, 0x64, - 0x20, 0x66, 0x6f, 0x72, 0x20, 0x22, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x20, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x20, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x20, 0x61, - 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, - 0x04, 0x01, 0x01, 0x12, 0x03, 0x1a, 0x08, 0x28, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, - 0x12, 0x03, 0x1b, 0x02, 0x36, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, - 0x1b, 0x02, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1b, 0x19, - 0x31, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1b, 0x34, 0x35, 0x0a, - 0x20, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, 0x12, 0x03, 0x1c, 0x02, 0x2a, 0x22, 0x13, 0x20, 0x45, - 0x49, 0x50, 0x2d, 0x31, 0x39, 0x31, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x06, 0x12, 0x03, 0x1c, 0x02, 0x1b, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x1c, 0x1c, 0x25, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x1c, 0x28, 0x29, 0x0a, 0x0b, 0x0a, 0x04, 0x04, - 0x01, 0x02, 0x02, 0x12, 0x03, 0x1d, 0x02, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, - 0x05, 0x12, 0x03, 0x1d, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x01, 0x12, - 0x03, 0x1d, 0x09, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x03, 0x12, 0x03, 0x1d, - 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x03, 0x12, 0x03, 0x1e, 0x02, 0x18, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x05, 0x12, 0x03, 0x1e, 0x02, 0x08, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x01, 0x02, 0x03, 0x01, 0x12, 0x03, 0x1e, 0x09, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x01, 0x02, 0x03, 0x03, 0x12, 0x03, 0x1e, 0x16, 0x17, 0x0a, 0xb3, 0x01, 0x0a, 0x02, 0x04, 0x02, - 0x12, 0x04, 0x24, 0x00, 0x2a, 0x01, 0x1a, 0xa6, 0x01, 0x20, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x73, - 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, - 0x64, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x61, 0x20, 0x76, 0x33, 0x20, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6b, 0x65, 0x79, 0x0a, 0x20, 0x69, 0x73, - 0x20, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x61, 0x20, 0x76, 0x32, 0x20, - 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x77, 0x68, - 0x69, 0x63, 0x68, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x73, - 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x69, 0x61, 0x20, 0x61, 0x0a, 0x20, 0x27, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x27, 0x20, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x0a, 0x0a, - 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x24, 0x08, 0x27, 0x0a, 0x35, 0x0a, 0x04, 0x04, - 0x02, 0x02, 0x00, 0x12, 0x03, 0x26, 0x02, 0x2a, 0x1a, 0x28, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x73, - 0x20, 0x53, 0x48, 0x41, 0x2d, 0x32, 0x35, 0x36, 0x20, 0x68, 0x61, 0x73, 0x68, 0x20, 0x6f, 0x66, - 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6b, 0x65, - 0x79, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x06, 0x12, 0x03, 0x26, 0x02, 0x1b, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x26, 0x1c, 0x25, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x26, 0x28, 0x29, 0x0a, 0x7e, 0x0a, 0x04, - 0x04, 0x02, 0x02, 0x01, 0x12, 0x03, 0x29, 0x02, 0x4e, 0x1a, 0x71, 0x20, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x64, 0x5f, 0x6e, 0x73, 0x20, 0x69, 0x73, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, - 0x64, 0x20, 0x69, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, - 0x7a, 0x65, 0x64, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x63, 0x6f, - 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x0a, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, - 0x79, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x02, 0x02, 0x01, 0x06, 0x12, 0x03, 0x29, 0x02, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, - 0x02, 0x01, 0x01, 0x12, 0x03, 0x29, 0x28, 0x49, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, - 0x03, 0x12, 0x03, 0x29, 0x4c, 0x4d, 0x0a, 0x27, 0x0a, 0x02, 0x04, 0x03, 0x12, 0x04, 0x2d, 0x00, - 0x30, 0x01, 0x1a, 0x1b, 0x20, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, - 0x45, 0x63, 0x64, 0x73, 0x61, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x0a, 0x0a, - 0x0a, 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x03, 0x2d, 0x08, 0x21, 0x0a, 0x4a, 0x0a, 0x04, 0x04, - 0x03, 0x02, 0x00, 0x12, 0x03, 0x2f, 0x02, 0x12, 0x1a, 0x3d, 0x20, 0x36, 0x35, 0x2d, 0x62, 0x79, - 0x74, 0x65, 0x73, 0x20, 0x5b, 0x20, 0x52, 0x20, 0x7c, 0x7c, 0x20, 0x53, 0x20, 0x7c, 0x7c, 0x20, - 0x56, 0x20, 0x5d, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x72, 0x65, 0x63, 0x6f, 0x76, 0x65, - 0x72, 0x79, 0x20, 0x69, 0x64, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, - 0x74, 0x20, 0x62, 0x79, 0x74, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x05, - 0x12, 0x03, 0x2f, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x01, 0x12, 0x03, - 0x2f, 0x08, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2f, 0x10, - 0x11, 0x0a, 0x35, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x33, 0x00, 0x35, 0x01, 0x1a, 0x29, 0x20, - 0x45, 0x64, 0x44, 0x53, 0x41, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x52, - 0x46, 0x43, 0x20, 0x38, 0x30, 0x33, 0x32, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x04, 0x01, 0x12, - 0x03, 0x33, 0x08, 0x16, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, 0x03, 0x34, 0x02, - 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x05, 0x12, 0x03, 0x34, 0x02, 0x07, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x34, 0x08, 0x0d, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x34, 0x10, 0x11, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, 0x0a, 0xc7, 0x2e, 0x0a, 0x22, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x78, 0x6d, 0x74, 0x70, - 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x95, 0x01, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x79, - 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x79, 0x70, - 0x65, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, - 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0c, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x6e, 0x6f, 0x72, 0x22, 0x8f, 0x03, - 0x0a, 0x0e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x12, 0x3c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x49, 0x64, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x59, 0x0a, 0x0a, 0x70, + 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x39, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x6e, 0x63, 0x6f, + 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, 0x08, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, + 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x66, 0x61, 0x6c, 0x6c, + 0x62, 0x61, 0x63, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x4d, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x72, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x01, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, + 0x0b, 0x0a, 0x09, 0x5f, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x42, 0x0e, 0x0a, 0x0c, + 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xdf, 0x03, 0x0a, + 0x11, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, + 0x70, 0x65, 0x12, 0x41, 0x0a, 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x49, 0x64, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x59, - 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, - 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, - 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1f, 0x0a, 0x08, 0x66, 0x61, 0x6c, - 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x66, - 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x4d, 0x0a, 0x0b, 0x63, 0x6f, - 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x26, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x70, - 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x01, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x72, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x42, - 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, - 0xdf, 0x03, 0x0a, 0x11, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x76, - 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x41, 0x0a, 0x02, 0x76, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x6c, 0x61, 0x69, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x2e, 0x56, 0x31, 0x48, + 0x00, 0x52, 0x02, 0x76, 0x31, 0x12, 0x41, 0x0a, 0x02, 0x76, 0x32, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x2e, - 0x56, 0x31, 0x48, 0x00, 0x52, 0x02, 0x76, 0x31, 0x12, 0x41, 0x0a, 0x02, 0x76, 0x32, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0x2e, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, - 0x70, 0x65, 0x2e, 0x56, 0x32, 0x48, 0x00, 0x52, 0x02, 0x76, 0x32, 0x1a, 0x47, 0x0a, 0x02, 0x56, - 0x31, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x69, - 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, - 0x79, 0x4b, 0x65, 0x79, 0x1a, 0xef, 0x01, 0x0a, 0x02, 0x56, 0x32, 0x12, 0x27, 0x0a, 0x0f, 0x69, - 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, - 0x79, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x12, 0x4c, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x30, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x46, - 0x0a, 0x05, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x48, 0x00, 0x52, - 0x05, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x42, 0x0e, 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x22, 0x51, 0x0a, 0x15, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x69, 0x6e, - 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x69, 0x6e, - 0x43, 0x6f, 0x64, 0x65, 0x22, 0x93, 0x02, 0x0a, 0x13, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, - 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x57, 0x0a, - 0x0e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, - 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x4b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0d, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x51, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, - 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x78, 0x6d, - 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x73, - 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x75, 0x6e, - 0x64, 0x6c, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, - 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x22, 0x4d, 0x0a, 0x15, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x11, 0x63, 0x68, 0x61, 0x63, 0x68, 0x61, 0x32, 0x30, 0x5f, - 0x70, 0x6f, 0x6c, 0x79, 0x31, 0x33, 0x30, 0x35, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, - 0x52, 0x10, 0x63, 0x68, 0x61, 0x63, 0x68, 0x61, 0x32, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x31, 0x33, - 0x30, 0x35, 0x42, 0x05, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x2a, 0x3c, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, - 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x4f, 0x4d, 0x50, - 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x46, 0x4c, 0x41, 0x54, 0x45, 0x10, - 0x00, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, - 0x5f, 0x47, 0x5a, 0x49, 0x50, 0x10, 0x01, 0x42, 0xe1, 0x01, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, - 0x4d, 0xaa, 0x02, 0x18, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x6c, 0x73, 0x2e, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, 0x18, 0x58, - 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xe2, 0x02, 0x24, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, - 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, - 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0x97, 0x20, 0x0a, 0x06, - 0x12, 0x04, 0x02, 0x00, 0x67, 0x01, 0x0a, 0x7c, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x02, 0x00, 0x12, - 0x1a, 0x72, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x0a, 0x20, 0x43, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x20, 0x66, - 0x72, 0x6f, 0x6d, 0x20, 0x56, 0x32, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x73, 0x6f, 0x20, 0x74, - 0x68, 0x61, 0x74, 0x20, 0x77, 0x65, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x75, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x72, 0x65, 0x74, 0x69, 0x72, 0x65, 0x20, 0x61, 0x6c, 0x6c, - 0x20, 0x56, 0x32, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x04, 0x00, 0x22, 0x0a, 0x08, - 0x0a, 0x01, 0x08, 0x12, 0x03, 0x06, 0x00, 0x47, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, - 0x06, 0x00, 0x47, 0x0a, 0x58, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x0a, 0x00, 0x0f, 0x01, 0x1a, - 0x4c, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x49, 0x64, 0x20, - 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x66, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66, 0x20, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x69, - 0x6e, 0x20, 0x61, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, - 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x0a, 0x08, 0x15, 0x0a, 0x34, 0x0a, 0x04, 0x04, 0x00, 0x02, - 0x00, 0x12, 0x03, 0x0b, 0x02, 0x1a, 0x22, 0x27, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, - 0x74, 0x79, 0x20, 0x67, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x69, - 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x0b, 0x02, 0x08, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0b, 0x09, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0b, 0x18, 0x19, 0x0a, 0x1e, 0x0a, 0x04, 0x04, 0x00, 0x02, - 0x01, 0x12, 0x03, 0x0c, 0x02, 0x15, 0x22, 0x11, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, - 0x01, 0x05, 0x12, 0x03, 0x0c, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, - 0x12, 0x03, 0x0c, 0x09, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, - 0x0c, 0x13, 0x14, 0x0a, 0x28, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x0d, 0x02, 0x1b, - 0x22, 0x1b, 0x20, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x0d, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x0d, 0x09, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, - 0x02, 0x03, 0x12, 0x03, 0x0d, 0x19, 0x1a, 0x0a, 0x28, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x03, 0x12, - 0x03, 0x0e, 0x02, 0x1b, 0x22, 0x1b, 0x20, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x20, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, - 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x05, 0x12, 0x03, 0x0e, 0x02, 0x08, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x0e, 0x09, 0x16, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x00, 0x02, 0x03, 0x03, 0x12, 0x03, 0x0e, 0x19, 0x1a, 0x0a, 0x67, 0x0a, 0x02, 0x05, - 0x00, 0x12, 0x04, 0x13, 0x00, 0x16, 0x01, 0x1a, 0x5b, 0x20, 0x52, 0x65, 0x63, 0x6f, 0x67, 0x6e, - 0x69, 0x7a, 0x65, 0x64, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x20, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x73, 0x0a, 0x20, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x6c, 0x69, 0x6e, 0x74, 0x3a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x45, - 0x4e, 0x55, 0x4d, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x5f, - 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x57, - 0x49, 0x54, 0x48, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x00, 0x01, 0x12, 0x03, 0x13, 0x05, 0x10, - 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x00, 0x12, 0x03, 0x14, 0x02, 0x1a, 0x0a, 0x0c, 0x0a, - 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x14, 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x05, - 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x14, 0x18, 0x19, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, - 0x01, 0x12, 0x03, 0x15, 0x02, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x01, 0x12, - 0x03, 0x15, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x15, - 0x15, 0x16, 0x0a, 0xa3, 0x01, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x1b, 0x00, 0x29, 0x01, 0x1a, - 0x96, 0x01, 0x20, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x20, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x69, 0x6e, 0x67, 0x20, - 0x69, 0x74, 0x73, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, - 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x20, 0x64, 0x65, - 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, - 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, - 0x03, 0x1b, 0x08, 0x16, 0x0a, 0x65, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x1e, 0x02, - 0x19, 0x1a, 0x58, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, - 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x75, 0x73, 0x65, 0x64, - 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x0a, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, - 0x20, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x72, 0x79, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, 0x1e, 0x02, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, - 0x00, 0x01, 0x12, 0x03, 0x1e, 0x10, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, - 0x12, 0x03, 0x1e, 0x17, 0x18, 0x0a, 0x54, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, 0x12, 0x03, 0x20, - 0x02, 0x25, 0x1a, 0x47, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x65, 0x6e, - 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, - 0x72, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x01, 0x02, 0x01, 0x06, 0x12, 0x03, 0x20, 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, - 0x01, 0x01, 0x12, 0x03, 0x20, 0x16, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, - 0x12, 0x03, 0x20, 0x23, 0x24, 0x0a, 0x84, 0x01, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x02, 0x12, 0x03, - 0x23, 0x02, 0x1f, 0x1a, 0x77, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x66, - 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x75, - 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x63, 0x61, 0x73, 0x65, 0x0a, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x64, - 0x65, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x01, 0x02, 0x02, 0x04, 0x12, 0x03, 0x23, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, - 0x02, 0x02, 0x05, 0x12, 0x03, 0x23, 0x0b, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, - 0x01, 0x12, 0x03, 0x23, 0x12, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x03, 0x12, - 0x03, 0x23, 0x1d, 0x1e, 0x0a, 0x6e, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x03, 0x12, 0x03, 0x26, 0x02, - 0x27, 0x1a, 0x61, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6d, - 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x3b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x6c, - 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x0a, - 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, - 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x62, 0x79, - 0x74, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x04, 0x12, 0x03, 0x26, - 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x06, 0x12, 0x03, 0x26, 0x0b, 0x16, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x01, 0x12, 0x03, 0x26, 0x17, 0x22, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x03, 0x12, 0x03, 0x26, 0x25, 0x26, 0x0a, 0x25, 0x0a, 0x04, - 0x04, 0x01, 0x02, 0x04, 0x12, 0x03, 0x28, 0x02, 0x14, 0x1a, 0x18, 0x20, 0x65, 0x6e, 0x63, 0x6f, - 0x64, 0x65, 0x64, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x74, 0x73, 0x65, - 0x6c, 0x66, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x04, 0x05, 0x12, 0x03, 0x28, 0x02, - 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x04, 0x01, 0x12, 0x03, 0x28, 0x08, 0x0f, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x04, 0x03, 0x12, 0x03, 0x28, 0x12, 0x13, 0x0a, 0x55, 0x0a, - 0x02, 0x04, 0x02, 0x12, 0x04, 0x2c, 0x00, 0x4c, 0x01, 0x1a, 0x49, 0x20, 0x41, 0x20, 0x50, 0x6c, - 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x20, - 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x6d, 0x6f, 0x73, 0x74, - 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x67, 0x65, - 0x74, 0x73, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, - 0x4d, 0x4c, 0x53, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x2c, 0x08, 0x19, - 0x0a, 0x33, 0x0a, 0x04, 0x04, 0x02, 0x03, 0x00, 0x12, 0x04, 0x2e, 0x02, 0x34, 0x03, 0x1a, 0x25, - 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x20, 0x65, 0x6e, 0x76, 0x65, - 0x6c, 0x6f, 0x70, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x03, 0x00, 0x01, 0x12, 0x03, - 0x2e, 0x0a, 0x0c, 0x0a, 0x2e, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x30, - 0x04, 0x16, 0x1a, 0x1f, 0x20, 0x45, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6f, - 0x20, 0x62, 0x65, 0x20, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, - 0x30, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, - 0x30, 0x0a, 0x11, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, - 0x30, 0x14, 0x15, 0x0a, 0x8c, 0x01, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, - 0x33, 0x04, 0x1f, 0x1a, 0x7d, 0x20, 0x41, 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, - 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x61, 0x6e, 0x0a, 0x20, 0x70, 0x72, 0x6f, 0x64, 0x75, - 0x63, 0x65, 0x20, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x73, - 0x68, 0x65, 0x73, 0x2e, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x2e, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x33, - 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x33, - 0x0b, 0x1a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x33, - 0x1d, 0x1e, 0x0a, 0x33, 0x0a, 0x04, 0x04, 0x02, 0x03, 0x01, 0x12, 0x04, 0x37, 0x02, 0x44, 0x03, - 0x1a, 0x25, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x20, 0x6f, 0x66, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x20, 0x65, 0x6e, - 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x03, 0x01, 0x01, - 0x12, 0x03, 0x37, 0x0a, 0x0c, 0x0a, 0x8c, 0x01, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, - 0x12, 0x03, 0x3a, 0x04, 0x1f, 0x1a, 0x7d, 0x20, 0x41, 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, - 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x63, 0x61, 0x6e, 0x20, - 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x6e, 0x73, 0x75, 0x72, - 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x61, 0x6e, 0x0a, 0x20, 0x70, 0x72, 0x6f, - 0x64, 0x75, 0x63, 0x65, 0x20, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x68, - 0x61, 0x73, 0x68, 0x65, 0x73, 0x2e, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x2e, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x05, 0x12, - 0x03, 0x3a, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01, 0x12, - 0x03, 0x3a, 0x0b, 0x1a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x03, 0x12, - 0x03, 0x3a, 0x1d, 0x1e, 0x0a, 0x0e, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x01, 0x08, 0x00, 0x12, 0x04, - 0x3c, 0x04, 0x43, 0x05, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x08, 0x00, 0x01, 0x12, - 0x03, 0x3c, 0x0a, 0x16, 0x0a, 0x2e, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x12, 0x03, - 0x3e, 0x06, 0x18, 0x1a, 0x1f, 0x20, 0x45, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x74, - 0x6f, 0x20, 0x62, 0x65, 0x20, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x05, 0x12, - 0x03, 0x3e, 0x06, 0x0b, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x01, 0x12, - 0x03, 0x3e, 0x0c, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x03, 0x12, - 0x03, 0x3e, 0x16, 0x17, 0x0a, 0x45, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x01, 0x02, 0x02, 0x12, 0x03, - 0x40, 0x06, 0x28, 0x1a, 0x36, 0x20, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x20, - 0x73, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x61, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, - 0x74, 0x6f, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x20, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x02, 0x03, 0x01, 0x02, 0x02, 0x06, 0x12, 0x03, 0x40, 0x06, 0x1b, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x02, 0x03, 0x01, 0x02, 0x02, 0x01, 0x12, 0x03, 0x40, 0x1c, 0x23, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x02, 0x03, 0x01, 0x02, 0x02, 0x03, 0x12, 0x03, 0x40, 0x26, 0x27, 0x0a, 0x41, 0x0a, 0x06, 0x04, - 0x02, 0x03, 0x01, 0x02, 0x03, 0x12, 0x03, 0x42, 0x06, 0x24, 0x1a, 0x32, 0x20, 0x53, 0x6f, 0x6d, - 0x65, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, - 0x65, 0x64, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x73, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x61, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x0a, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x03, 0x06, 0x12, 0x03, 0x42, 0x06, 0x19, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x12, 0x03, 0x42, 0x1a, 0x1f, 0x0a, 0x0e, - 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x03, 0x03, 0x12, 0x03, 0x42, 0x22, 0x23, 0x0a, 0x66, - 0x0a, 0x04, 0x04, 0x02, 0x08, 0x00, 0x12, 0x04, 0x48, 0x02, 0x4b, 0x03, 0x1a, 0x58, 0x20, 0x53, - 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x64, 0x65, - 0x63, 0x6c, 0x61, 0x72, 0x65, 0x73, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x45, 0x6e, 0x63, 0x6f, - 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x68, 0x69, 0x73, 0x0a, - 0x20, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, - 0x70, 0x65, 0x20, 0x69, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x08, 0x00, 0x01, 0x12, - 0x03, 0x48, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x49, 0x04, - 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x06, 0x12, 0x03, 0x49, 0x04, 0x06, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x49, 0x07, 0x09, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x49, 0x0c, 0x0d, 0x0a, 0x0b, 0x0a, 0x04, 0x04, - 0x02, 0x02, 0x01, 0x12, 0x03, 0x4a, 0x04, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, - 0x06, 0x12, 0x03, 0x4a, 0x04, 0x06, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x01, 0x12, - 0x03, 0x4a, 0x07, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, 0x03, 0x4a, - 0x0c, 0x0d, 0x0a, 0x57, 0x0a, 0x02, 0x04, 0x03, 0x12, 0x04, 0x4f, 0x00, 0x54, 0x01, 0x1a, 0x4b, - 0x20, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x6f, 0x72, 0x20, 0x6e, 0x65, - 0x77, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, - 0x64, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x68, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x73, 0x65, 0x6e, 0x64, - 0x20, 0x61, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, - 0x03, 0x01, 0x12, 0x03, 0x4f, 0x08, 0x1d, 0x0a, 0x31, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x00, 0x12, - 0x03, 0x51, 0x02, 0x18, 0x1a, 0x24, 0x20, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, 0x61, 0x63, - 0x68, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, - 0x02, 0x00, 0x05, 0x12, 0x03, 0x51, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, - 0x01, 0x12, 0x03, 0x51, 0x09, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, - 0x03, 0x51, 0x16, 0x17, 0x0a, 0x2d, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x01, 0x12, 0x03, 0x53, 0x02, - 0x16, 0x1a, 0x20, 0x20, 0x45, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x73, 0x20, 0x61, 0x20, 0x68, 0x75, - 0x6d, 0x61, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x6f, - 0x6f, 0x70, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x05, 0x12, 0x03, 0x53, 0x02, - 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x12, 0x03, 0x53, 0x09, 0x11, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x03, 0x12, 0x03, 0x53, 0x14, 0x15, 0x0a, 0x5a, 0x0a, - 0x02, 0x04, 0x04, 0x12, 0x04, 0x57, 0x00, 0x62, 0x01, 0x1a, 0x4e, 0x20, 0x50, 0x72, 0x65, 0x2d, - 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x64, 0x20, 0x63, 0x61, 0x70, 0x61, 0x62, 0x6c, 0x65, - 0x20, 0x6f, 0x66, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, - 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x74, 0x68, - 0x69, 0x73, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x04, 0x01, - 0x12, 0x03, 0x57, 0x08, 0x1b, 0x0a, 0x4f, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, 0x03, 0x59, - 0x02, 0x18, 0x1a, 0x42, 0x20, 0x4d, 0x75, 0x73, 0x74, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, - 0x61, 0x6e, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x05, 0x12, - 0x03, 0x59, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x59, - 0x09, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x59, 0x16, 0x17, - 0x0a, 0x37, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x01, 0x12, 0x03, 0x5b, 0x02, 0x11, 0x1a, 0x2a, 0x20, - 0x57, 0x68, 0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x73, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, - 0x76, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, - 0x01, 0x05, 0x12, 0x03, 0x5b, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x01, - 0x12, 0x03, 0x5b, 0x09, 0x0c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x03, 0x12, 0x03, - 0x5b, 0x0f, 0x10, 0x0a, 0x5a, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x02, 0x12, 0x03, 0x5d, 0x02, 0x2b, - 0x1a, 0x4d, 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x70, - 0x75, 0x74, 0x20, 0x27, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x27, 0x20, 0x66, 0x6f, 0x72, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x41, 0x45, 0x53, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x75, 0x73, 0x65, 0x64, - 0x20, 0x74, 0x6f, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2d, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x02, 0x06, 0x12, 0x03, 0x5d, 0x02, 0x17, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x04, 0x02, 0x02, 0x01, 0x12, 0x03, 0x5d, 0x18, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x04, 0x02, 0x02, 0x03, 0x12, 0x03, 0x5d, 0x29, 0x2a, 0x0a, 0x55, 0x0a, 0x04, 0x04, 0x04, 0x02, - 0x03, 0x12, 0x03, 0x5f, 0x02, 0x28, 0x1a, 0x48, 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, - 0x65, 0x64, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x27, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x27, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x4b, - 0x65, 0x79, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x0a, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x03, 0x06, 0x12, 0x03, 0x5f, 0x02, 0x17, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x04, 0x02, 0x03, 0x01, 0x12, 0x03, 0x5f, 0x18, 0x23, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x04, 0x02, 0x03, 0x03, 0x12, 0x03, 0x5f, 0x26, 0x27, 0x0a, 0x33, 0x0a, 0x04, 0x04, 0x04, - 0x02, 0x04, 0x12, 0x03, 0x61, 0x02, 0x18, 0x1a, 0x26, 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x53, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2d, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x04, 0x05, 0x12, 0x03, 0x61, 0x02, 0x07, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x04, 0x02, 0x04, 0x01, 0x12, 0x03, 0x61, 0x08, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x04, 0x02, 0x04, 0x03, 0x12, 0x03, 0x61, 0x16, 0x17, 0x0a, 0x3c, 0x0a, 0x02, 0x04, 0x05, 0x12, - 0x04, 0x65, 0x00, 0x67, 0x01, 0x1a, 0x30, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x75, 0x73, 0x65, 0x64, - 0x20, 0x74, 0x6f, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x73, - 0x69, 0x67, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2d, - 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x05, 0x01, 0x12, 0x03, - 0x65, 0x08, 0x1d, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x08, 0x00, 0x12, 0x03, 0x66, 0x02, 0x2c, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x08, 0x00, 0x01, 0x12, 0x03, 0x66, 0x08, 0x0b, 0x0a, 0x0b, - 0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, 0x12, 0x03, 0x66, 0x0e, 0x2a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x05, 0x02, 0x00, 0x05, 0x12, 0x03, 0x66, 0x0e, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, - 0x00, 0x01, 0x12, 0x03, 0x66, 0x14, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x03, - 0x12, 0x03, 0x66, 0x28, 0x29, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xca, 0x0d, - 0x0a, 0x25, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, - 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x73, 0x1a, 0x26, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb3, 0x02, 0x0a, 0x0d, 0x4d, - 0x6c, 0x73, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x17, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x4b, 0x65, 0x79, 0x12, 0x67, 0x0a, 0x10, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x69, 0x6e, - 0x67, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, + 0x56, 0x32, 0x48, 0x00, 0x52, 0x02, 0x76, 0x32, 0x1a, 0x47, 0x0a, 0x02, 0x56, 0x31, 0x12, 0x18, + 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x64, 0x65, 0x6d, + 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x69, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4b, 0x65, + 0x79, 0x1a, 0xef, 0x01, 0x0a, 0x02, 0x56, 0x32, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x64, 0x65, 0x6d, + 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x69, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4b, 0x65, + 0x79, 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x4c, 0x0a, + 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x47, 0x72, 0x61, 0x6e, 0x74, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x41, - 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0f, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x72, 0x0a, - 0x16, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x69, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x41, 0x73, - 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x14, 0x6c, 0x65, 0x67, - 0x61, 0x63, 0x79, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x42, 0x0d, 0x0a, 0x0b, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x22, 0x9a, 0x02, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, - 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x10, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x4e, 0x0a, 0x23, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, - 0x65, 0x64, 0x5f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x1f, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4c, - 0x65, 0x67, 0x61, 0x63, 0x79, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x4b, 0x65, 0x79, 0x12, 0x68, 0x0a, 0x10, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x3b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x76, - 0x6f, 0x6b, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x41, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x01, 0x52, - 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x42, 0x0c, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x42, 0x0d, - 0x0a, 0x0b, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0xe4, 0x01, - 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, - 0x0f, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, - 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, - 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x4d, 0xaa, 0x02, 0x18, 0x58, 0x6d, 0x74, - 0x70, 0x2e, 0x4d, 0x6c, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, 0x18, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, - 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0xe2, 0x02, 0x24, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, - 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x4a, 0x9b, 0x06, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x1e, 0x01, 0x0a, - 0x27, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x1d, 0x20, 0x43, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x76, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x03, - 0x00, 0x22, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x05, 0x00, 0x30, 0x0a, 0x08, 0x0a, - 0x01, 0x08, 0x12, 0x03, 0x07, 0x00, 0x47, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x07, - 0x00, 0x47, 0x0a, 0x3d, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x0b, 0x00, 0x11, 0x01, 0x1a, 0x31, - 0x20, 0x41, 0x20, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x74, 0x68, - 0x61, 0x74, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, - 0x6e, 0x20, 0x4d, 0x4c, 0x53, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, - 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x0b, 0x08, 0x15, 0x0a, 0x0b, 0x0a, - 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0c, 0x02, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, - 0x02, 0x00, 0x05, 0x12, 0x03, 0x0c, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, - 0x01, 0x12, 0x03, 0x0c, 0x08, 0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, - 0x03, 0x0c, 0x22, 0x23, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x00, 0x08, 0x00, 0x12, 0x04, 0x0d, 0x02, - 0x10, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x08, 0x00, 0x01, 0x12, 0x03, 0x0d, 0x08, 0x13, - 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x0e, 0x04, 0x39, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, 0x0e, 0x04, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x0e, 0x24, 0x34, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, - 0x01, 0x03, 0x12, 0x03, 0x0e, 0x37, 0x38, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x02, 0x12, - 0x03, 0x0f, 0x04, 0x3f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x06, 0x12, 0x03, 0x0f, - 0x04, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x0f, 0x24, 0x3a, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x0f, 0x3d, 0x3e, 0x0a, 0x4a, - 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x14, 0x00, 0x1e, 0x01, 0x1a, 0x3e, 0x20, 0x41, 0x20, 0x64, - 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, - 0x72, 0x6f, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x63, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x20, 0x6c, 0x6f, 0x6e, - 0x67, 0x65, 0x72, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, - 0x01, 0x12, 0x03, 0x14, 0x08, 0x1c, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x01, 0x08, 0x00, 0x12, 0x04, - 0x15, 0x02, 0x1a, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x08, 0x00, 0x01, 0x12, 0x03, 0x15, - 0x08, 0x12, 0x0a, 0x4d, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x17, 0x04, 0x1f, 0x1a, - 0x40, 0x20, 0x54, 0x68, 0x65, 0x20, 0x27, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x27, 0x20, - 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4d, 0x6c, 0x73, - 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x05, 0x12, 0x03, 0x17, 0x04, 0x09, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x17, 0x0a, 0x1a, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x17, 0x1d, 0x1e, 0x0a, 0x48, 0x0a, 0x04, 0x04, - 0x01, 0x02, 0x01, 0x12, 0x03, 0x19, 0x04, 0x32, 0x1a, 0x3b, 0x20, 0x54, 0x68, 0x65, 0x20, 0x27, - 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x27, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x20, 0x53, - 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x20, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x05, 0x12, 0x03, - 0x19, 0x04, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x19, 0x0a, - 0x2d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x19, 0x30, 0x31, 0x0a, - 0x0c, 0x0a, 0x04, 0x04, 0x01, 0x08, 0x01, 0x12, 0x04, 0x1b, 0x02, 0x1d, 0x03, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x01, 0x08, 0x01, 0x01, 0x12, 0x03, 0x1b, 0x08, 0x13, 0x0a, 0x0b, 0x0a, 0x04, 0x04, - 0x01, 0x02, 0x02, 0x12, 0x03, 0x1c, 0x04, 0x3a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, - 0x06, 0x12, 0x03, 0x1c, 0x04, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x01, 0x12, - 0x03, 0x1c, 0x25, 0x35, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x03, 0x12, 0x03, 0x1c, - 0x38, 0x39, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0x90, 0x06, 0x0a, 0x2b, 0x6d, - 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x73, 0x68, 0x69, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x78, 0x6d, 0x74, 0x70, - 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xa0, 0x01, 0x0a, 0x0f, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x51, 0x0a, 0x07, 0x6d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x78, 0x6d, 0x74, + 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x46, 0x0a, 0x05, 0x72, + 0x65, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0xe9, 0x01, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, - 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x14, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x48, 0x00, 0x52, 0x05, 0x72, 0x65, + 0x70, 0x6c, 0x79, 0x42, 0x0e, 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x51, + 0x0a, 0x15, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x69, 0x6e, 0x5f, 0x63, 0x6f, + 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x69, 0x6e, 0x43, 0x6f, 0x64, + 0x65, 0x22, 0x9f, 0x01, 0x0a, 0x13, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x57, 0x0a, 0x0e, 0x65, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4b, 0x65, 0x79, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x0d, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x4b, 0x65, 0x79, 0x22, 0x4d, 0x0a, 0x15, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x11, + 0x63, 0x68, 0x61, 0x63, 0x68, 0x61, 0x32, 0x30, 0x5f, 0x70, 0x6f, 0x6c, 0x79, 0x31, 0x33, 0x30, + 0x35, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x10, 0x63, 0x68, 0x61, 0x63, 0x68, + 0x61, 0x32, 0x30, 0x50, 0x6f, 0x6c, 0x79, 0x31, 0x33, 0x30, 0x35, 0x42, 0x05, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x2a, 0x3c, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, + 0x5f, 0x44, 0x45, 0x46, 0x4c, 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x4f, + 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x47, 0x5a, 0x49, 0x50, 0x10, 0x01, + 0x42, 0xe1, 0x01, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, + 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x42, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, @@ -1389,795 +675,1111 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[ 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xae, 0x02, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x0c, 0x01, 0x0a, - 0x1c, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x12, 0x20, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x0a, 0x0a, 0x08, 0x0a, - 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, 0x22, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x05, 0x00, - 0x47, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x05, 0x00, 0x47, 0x0a, 0xa3, 0x01, 0x0a, - 0x02, 0x04, 0x00, 0x12, 0x04, 0x0a, 0x00, 0x0c, 0x01, 0x1a, 0x96, 0x01, 0x20, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x20, - 0x6f, 0x66, 0x20, 0x60, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x60, 0x20, 0x2d, 0x3e, - 0x20, 0x60, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x60, 0x20, 0x66, - 0x6f, 0x72, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, 0x6f, - 0x66, 0x20, 0x61, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x0a, 0x20, 0x44, 0x65, 0x73, 0x69, - 0x67, 0x6e, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, - 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4d, 0x4c, 0x53, 0x20, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x0a, 0x08, 0x17, 0x0a, 0x0b, - 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0b, 0x02, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x0b, 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, - 0x00, 0x01, 0x12, 0x03, 0x0b, 0x16, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, - 0x12, 0x03, 0x0b, 0x20, 0x21, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xc2, 0x09, - 0x0a, 0x29, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x78, 0x6d, 0x74, - 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xcd, 0x01, 0x0a, 0x0f, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x56, 0x31, 0x12, 0x58, 0x0a, 0x11, 0x63, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, - 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, - 0x70, 0x65, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x5f, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x28, 0x0a, 0x10, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x49, - 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x2a, 0x88, 0x01, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x1d, 0x43, - 0x4f, 0x4e, 0x56, 0x45, 0x52, 0x53, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, - 0x0a, 0x17, 0x43, 0x4f, 0x4e, 0x56, 0x45, 0x52, 0x53, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x43, - 0x4f, 0x4e, 0x56, 0x45, 0x52, 0x53, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x44, 0x4d, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4f, 0x4e, 0x56, 0x45, 0x52, 0x53, - 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x10, - 0x03, 0x42, 0xe7, 0x01, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, - 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x42, 0x12, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, - 0x4d, 0xaa, 0x02, 0x18, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x6c, 0x73, 0x2e, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, 0x18, 0x58, - 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xe2, 0x02, 0x24, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, - 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, - 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xac, 0x04, 0x0a, 0x06, - 0x12, 0x04, 0x01, 0x00, 0x16, 0x01, 0x0a, 0x24, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, - 0x1a, 0x1a, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x69, 0x6d, 0x6d, 0x75, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x08, 0x0a, 0x01, - 0x02, 0x12, 0x03, 0x03, 0x00, 0x22, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x05, 0x00, 0x47, - 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x05, 0x00, 0x47, 0x0a, 0x2f, 0x0a, 0x02, 0x04, - 0x00, 0x12, 0x04, 0x09, 0x00, 0x0e, 0x01, 0x1a, 0x23, 0x20, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, - 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, - 0x04, 0x00, 0x01, 0x12, 0x03, 0x09, 0x08, 0x17, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, - 0x12, 0x03, 0x0a, 0x02, 0x29, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, - 0x0a, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0a, 0x13, - 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0a, 0x27, 0x28, 0x0a, - 0x28, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x0c, 0x02, 0x25, 0x1a, 0x1b, 0x20, 0x54, - 0x68, 0x69, 0x73, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x64, 0x20, 0x73, 0x6f, 0x6f, 0x6e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, - 0x01, 0x05, 0x12, 0x03, 0x0c, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, - 0x12, 0x03, 0x0c, 0x09, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, - 0x0c, 0x23, 0x24, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x0d, 0x02, 0x1e, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x0d, 0x02, 0x08, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x0d, 0x09, 0x19, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x0d, 0x1c, 0x1d, 0x0a, 0x2e, 0x0a, 0x02, 0x05, 0x00, - 0x12, 0x04, 0x11, 0x00, 0x16, 0x01, 0x1a, 0x22, 0x20, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x73, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x63, 0x6f, 0x6e, - 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x00, - 0x01, 0x12, 0x03, 0x11, 0x05, 0x15, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x00, 0x12, 0x03, - 0x12, 0x02, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x12, 0x02, - 0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x12, 0x22, 0x23, 0x0a, - 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x01, 0x12, 0x03, 0x13, 0x02, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, - 0x05, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x13, 0x02, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, - 0x02, 0x01, 0x02, 0x12, 0x03, 0x13, 0x1c, 0x1d, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x02, - 0x12, 0x03, 0x14, 0x02, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, - 0x14, 0x02, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x14, 0x19, - 0x1a, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x03, 0x12, 0x03, 0x15, 0x02, 0x1d, 0x0a, 0x0c, - 0x0a, 0x05, 0x05, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x15, 0x02, 0x18, 0x0a, 0x0c, 0x0a, 0x05, - 0x05, 0x00, 0x02, 0x03, 0x02, 0x12, 0x03, 0x15, 0x1b, 0x1c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, 0x0a, 0x92, 0x0a, 0x0a, 0x31, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xb0, 0x1e, 0x0a, 0x06, 0x12, 0x04, 0x02, 0x00, 0x65, 0x01, 0x0a, + 0x7c, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x02, 0x00, 0x12, 0x1a, 0x72, 0x20, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x0a, + 0x20, 0x43, 0x6f, 0x70, 0x69, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x56, 0x32, 0x20, + 0x63, 0x6f, 0x64, 0x65, 0x20, 0x73, 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x65, 0x20, + 0x63, 0x61, 0x6e, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x75, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x72, + 0x65, 0x74, 0x69, 0x72, 0x65, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x56, 0x32, 0x20, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x08, 0x0a, + 0x01, 0x02, 0x12, 0x03, 0x04, 0x00, 0x22, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x06, 0x00, + 0x47, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x06, 0x00, 0x47, 0x0a, 0x58, 0x0a, 0x02, + 0x04, 0x00, 0x12, 0x04, 0x0a, 0x00, 0x0f, 0x01, 0x1a, 0x4c, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x49, 0x64, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, + 0x20, 0x74, 0x6f, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x0a, + 0x08, 0x15, 0x0a, 0x34, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0b, 0x02, 0x1a, 0x22, + 0x27, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x67, 0x6f, 0x76, 0x65, + 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, + 0x05, 0x12, 0x03, 0x0b, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, + 0x03, 0x0b, 0x09, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0b, + 0x18, 0x19, 0x0a, 0x1e, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x0c, 0x02, 0x15, 0x22, + 0x11, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x0c, 0x02, 0x08, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x0c, 0x09, 0x10, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x0c, 0x13, 0x14, 0x0a, 0x28, 0x0a, 0x04, + 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x0d, 0x02, 0x1b, 0x22, 0x1b, 0x20, 0x6d, 0x61, 0x6a, 0x6f, + 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x05, 0x12, + 0x03, 0x0d, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x0d, + 0x09, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x0d, 0x19, 0x1a, + 0x0a, 0x28, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x03, 0x12, 0x03, 0x0e, 0x02, 0x1b, 0x22, 0x1b, 0x20, + 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, + 0x02, 0x03, 0x05, 0x12, 0x03, 0x0e, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, + 0x01, 0x12, 0x03, 0x0e, 0x09, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x03, 0x12, + 0x03, 0x0e, 0x19, 0x1a, 0x0a, 0x67, 0x0a, 0x02, 0x05, 0x00, 0x12, 0x04, 0x13, 0x00, 0x16, 0x01, + 0x1a, 0x5b, 0x20, 0x52, 0x65, 0x63, 0x6f, 0x67, 0x6e, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x63, 0x6f, + 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, + 0x74, 0x68, 0x6d, 0x73, 0x0a, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6c, 0x69, 0x6e, 0x74, 0x3a, + 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x46, 0x49, 0x45, + 0x4c, 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x5f, 0x5a, 0x45, 0x52, 0x4f, 0x5f, 0x56, 0x41, + 0x4c, 0x55, 0x45, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x57, 0x49, 0x54, 0x48, 0x0a, 0x0a, 0x0a, 0x0a, + 0x03, 0x05, 0x00, 0x01, 0x12, 0x03, 0x13, 0x05, 0x10, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, + 0x00, 0x12, 0x03, 0x14, 0x02, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, 0x12, + 0x03, 0x14, 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x14, + 0x18, 0x19, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x01, 0x12, 0x03, 0x15, 0x02, 0x17, 0x0a, + 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x15, 0x02, 0x12, 0x0a, 0x0c, 0x0a, + 0x05, 0x05, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x15, 0x15, 0x16, 0x0a, 0xa3, 0x01, 0x0a, 0x02, + 0x04, 0x01, 0x12, 0x04, 0x1b, 0x00, 0x29, 0x01, 0x1a, 0x96, 0x01, 0x20, 0x45, 0x6e, 0x63, 0x6f, + 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x62, 0x75, 0x6e, 0x64, 0x6c, + 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x77, + 0x69, 0x74, 0x68, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x66, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x73, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x0a, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, + 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, + 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x1b, 0x08, 0x16, 0x0a, 0x65, 0x0a, + 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x1e, 0x02, 0x19, 0x1a, 0x58, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x61, 0x74, + 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x77, + 0x69, 0x74, 0x68, 0x0a, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, + 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, + 0x65, 0x72, 0x79, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, 0x1e, + 0x02, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1e, 0x10, 0x14, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1e, 0x17, 0x18, 0x0a, 0x54, + 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, 0x12, 0x03, 0x20, 0x02, 0x25, 0x1a, 0x47, 0x20, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, + 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x06, 0x12, 0x03, 0x20, + 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x20, 0x16, 0x20, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x20, 0x23, 0x24, 0x0a, 0x84, + 0x01, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x02, 0x12, 0x03, 0x23, 0x02, 0x1f, 0x1a, 0x77, 0x20, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, + 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x68, 0x61, 0x74, + 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, + 0x63, 0x61, 0x73, 0x65, 0x0a, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x6f, + 0x72, 0x20, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x04, 0x12, 0x03, + 0x23, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x05, 0x12, 0x03, 0x23, 0x0b, + 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x01, 0x12, 0x03, 0x23, 0x12, 0x1a, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x03, 0x12, 0x03, 0x23, 0x1d, 0x1e, 0x0a, 0x6e, 0x0a, + 0x04, 0x04, 0x01, 0x02, 0x03, 0x12, 0x03, 0x26, 0x02, 0x27, 0x1a, 0x61, 0x20, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x3b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x69, 0x6e, 0x64, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, + 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, + 0x73, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x01, 0x02, 0x03, 0x04, 0x12, 0x03, 0x26, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x01, 0x02, 0x03, 0x06, 0x12, 0x03, 0x26, 0x0b, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, + 0x03, 0x01, 0x12, 0x03, 0x26, 0x17, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x03, + 0x12, 0x03, 0x26, 0x25, 0x26, 0x0a, 0x25, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x04, 0x12, 0x03, 0x28, + 0x02, 0x14, 0x1a, 0x18, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x66, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x01, 0x02, 0x04, 0x05, 0x12, 0x03, 0x28, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, + 0x02, 0x04, 0x01, 0x12, 0x03, 0x28, 0x08, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x04, + 0x03, 0x12, 0x03, 0x28, 0x12, 0x13, 0x0a, 0x55, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x2c, 0x00, + 0x4c, 0x01, 0x1a, 0x49, 0x20, 0x41, 0x20, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x6f, 0x75, 0x74, 0x65, 0x72, 0x6d, 0x6f, 0x73, 0x74, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x67, 0x65, 0x74, 0x73, 0x20, 0x65, 0x6e, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x4d, 0x4c, 0x53, 0x0a, 0x0a, 0x0a, 0x0a, + 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x2c, 0x08, 0x19, 0x0a, 0x33, 0x0a, 0x04, 0x04, 0x02, 0x03, + 0x00, 0x12, 0x04, 0x2e, 0x02, 0x34, 0x03, 0x1a, 0x25, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x20, 0x31, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x65, 0x64, 0x20, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x0a, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x02, 0x03, 0x00, 0x01, 0x12, 0x03, 0x2e, 0x0a, 0x0c, 0x0a, 0x2e, 0x0a, 0x06, + 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x30, 0x04, 0x16, 0x1a, 0x1f, 0x20, 0x45, 0x78, + 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x45, 0x6e, 0x63, + 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x30, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x30, 0x0a, 0x11, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x30, 0x14, 0x15, 0x0a, 0x8c, 0x01, 0x0a, + 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x33, 0x04, 0x1f, 0x1a, 0x7d, 0x20, 0x41, + 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x74, 0x68, + 0x61, 0x74, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, + 0x6f, 0x20, 0x65, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x63, + 0x61, 0x6e, 0x0a, 0x20, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x20, 0x64, 0x69, 0x66, 0x66, + 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x2e, 0x20, 0x4d, 0x61, + 0x79, 0x20, 0x62, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x02, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x33, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x02, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x33, 0x0b, 0x1a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x02, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x33, 0x1d, 0x1e, 0x0a, 0x33, 0x0a, 0x04, 0x04, + 0x02, 0x03, 0x01, 0x12, 0x04, 0x37, 0x02, 0x44, 0x03, 0x1a, 0x25, 0x20, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x20, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x0a, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x03, 0x01, 0x01, 0x12, 0x03, 0x37, 0x0a, 0x0c, 0x0a, 0x8c, + 0x01, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x12, 0x03, 0x3a, 0x04, 0x1f, 0x1a, 0x7d, + 0x20, 0x41, 0x20, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, + 0x74, 0x68, 0x61, 0x74, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, + 0x20, 0x74, 0x6f, 0x20, 0x65, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x20, 0x63, 0x61, 0x6e, 0x0a, 0x20, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x20, 0x64, 0x69, + 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x2e, 0x20, + 0x4d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x65, + 0x72, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x0a, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x05, 0x12, 0x03, 0x3a, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x3a, 0x0b, 0x1a, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x3a, 0x1d, 0x1e, 0x0a, 0x0e, 0x0a, + 0x06, 0x04, 0x02, 0x03, 0x01, 0x08, 0x00, 0x12, 0x04, 0x3c, 0x04, 0x43, 0x05, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x02, 0x03, 0x01, 0x08, 0x00, 0x01, 0x12, 0x03, 0x3c, 0x0a, 0x16, 0x0a, 0x2e, 0x0a, + 0x06, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x12, 0x03, 0x3e, 0x06, 0x18, 0x1a, 0x1f, 0x20, 0x45, + 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x45, 0x6e, + 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x0a, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x05, 0x12, 0x03, 0x3e, 0x06, 0x0b, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x3e, 0x0c, 0x13, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x3e, 0x16, 0x17, 0x0a, 0x45, 0x0a, + 0x06, 0x04, 0x02, 0x03, 0x01, 0x02, 0x02, 0x12, 0x03, 0x40, 0x06, 0x28, 0x1a, 0x36, 0x20, 0x49, + 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x61, + 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x65, 0x63, 0x65, + 0x69, 0x76, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x68, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x02, 0x06, 0x12, + 0x03, 0x40, 0x06, 0x1b, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x02, 0x01, 0x12, + 0x03, 0x40, 0x1c, 0x23, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x02, 0x03, 0x12, + 0x03, 0x40, 0x26, 0x27, 0x0a, 0x41, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x01, 0x02, 0x03, 0x12, 0x03, + 0x42, 0x06, 0x24, 0x1a, 0x32, 0x20, 0x53, 0x6f, 0x6d, 0x65, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, + 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x61, + 0x20, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, + 0x03, 0x06, 0x12, 0x03, 0x42, 0x06, 0x19, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, + 0x03, 0x01, 0x12, 0x03, 0x42, 0x1a, 0x1f, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, + 0x03, 0x03, 0x12, 0x03, 0x42, 0x22, 0x23, 0x0a, 0x66, 0x0a, 0x04, 0x04, 0x02, 0x08, 0x00, 0x12, + 0x04, 0x48, 0x02, 0x4b, 0x03, 0x1a, 0x58, 0x20, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x64, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x65, 0x73, 0x20, + 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x20, 0x74, 0x68, 0x69, 0x73, 0x0a, 0x20, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x20, 0x69, 0x73, 0x0a, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x08, 0x00, 0x01, 0x12, 0x03, 0x48, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, + 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x49, 0x04, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, + 0x02, 0x00, 0x06, 0x12, 0x03, 0x49, 0x04, 0x06, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, + 0x01, 0x12, 0x03, 0x49, 0x07, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, + 0x03, 0x49, 0x0c, 0x0d, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x01, 0x12, 0x03, 0x4a, 0x04, + 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x06, 0x12, 0x03, 0x4a, 0x04, 0x06, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x01, 0x12, 0x03, 0x4a, 0x07, 0x09, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, 0x03, 0x4a, 0x0c, 0x0d, 0x0a, 0x57, 0x0a, 0x02, 0x04, + 0x03, 0x12, 0x04, 0x4f, 0x00, 0x54, 0x01, 0x1a, 0x4b, 0x20, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, + 0x74, 0x6f, 0x72, 0x20, 0x6f, 0x72, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x64, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x20, + 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x03, 0x4f, 0x08, 0x1d, + 0x0a, 0x31, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x00, 0x12, 0x03, 0x51, 0x02, 0x18, 0x1a, 0x24, 0x20, + 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x05, 0x12, 0x03, 0x51, 0x02, + 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x01, 0x12, 0x03, 0x51, 0x09, 0x13, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, 0x51, 0x16, 0x17, 0x0a, 0x2d, 0x0a, + 0x04, 0x04, 0x03, 0x02, 0x01, 0x12, 0x03, 0x53, 0x02, 0x16, 0x1a, 0x20, 0x20, 0x45, 0x6e, 0x73, + 0x75, 0x72, 0x65, 0x73, 0x20, 0x61, 0x20, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x69, 0x73, 0x20, + 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x6f, 0x6f, 0x70, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x03, 0x02, 0x01, 0x05, 0x12, 0x03, 0x53, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, + 0x02, 0x01, 0x01, 0x12, 0x03, 0x53, 0x09, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, + 0x03, 0x12, 0x03, 0x53, 0x14, 0x15, 0x0a, 0x5a, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x57, 0x00, + 0x5e, 0x01, 0x1a, 0x4e, 0x20, 0x50, 0x72, 0x65, 0x2d, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, + 0x67, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, + 0x64, 0x20, 0x63, 0x61, 0x70, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x75, 0x70, + 0x70, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x20, 0x73, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x70, 0x6c, + 0x79, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x04, 0x01, 0x12, 0x03, 0x57, 0x08, 0x1b, 0x0a, 0x4f, + 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, 0x03, 0x59, 0x02, 0x18, 0x1a, 0x42, 0x20, 0x4d, 0x75, + 0x73, 0x74, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, 0x69, 0x73, + 0x74, 0x69, 0x6e, 0x67, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x20, + 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x68, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x05, 0x12, 0x03, 0x59, 0x02, 0x08, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x59, 0x09, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x59, 0x16, 0x17, 0x0a, 0x37, 0x0a, 0x04, 0x04, 0x04, 0x02, + 0x01, 0x12, 0x03, 0x5b, 0x02, 0x11, 0x1a, 0x2a, 0x20, 0x57, 0x68, 0x65, 0x72, 0x65, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x20, 0x63, 0x61, 0x6e, 0x20, + 0x62, 0x65, 0x20, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, + 0x6d, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x05, 0x12, 0x03, 0x5b, 0x02, 0x08, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x01, 0x12, 0x03, 0x5b, 0x09, 0x0c, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x03, 0x12, 0x03, 0x5b, 0x0f, 0x10, 0x0a, 0x5a, 0x0a, 0x04, + 0x04, 0x04, 0x02, 0x02, 0x12, 0x03, 0x5d, 0x02, 0x2b, 0x1a, 0x4d, 0x20, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x27, 0x73, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x27, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x41, 0x45, 0x53, + 0x20, 0x4b, 0x65, 0x79, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x6e, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x2d, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x02, + 0x06, 0x12, 0x03, 0x5d, 0x02, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x02, 0x01, 0x12, + 0x03, 0x5d, 0x18, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x02, 0x03, 0x12, 0x03, 0x5d, + 0x29, 0x2a, 0x0a, 0x34, 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04, 0x61, 0x00, 0x65, 0x01, 0x1a, 0x28, + 0x20, 0x4b, 0x65, 0x79, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x6e, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x2d, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x05, 0x01, 0x12, + 0x03, 0x61, 0x08, 0x1d, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x05, 0x08, 0x00, 0x12, 0x04, 0x62, 0x02, + 0x64, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x08, 0x00, 0x01, 0x12, 0x03, 0x62, 0x08, 0x0b, + 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, 0x12, 0x03, 0x63, 0x04, 0x20, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x05, 0x02, 0x00, 0x05, 0x12, 0x03, 0x63, 0x04, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x05, 0x02, 0x00, 0x01, 0x12, 0x03, 0x63, 0x0a, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, + 0x00, 0x03, 0x12, 0x03, 0x63, 0x1e, 0x1f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, + 0x90, 0x06, 0x0a, 0x2b, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x19, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xa0, 0x01, 0x0a, 0x0f, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x12, 0x51, + 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x37, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x2e, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0xe9, 0x01, + 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, + 0x14, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, + 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x4d, 0xaa, + 0x02, 0x18, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x6c, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, 0x18, 0x58, 0x6d, 0x74, + 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x73, 0xe2, 0x02, 0x24, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, + 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, + 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1a, 0x58, + 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xae, 0x02, 0x0a, 0x06, 0x12, 0x04, + 0x01, 0x00, 0x0c, 0x01, 0x0a, 0x1c, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x12, + 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, + 0x70, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, 0x22, 0x0a, 0x08, 0x0a, 0x01, + 0x08, 0x12, 0x03, 0x05, 0x00, 0x47, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x05, 0x00, + 0x47, 0x0a, 0xa3, 0x01, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x0a, 0x00, 0x0c, 0x01, 0x1a, 0x96, + 0x01, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x6d, 0x61, 0x70, + 0x70, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x60, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, + 0x64, 0x60, 0x20, 0x2d, 0x3e, 0x20, 0x60, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x60, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x0a, + 0x20, 0x44, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x65, 0x78, 0x74, 0x65, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4d, 0x4c, 0x53, + 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, + 0x0a, 0x08, 0x17, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0b, 0x02, 0x22, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x0b, 0x02, 0x15, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0b, 0x16, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0b, 0x20, 0x21, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, 0x0a, 0xe5, 0x0e, 0x0a, 0x29, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x5f, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, - 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x22, 0xcb, 0x02, 0x0a, 0x16, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x75, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x56, 0x31, 0x12, 0x61, - 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x56, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, - 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x65, 0x73, 0x52, 0x09, 0x61, 0x64, 0x6d, 0x69, 0x6e, - 0x4c, 0x69, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x65, 0x72, 0x5f, 0x61, 0x64, - 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, - 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x78, - 0x65, 0x73, 0x52, 0x0e, 0x73, 0x75, 0x70, 0x65, 0x72, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x4c, 0x69, - 0x73, 0x74, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0x26, 0x0a, 0x07, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, - 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x08, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x73, 0x42, 0xee, 0x01, 0x0a, 0x1d, 0x63, 0x6f, - 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x19, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x4d, - 0xaa, 0x02, 0x18, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x6c, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, 0x18, 0x58, 0x6d, - 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xe2, 0x02, 0x24, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, - 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1a, - 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xd2, 0x04, 0x0a, 0x06, 0x12, - 0x04, 0x01, 0x00, 0x16, 0x01, 0x0a, 0x22, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, - 0x18, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, - 0x03, 0x00, 0x22, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x05, 0x00, 0x47, 0x0a, 0x09, 0x0a, - 0x02, 0x08, 0x0b, 0x12, 0x03, 0x05, 0x00, 0x47, 0x0a, 0x30, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, - 0x0a, 0x00, 0x11, 0x01, 0x1a, 0x24, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, - 0x6f, 0x72, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, - 0x01, 0x12, 0x03, 0x0a, 0x08, 0x1e, 0x0a, 0x4a, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, - 0x0c, 0x02, 0x25, 0x1a, 0x3d, 0x20, 0x4d, 0x61, 0x70, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x20, 0x76, 0x61, 0x72, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x20, 0x28, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x2e, - 0x29, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x0c, 0x02, 0x15, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0c, 0x16, 0x20, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0c, 0x23, 0x24, 0x0a, 0x0b, 0x0a, 0x04, - 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x0d, 0x02, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, - 0x01, 0x06, 0x12, 0x03, 0x0d, 0x02, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, - 0x12, 0x03, 0x0d, 0x0a, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, - 0x0d, 0x17, 0x18, 0x0a, 0x64, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x10, 0x02, 0x1f, - 0x1a, 0x57, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x73, 0x20, 0x61, 0x73, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x73, 0x75, 0x70, 0x65, 0x72, 0x5f, - 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0a, 0x20, 0x4f, 0x6e, 0x6c, 0x79, 0x20, 0x73, 0x75, 0x70, 0x65, - 0x72, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x2f, - 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x73, 0x75, 0x70, - 0x65, 0x72, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, - 0x02, 0x06, 0x12, 0x03, 0x10, 0x02, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x01, - 0x12, 0x03, 0x10, 0x0a, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, - 0x10, 0x1d, 0x1e, 0x0a, 0x39, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x14, 0x00, 0x16, 0x01, 0x1a, - 0x2d, 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, - 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x72, 0x65, 0x70, 0x65, 0x61, - 0x74, 0x65, 0x64, 0x20, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x20, 0x49, 0x64, 0x73, 0x0a, 0x0a, 0x0a, - 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x14, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, - 0x02, 0x00, 0x12, 0x03, 0x15, 0x02, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x04, - 0x12, 0x03, 0x15, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x05, 0x12, 0x03, - 0x15, 0x0b, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x15, 0x12, - 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x15, 0x1e, 0x1f, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xfb, 0x31, 0x0a, 0x2c, 0x6d, 0x6c, 0x73, 0x2f, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, - 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x22, 0x5d, 0x0a, 0x19, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x75, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x56, 0x31, - 0x12, 0x40, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x53, 0x65, 0x74, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, - 0x65, 0x73, 0x22, 0xdf, 0x05, 0x0a, 0x09, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x53, 0x65, 0x74, - 0x12, 0x57, 0x0a, 0x11, 0x61, 0x64, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x6d, - 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, - 0x69, 0x70, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0f, 0x61, 0x64, 0x64, 0x4d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x5d, 0x0a, 0x14, 0x72, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, - 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x52, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x74, 0x0a, 0x16, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, - 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x53, 0x65, 0x74, 0x2e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x5c, - 0x0a, 0x10, 0x61, 0x64, 0x64, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, - 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0e, 0x61, 0x64, - 0x64, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x62, 0x0a, 0x13, - 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x78, 0x6d, 0x74, 0x70, - 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x11, 0x72, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x12, 0x6e, 0x0a, 0x19, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, + 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x19, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xa6, 0x02, 0x0a, 0x0f, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x56, 0x31, 0x12, + 0x58, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x62, + 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x12, 0x48, 0x0a, 0x0a, 0x64, + 0x6d, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x24, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x44, 0x6d, 0x4d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x73, 0x48, 0x00, 0x52, 0x09, 0x64, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x73, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, 0x6d, 0x5f, 0x6d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x73, 0x22, 0x22, 0x0a, 0x05, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x12, 0x19, 0x0a, + 0x08, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x22, 0x97, 0x01, 0x0a, 0x09, 0x44, 0x6d, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x44, 0x0a, 0x0d, 0x64, 0x6d, 0x5f, 0x6d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x5f, 0x6f, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, + 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x52, + 0x0b, 0x64, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4f, 0x6e, 0x65, 0x12, 0x44, 0x0a, 0x0d, + 0x64, 0x6d, 0x5f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x74, 0x77, 0x6f, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, - 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x17, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x1a, 0x72, 0x0a, 0x19, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x52, 0x0b, 0x64, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x54, + 0x77, 0x6f, 0x2a, 0x88, 0x01, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x1d, 0x43, 0x4f, 0x4e, 0x56, 0x45, + 0x52, 0x53, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, + 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x43, 0x4f, + 0x4e, 0x56, 0x45, 0x52, 0x53, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x47, 0x52, 0x4f, 0x55, 0x50, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4e, 0x56, 0x45, + 0x52, 0x53, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x4d, 0x10, + 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4f, 0x4e, 0x56, 0x45, 0x52, 0x53, 0x41, 0x54, 0x49, 0x4f, + 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x10, 0x03, 0x42, 0xe7, 0x01, + 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, + 0x12, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, + 0x67, 0x6f, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x4d, 0xaa, 0x02, 0x18, + 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x6c, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, 0x18, 0x58, 0x6d, 0x74, 0x70, 0x5c, + 0x4d, 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0xe2, 0x02, 0x24, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, + 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1a, 0x58, 0x6d, 0x74, + 0x70, 0x3a, 0x3a, 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xb8, 0x07, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, + 0x23, 0x01, 0x0a, 0x24, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x1a, 0x20, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x20, 0x69, 0x6d, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x03, + 0x00, 0x22, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x05, 0x00, 0x47, 0x0a, 0x09, 0x0a, 0x02, + 0x08, 0x0b, 0x12, 0x03, 0x05, 0x00, 0x47, 0x0a, 0x2f, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x09, + 0x00, 0x10, 0x01, 0x1a, 0x23, 0x20, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, + 0x03, 0x09, 0x08, 0x17, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0a, 0x02, + 0x29, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x0a, 0x02, 0x12, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0a, 0x13, 0x24, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0a, 0x27, 0x28, 0x0a, 0x28, 0x0a, 0x04, 0x04, + 0x00, 0x02, 0x01, 0x12, 0x03, 0x0c, 0x02, 0x25, 0x1a, 0x1b, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, + 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, + 0x73, 0x6f, 0x6f, 0x6e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, + 0x0c, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x0c, 0x09, + 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x0c, 0x23, 0x24, 0x0a, + 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x0d, 0x02, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x0d, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, + 0x02, 0x02, 0x01, 0x12, 0x03, 0x0d, 0x09, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, + 0x03, 0x12, 0x03, 0x0d, 0x1c, 0x1d, 0x0a, 0x3e, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x03, 0x12, 0x03, + 0x0f, 0x02, 0x24, 0x1a, 0x31, 0x20, 0x53, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x6e, 0x6c, + 0x79, 0x20, 0x62, 0x65, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x43, 0x4f, 0x4e, 0x56, 0x45, 0x52, 0x53, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x44, 0x4d, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x04, 0x12, + 0x03, 0x0f, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x06, 0x12, 0x03, 0x0f, + 0x0b, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x0f, 0x15, 0x1f, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x03, 0x03, 0x12, 0x03, 0x0f, 0x22, 0x23, 0x0a, 0x2e, + 0x0a, 0x02, 0x05, 0x00, 0x12, 0x04, 0x13, 0x00, 0x18, 0x01, 0x1a, 0x22, 0x20, 0x44, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x66, + 0x20, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x0a, 0x0a, + 0x0a, 0x03, 0x05, 0x00, 0x01, 0x12, 0x03, 0x13, 0x05, 0x15, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, + 0x02, 0x00, 0x12, 0x03, 0x14, 0x02, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, + 0x12, 0x03, 0x14, 0x02, 0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, + 0x14, 0x22, 0x23, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x01, 0x12, 0x03, 0x15, 0x02, 0x1e, + 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x15, 0x02, 0x19, 0x0a, 0x0c, + 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x15, 0x1c, 0x1d, 0x0a, 0x0b, 0x0a, 0x04, + 0x05, 0x00, 0x02, 0x02, 0x12, 0x03, 0x16, 0x02, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, + 0x02, 0x01, 0x12, 0x03, 0x16, 0x02, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x02, + 0x12, 0x03, 0x16, 0x19, 0x1a, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x03, 0x12, 0x03, 0x17, + 0x02, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x17, 0x02, 0x18, + 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x03, 0x02, 0x12, 0x03, 0x17, 0x1b, 0x1c, 0x0a, 0x28, + 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x1b, 0x00, 0x1d, 0x01, 0x1a, 0x1c, 0x20, 0x57, 0x72, 0x61, + 0x70, 0x70, 0x65, 0x72, 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x61, 0x6e, 0x20, 0x49, + 0x6e, 0x62, 0x6f, 0x78, 0x20, 0x49, 0x64, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, + 0x03, 0x1b, 0x08, 0x0d, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x1c, 0x02, + 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x05, 0x12, 0x03, 0x1c, 0x02, 0x08, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1c, 0x09, 0x11, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1c, 0x14, 0x15, 0x0a, 0x2b, 0x0a, 0x02, 0x04, + 0x02, 0x12, 0x04, 0x20, 0x00, 0x23, 0x01, 0x1a, 0x1f, 0x20, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x69, + 0x6e, 0x67, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6d, 0x61, 0x74, 0x74, + 0x65, 0x72, 0x20, 0x68, 0x65, 0x72, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, + 0x03, 0x20, 0x08, 0x11, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x21, 0x02, + 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x06, 0x12, 0x03, 0x21, 0x02, 0x07, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x21, 0x08, 0x15, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x21, 0x18, 0x19, 0x0a, 0x0b, 0x0a, 0x04, 0x04, + 0x02, 0x02, 0x01, 0x12, 0x03, 0x22, 0x02, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, + 0x06, 0x12, 0x03, 0x22, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x01, 0x12, + 0x03, 0x22, 0x08, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, 0x03, 0x22, + 0x18, 0x19, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0x92, 0x0a, 0x0a, 0x31, 0x6d, + 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x19, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xcb, 0x02, 0x0a, 0x16, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x56, 0x31, 0x12, 0x61, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x75, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x56, 0x31, 0x2e, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, + 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x65, + 0x73, 0x52, 0x09, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x10, + 0x73, 0x75, 0x70, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x69, 0x73, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, + 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x65, 0x73, 0x52, 0x0e, 0x73, 0x75, 0x70, 0x65, + 0x72, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x3f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, - 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8a, 0x05, 0x0a, 0x10, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, - 0x68, 0x69, 0x70, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x4c, 0x0a, 0x04, 0x62, 0x61, 0x73, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x26, 0x0a, 0x07, 0x49, 0x6e, 0x62, + 0x6f, 0x78, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, + 0x73, 0x42, 0xee, 0x01, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x48, - 0x00, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x5f, 0x0a, 0x0d, 0x61, 0x6e, 0x64, 0x5f, 0x63, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, - 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6e, 0x64, 0x43, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x6e, 0x64, 0x43, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x5f, 0x0a, 0x0d, 0x61, 0x6e, 0x79, 0x5f, - 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x38, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x6e, 0x74, 0x73, 0x42, 0x19, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, + 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, + 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x6c, + 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x4d, 0xaa, 0x02, 0x18, 0x58, 0x6d, 0x74, 0x70, 0x2e, + 0x4d, 0x6c, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0xca, 0x02, 0x18, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xe2, 0x02, + 0x24, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x6c, + 0x73, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x4a, 0xd2, 0x04, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x16, 0x01, 0x0a, 0x22, 0x0a, + 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x18, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, + 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, 0x22, 0x0a, 0x08, 0x0a, 0x01, 0x08, + 0x12, 0x03, 0x05, 0x00, 0x47, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x05, 0x00, 0x47, + 0x0a, 0x30, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x0a, 0x00, 0x11, 0x01, 0x1a, 0x24, 0x20, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x20, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x0a, 0x08, 0x1e, 0x0a, 0x4a, + 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0c, 0x02, 0x25, 0x1a, 0x3d, 0x20, 0x4d, 0x61, + 0x70, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x76, 0x61, 0x72, 0x69, 0x6f, + 0x75, 0x73, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x61, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x20, 0x28, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6e, 0x61, + 0x6d, 0x65, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x2e, 0x29, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, + 0x02, 0x00, 0x06, 0x12, 0x03, 0x0c, 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, + 0x01, 0x12, 0x03, 0x0c, 0x16, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, + 0x03, 0x0c, 0x23, 0x24, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x0d, 0x02, + 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x06, 0x12, 0x03, 0x0d, 0x02, 0x09, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x0d, 0x0a, 0x14, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x0d, 0x17, 0x18, 0x0a, 0x64, 0x0a, 0x04, 0x04, + 0x00, 0x02, 0x02, 0x12, 0x03, 0x10, 0x02, 0x1f, 0x1a, 0x57, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x6f, 0x72, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x20, 0x61, 0x73, 0x20, 0x6f, 0x6e, 0x6c, + 0x79, 0x20, 0x73, 0x75, 0x70, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x0a, 0x20, 0x4f, + 0x6e, 0x6c, 0x79, 0x20, 0x73, 0x75, 0x70, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x20, + 0x63, 0x61, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x6f, + 0x74, 0x68, 0x65, 0x72, 0x20, 0x73, 0x75, 0x70, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x06, 0x12, 0x03, 0x10, 0x02, 0x09, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x10, 0x0a, 0x1a, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x10, 0x1d, 0x1e, 0x0a, 0x39, 0x0a, 0x02, 0x04, + 0x01, 0x12, 0x04, 0x14, 0x00, 0x16, 0x01, 0x1a, 0x2d, 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, + 0x72, 0x20, 0x61, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, + 0x6f, 0x66, 0x20, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x49, 0x6e, 0x62, 0x6f, + 0x78, 0x20, 0x49, 0x64, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x14, + 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x15, 0x02, 0x20, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x04, 0x12, 0x03, 0x15, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x01, 0x02, 0x00, 0x05, 0x12, 0x03, 0x15, 0x0b, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x15, 0x12, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, + 0x00, 0x03, 0x12, 0x03, 0x15, 0x1e, 0x1f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, + 0xfb, 0x31, 0x0a, 0x2c, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, + 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x19, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x5d, 0x0a, 0x19, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x56, 0x31, 0x12, 0x40, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x53, 0x65, 0x74, + 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x22, 0xdf, 0x05, 0x0a, 0x09, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x53, 0x65, 0x74, 0x12, 0x57, 0x0a, 0x11, 0x61, 0x64, 0x64, 0x5f, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x52, 0x0f, 0x61, 0x64, 0x64, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x12, 0x5d, 0x0a, 0x14, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6e, 0x79, - 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x6e, 0x79, - 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x57, 0x0a, 0x0c, 0x41, 0x6e, 0x64, - 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x47, 0x0a, 0x08, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x6d, + 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x12, 0x72, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x12, 0x74, 0x0a, 0x16, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x3e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x53, 0x65, 0x74, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x14, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x5c, 0x0a, 0x10, 0x61, 0x64, 0x64, 0x5f, 0x61, 0x64, + 0x6d, 0x69, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x32, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x65, 0x72, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0e, 0x61, 0x64, 0x64, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x12, 0x62, 0x0a, 0x13, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x5f, 0x61, + 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x32, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x65, + 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x64, 0x6d, + 0x69, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x6e, 0x0a, 0x19, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, - 0x69, 0x70, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, - 0x65, 0x73, 0x1a, 0x57, 0x0a, 0x0c, 0x41, 0x6e, 0x79, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x47, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x22, 0xab, 0x01, 0x0a, 0x0a, - 0x42, 0x61, 0x73, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1b, 0x0a, 0x17, 0x42, 0x41, - 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x42, 0x41, 0x53, 0x45, 0x5f, - 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x10, 0x01, 0x12, 0x14, - 0x0a, 0x10, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x44, 0x45, - 0x4e, 0x59, 0x10, 0x02, 0x12, 0x2d, 0x0a, 0x29, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, - 0x49, 0x43, 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, 0x49, 0x46, 0x5f, 0x41, 0x44, 0x4d, - 0x49, 0x4e, 0x5f, 0x4f, 0x52, 0x5f, 0x53, 0x55, 0x50, 0x45, 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, - 0x4e, 0x10, 0x03, 0x12, 0x24, 0x0a, 0x20, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, - 0x43, 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, 0x49, 0x46, 0x5f, 0x53, 0x55, 0x50, 0x45, - 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x04, 0x42, 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, - 0x64, 0x22, 0xac, 0x05, 0x0a, 0x0e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x12, 0x52, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x3c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x61, 0x73, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x48, 0x00, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x0d, 0x61, 0x6e, 0x64, 0x5f, - 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, + 0x17, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x1a, 0x72, 0x0a, 0x19, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, + 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8a, 0x05, 0x0a, + 0x10, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x12, 0x4c, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6e, 0x64, 0x43, 0x6f, - 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x6e, 0x64, 0x43, 0x6f, - 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x5d, 0x0a, 0x0d, 0x61, 0x6e, 0x79, 0x5f, 0x63, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, - 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6e, 0x79, 0x43, 0x6f, 0x6e, - 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x6e, 0x79, 0x43, 0x6f, 0x6e, - 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x55, 0x0a, 0x0c, 0x41, 0x6e, 0x64, 0x43, 0x6f, 0x6e, - 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, - 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x1a, 0x55, 0x0a, - 0x0c, 0x41, 0x6e, 0x79, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, - 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x29, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x69, 0x65, 0x73, 0x22, 0xd1, 0x01, 0x0a, 0x12, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x42, 0x61, 0x73, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x24, 0x0a, 0x20, 0x4d, - 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, - 0x49, 0x43, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, - 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x42, 0x41, - 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x10, - 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x42, 0x41, - 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x44, 0x45, 0x4e, 0x59, 0x10, 0x02, - 0x12, 0x27, 0x0a, 0x23, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x42, 0x41, 0x53, - 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, 0x49, - 0x46, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x03, 0x12, 0x2d, 0x0a, 0x29, 0x4d, 0x45, 0x54, - 0x41, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, - 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, 0x49, 0x46, 0x5f, 0x53, 0x55, 0x50, 0x45, 0x52, - 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x04, 0x42, 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, - 0x22, 0xd4, 0x05, 0x0a, 0x17, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x5e, 0x0a, 0x04, - 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x48, 0x2e, 0x78, 0x6d, 0x74, - 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x61, 0x73, 0x65, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x48, 0x00, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x0d, - 0x61, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, + 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x42, 0x61, 0x73, + 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x48, 0x00, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, + 0x5f, 0x0a, 0x0d, 0x61, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, + 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x48, 0x00, 0x52, 0x0c, 0x61, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x5f, 0x0a, 0x0d, 0x61, 0x6e, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, + 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6e, 0x79, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x6e, 0x79, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x1a, 0x57, 0x0a, 0x0c, 0x41, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x47, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, - 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x66, 0x0a, 0x0d, 0x61, 0x6e, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x64, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x78, 0x6d, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x1a, 0x57, 0x0a, 0x0c, 0x41, 0x6e, + 0x79, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x47, 0x0a, 0x08, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, + 0x68, 0x69, 0x70, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x69, 0x65, 0x73, 0x22, 0xab, 0x01, 0x0a, 0x0a, 0x42, 0x61, 0x73, 0x65, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x12, 0x1b, 0x0a, 0x17, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, + 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x15, 0x0a, 0x11, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x41, + 0x4c, 0x4c, 0x4f, 0x57, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, + 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x44, 0x45, 0x4e, 0x59, 0x10, 0x02, 0x12, 0x2d, 0x0a, 0x29, + 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, + 0x57, 0x5f, 0x49, 0x46, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x5f, 0x4f, 0x52, 0x5f, 0x53, 0x55, + 0x50, 0x45, 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x03, 0x12, 0x24, 0x0a, 0x20, 0x42, + 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, + 0x5f, 0x49, 0x46, 0x5f, 0x53, 0x55, 0x50, 0x45, 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, + 0x04, 0x42, 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0xac, 0x05, 0x0a, 0x0e, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x52, 0x0a, 0x04, + 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3c, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x61, + 0x73, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x48, 0x00, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, + 0x12, 0x5d, 0x0a, 0x0d, 0x61, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, + 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x41, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x48, + 0x00, 0x52, 0x0c, 0x61, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x5d, 0x0a, 0x0d, 0x61, 0x6e, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, + 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x41, 0x6e, 0x79, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, + 0x52, 0x0c, 0x61, 0x6e, 0x79, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x55, + 0x0a, 0x0c, 0x41, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, + 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x29, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x08, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x69, 0x65, 0x73, 0x1a, 0x55, 0x0a, 0x0c, 0x41, 0x6e, 0x79, 0x43, 0x6f, 0x6e, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, + 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x22, 0xd1, 0x01, 0x0a, + 0x12, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x61, 0x73, 0x65, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x12, 0x24, 0x0a, 0x20, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x5f, + 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, + 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x4d, 0x45, 0x54, + 0x41, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, + 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x4d, 0x45, 0x54, + 0x41, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, + 0x59, 0x5f, 0x44, 0x45, 0x4e, 0x59, 0x10, 0x02, 0x12, 0x27, 0x0a, 0x23, 0x4d, 0x45, 0x54, 0x41, + 0x44, 0x41, 0x54, 0x41, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, + 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, 0x49, 0x46, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, + 0x03, 0x12, 0x2d, 0x0a, 0x29, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x42, 0x41, + 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, + 0x49, 0x46, 0x5f, 0x53, 0x55, 0x50, 0x45, 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x04, + 0x42, 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0xd4, 0x05, 0x0a, 0x17, 0x50, 0x65, 0x72, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x12, 0x5e, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x48, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, + 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x42, 0x61, 0x73, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x48, 0x00, 0x52, 0x04, + 0x62, 0x61, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x0d, 0x61, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, - 0x41, 0x6e, 0x79, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0c, - 0x61, 0x6e, 0x79, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x5e, 0x0a, 0x0c, - 0x41, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x08, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, - 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x1a, 0x5e, 0x0a, 0x0c, - 0x41, 0x6e, 0x79, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x08, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, - 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x22, 0xc0, 0x01, 0x0a, - 0x15, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x61, 0x73, 0x65, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x27, 0x0a, 0x23, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, - 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, - 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x20, 0x0a, 0x1c, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x42, - 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x44, 0x45, 0x4e, 0x59, 0x10, - 0x01, 0x12, 0x2a, 0x0a, 0x26, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, - 0x5f, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x41, 0x4c, 0x4c, - 0x4f, 0x57, 0x5f, 0x49, 0x46, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x30, 0x0a, - 0x2c, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x42, 0x41, 0x53, - 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, 0x49, - 0x46, 0x5f, 0x53, 0x55, 0x50, 0x45, 0x52, 0x5f, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x03, 0x42, - 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x42, 0xea, 0x01, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x15, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, - 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, - 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x4d, 0xaa, 0x02, 0x18, 0x58, 0x6d, 0x74, - 0x70, 0x2e, 0x4d, 0x6c, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, 0x18, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, - 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0xe2, 0x02, 0x24, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, - 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xe6, 0x18, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x68, 0x01, 0x0a, - 0x2e, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x24, 0x20, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x20, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x0a, - 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, 0x22, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, - 0x05, 0x00, 0x47, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x05, 0x00, 0x47, 0x0a, 0x30, - 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x09, 0x00, 0x0b, 0x01, 0x1a, 0x24, 0x20, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, - 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, - 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x09, 0x08, 0x21, 0x0a, 0x0b, 0x0a, 0x04, - 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0a, 0x02, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, - 0x00, 0x06, 0x12, 0x03, 0x0a, 0x02, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, - 0x12, 0x03, 0x0a, 0x0c, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, - 0x0a, 0x17, 0x18, 0x0a, 0x37, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x0e, 0x00, 0x15, 0x01, 0x1a, - 0x2b, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x69, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x67, 0x6f, 0x76, 0x65, 0x72, - 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, - 0x04, 0x01, 0x01, 0x12, 0x03, 0x0e, 0x08, 0x11, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, - 0x12, 0x03, 0x0f, 0x02, 0x29, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, - 0x0f, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0f, 0x13, - 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0f, 0x27, 0x28, 0x0a, - 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, 0x12, 0x03, 0x10, 0x02, 0x2c, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x01, 0x02, 0x01, 0x06, 0x12, 0x03, 0x10, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, - 0x02, 0x01, 0x01, 0x12, 0x03, 0x10, 0x13, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, - 0x03, 0x12, 0x03, 0x10, 0x2a, 0x2b, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x02, 0x12, 0x03, - 0x11, 0x02, 0x39, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x06, 0x12, 0x03, 0x11, 0x02, - 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x01, 0x12, 0x03, 0x11, 0x1e, 0x34, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x03, 0x12, 0x03, 0x11, 0x37, 0x38, 0x0a, 0x0b, 0x0a, - 0x04, 0x04, 0x01, 0x02, 0x03, 0x12, 0x03, 0x12, 0x02, 0x2f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, - 0x02, 0x03, 0x06, 0x12, 0x03, 0x12, 0x02, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, - 0x01, 0x12, 0x03, 0x12, 0x1a, 0x2a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x03, 0x12, - 0x03, 0x12, 0x2d, 0x2e, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x04, 0x12, 0x03, 0x13, 0x02, - 0x32, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x04, 0x06, 0x12, 0x03, 0x13, 0x02, 0x19, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x04, 0x01, 0x12, 0x03, 0x13, 0x1a, 0x2d, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x01, 0x02, 0x04, 0x03, 0x12, 0x03, 0x13, 0x30, 0x31, 0x0a, 0x0b, 0x0a, 0x04, 0x04, - 0x01, 0x02, 0x05, 0x12, 0x03, 0x14, 0x02, 0x38, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x05, - 0x06, 0x12, 0x03, 0x14, 0x02, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x05, 0x01, 0x12, - 0x03, 0x14, 0x1a, 0x33, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x05, 0x03, 0x12, 0x03, 0x14, - 0x36, 0x37, 0x0a, 0x4c, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x18, 0x00, 0x31, 0x01, 0x1a, 0x40, - 0x20, 0x41, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x67, - 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x2f, 0x72, 0x65, - 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, 0x6f, - 0x72, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, - 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x18, 0x08, 0x18, 0x0a, 0x1b, 0x0a, 0x04, - 0x04, 0x02, 0x04, 0x00, 0x12, 0x04, 0x1a, 0x02, 0x20, 0x03, 0x1a, 0x0d, 0x20, 0x42, 0x61, 0x73, - 0x65, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x04, - 0x00, 0x01, 0x12, 0x03, 0x1a, 0x07, 0x11, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x04, 0x00, 0x02, - 0x00, 0x12, 0x03, 0x1b, 0x04, 0x20, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x04, 0x00, 0x02, 0x00, - 0x01, 0x12, 0x03, 0x1b, 0x04, 0x1b, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x04, 0x00, 0x02, 0x00, - 0x02, 0x12, 0x03, 0x1b, 0x1e, 0x1f, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x04, 0x00, 0x02, 0x01, - 0x12, 0x03, 0x1c, 0x04, 0x1a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, - 0x12, 0x03, 0x1c, 0x04, 0x15, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x04, 0x00, 0x02, 0x01, 0x02, - 0x12, 0x03, 0x1c, 0x18, 0x19, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x04, 0x00, 0x02, 0x02, 0x12, - 0x03, 0x1d, 0x04, 0x19, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, - 0x03, 0x1d, 0x04, 0x14, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x04, 0x00, 0x02, 0x02, 0x02, 0x12, - 0x03, 0x1d, 0x17, 0x18, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x04, 0x00, 0x02, 0x03, 0x12, 0x03, - 0x1e, 0x04, 0x32, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x04, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, - 0x1e, 0x04, 0x2d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x04, 0x00, 0x02, 0x03, 0x02, 0x12, 0x03, - 0x1e, 0x30, 0x31, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x04, 0x00, 0x02, 0x04, 0x12, 0x03, 0x1f, - 0x04, 0x29, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x04, 0x00, 0x02, 0x04, 0x01, 0x12, 0x03, 0x1f, - 0x04, 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x04, 0x00, 0x02, 0x04, 0x02, 0x12, 0x03, 0x1f, - 0x27, 0x28, 0x0a, 0x44, 0x0a, 0x04, 0x04, 0x02, 0x03, 0x00, 0x12, 0x04, 0x23, 0x02, 0x25, 0x03, - 0x1a, 0x36, 0x20, 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, - 0x70, 0x6c, 0x65, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x2e, 0x20, 0x41, 0x6c, - 0x6c, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x20, - 0x74, 0x6f, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x03, 0x00, - 0x01, 0x12, 0x03, 0x23, 0x0a, 0x16, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, - 0x12, 0x03, 0x24, 0x04, 0x2b, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x04, - 0x12, 0x03, 0x24, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x06, - 0x12, 0x03, 0x24, 0x0d, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x01, - 0x12, 0x03, 0x24, 0x1e, 0x26, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x03, - 0x12, 0x03, 0x24, 0x29, 0x2a, 0x0a, 0x44, 0x0a, 0x04, 0x04, 0x02, 0x03, 0x01, 0x12, 0x04, 0x28, - 0x02, 0x2a, 0x03, 0x1a, 0x36, 0x20, 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x20, 0x6d, 0x75, - 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x2e, - 0x20, 0x41, 0x6e, 0x79, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, - 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x02, 0x03, 0x01, 0x01, 0x12, 0x03, 0x28, 0x0a, 0x16, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, - 0x01, 0x02, 0x00, 0x12, 0x03, 0x29, 0x04, 0x2b, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, - 0x02, 0x00, 0x04, 0x12, 0x03, 0x29, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, - 0x02, 0x00, 0x06, 0x12, 0x03, 0x29, 0x0d, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, - 0x02, 0x00, 0x01, 0x12, 0x03, 0x29, 0x1e, 0x26, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, - 0x02, 0x00, 0x03, 0x12, 0x03, 0x29, 0x29, 0x2a, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x02, 0x08, 0x00, - 0x12, 0x04, 0x2c, 0x02, 0x30, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x08, 0x00, 0x01, 0x12, - 0x03, 0x2c, 0x08, 0x0c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x2d, 0x04, - 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x06, 0x12, 0x03, 0x2d, 0x04, 0x0e, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2d, 0x0f, 0x13, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2d, 0x16, 0x17, 0x0a, 0x0b, 0x0a, 0x04, 0x04, - 0x02, 0x02, 0x01, 0x12, 0x03, 0x2e, 0x04, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, - 0x06, 0x12, 0x03, 0x2e, 0x04, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x01, 0x12, - 0x03, 0x2e, 0x11, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, 0x03, 0x2e, - 0x21, 0x22, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x02, 0x12, 0x03, 0x2f, 0x04, 0x23, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x06, 0x12, 0x03, 0x2f, 0x04, 0x10, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x02, 0x02, 0x02, 0x01, 0x12, 0x03, 0x2f, 0x11, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x02, 0x02, 0x02, 0x03, 0x12, 0x03, 0x2f, 0x21, 0x22, 0x0a, 0x35, 0x0a, 0x02, 0x04, 0x03, 0x12, - 0x04, 0x34, 0x00, 0x4d, 0x01, 0x1a, 0x29, 0x20, 0x41, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x67, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x73, 0x20, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, - 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x03, 0x34, 0x08, 0x16, 0x0a, 0x1b, 0x0a, 0x04, - 0x04, 0x03, 0x04, 0x00, 0x12, 0x04, 0x36, 0x02, 0x3c, 0x03, 0x1a, 0x0d, 0x20, 0x42, 0x61, 0x73, - 0x65, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x04, - 0x00, 0x01, 0x12, 0x03, 0x36, 0x07, 0x19, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x04, 0x00, 0x02, - 0x00, 0x12, 0x03, 0x37, 0x04, 0x29, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x04, 0x00, 0x02, 0x00, - 0x01, 0x12, 0x03, 0x37, 0x04, 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x04, 0x00, 0x02, 0x00, - 0x02, 0x12, 0x03, 0x37, 0x27, 0x28, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x04, 0x00, 0x02, 0x01, - 0x12, 0x03, 0x38, 0x04, 0x23, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x04, 0x00, 0x02, 0x01, 0x01, - 0x12, 0x03, 0x38, 0x04, 0x1e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x04, 0x00, 0x02, 0x01, 0x02, - 0x12, 0x03, 0x38, 0x21, 0x22, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x04, 0x00, 0x02, 0x02, 0x12, - 0x03, 0x39, 0x04, 0x22, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, - 0x03, 0x39, 0x04, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x04, 0x00, 0x02, 0x02, 0x02, 0x12, - 0x03, 0x39, 0x20, 0x21, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x04, 0x00, 0x02, 0x03, 0x12, 0x03, - 0x3a, 0x04, 0x2c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x04, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, - 0x3a, 0x04, 0x27, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x04, 0x00, 0x02, 0x03, 0x02, 0x12, 0x03, - 0x3a, 0x2a, 0x2b, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x04, 0x00, 0x02, 0x04, 0x12, 0x03, 0x3b, - 0x04, 0x32, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x04, 0x00, 0x02, 0x04, 0x01, 0x12, 0x03, 0x3b, - 0x04, 0x2d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x04, 0x00, 0x02, 0x04, 0x02, 0x12, 0x03, 0x3b, - 0x30, 0x31, 0x0a, 0x44, 0x0a, 0x04, 0x04, 0x03, 0x03, 0x00, 0x12, 0x04, 0x3f, 0x02, 0x41, 0x03, - 0x1a, 0x36, 0x20, 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, - 0x70, 0x6c, 0x65, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x2e, 0x20, 0x41, 0x6c, - 0x6c, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x20, - 0x74, 0x6f, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x03, 0x00, - 0x01, 0x12, 0x03, 0x3f, 0x0a, 0x16, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, - 0x12, 0x03, 0x40, 0x04, 0x29, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x04, - 0x12, 0x03, 0x40, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x06, - 0x12, 0x03, 0x40, 0x0d, 0x1b, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x01, - 0x12, 0x03, 0x40, 0x1c, 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x03, - 0x12, 0x03, 0x40, 0x27, 0x28, 0x0a, 0x44, 0x0a, 0x04, 0x04, 0x03, 0x03, 0x01, 0x12, 0x04, 0x44, - 0x02, 0x46, 0x03, 0x1a, 0x36, 0x20, 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x20, 0x6d, 0x75, - 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x2e, - 0x20, 0x41, 0x6e, 0x79, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, - 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x03, 0x03, 0x01, 0x01, 0x12, 0x03, 0x44, 0x0a, 0x16, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x03, - 0x01, 0x02, 0x00, 0x12, 0x03, 0x45, 0x04, 0x29, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x01, - 0x02, 0x00, 0x04, 0x12, 0x03, 0x45, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x01, - 0x02, 0x00, 0x06, 0x12, 0x03, 0x45, 0x0d, 0x1b, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x01, - 0x02, 0x00, 0x01, 0x12, 0x03, 0x45, 0x1c, 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x01, - 0x02, 0x00, 0x03, 0x12, 0x03, 0x45, 0x27, 0x28, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x03, 0x08, 0x00, - 0x12, 0x04, 0x48, 0x02, 0x4c, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x08, 0x00, 0x01, 0x12, - 0x03, 0x48, 0x08, 0x0c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x00, 0x12, 0x03, 0x49, 0x04, - 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x06, 0x12, 0x03, 0x49, 0x04, 0x16, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x01, 0x12, 0x03, 0x49, 0x17, 0x1b, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, 0x49, 0x1e, 0x1f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, - 0x03, 0x02, 0x01, 0x12, 0x03, 0x4a, 0x04, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, - 0x06, 0x12, 0x03, 0x4a, 0x04, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x12, - 0x03, 0x4a, 0x11, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x03, 0x12, 0x03, 0x4a, - 0x21, 0x22, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x02, 0x12, 0x03, 0x4b, 0x04, 0x23, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x06, 0x12, 0x03, 0x4b, 0x04, 0x10, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x03, 0x02, 0x02, 0x01, 0x12, 0x03, 0x4b, 0x11, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x03, 0x02, 0x02, 0x03, 0x12, 0x03, 0x4b, 0x21, 0x22, 0x0a, 0x38, 0x0a, 0x02, 0x04, 0x04, 0x12, - 0x04, 0x50, 0x00, 0x68, 0x01, 0x1a, 0x2c, 0x20, 0x41, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x67, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x73, 0x20, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x04, 0x01, 0x12, 0x03, 0x50, 0x08, 0x1f, 0x0a, - 0x1b, 0x0a, 0x04, 0x04, 0x04, 0x04, 0x00, 0x12, 0x04, 0x52, 0x02, 0x57, 0x03, 0x1a, 0x0d, 0x20, - 0x42, 0x61, 0x73, 0x65, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x04, 0x04, 0x00, 0x01, 0x12, 0x03, 0x52, 0x07, 0x1c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, - 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x53, 0x04, 0x2c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x04, - 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x53, 0x04, 0x27, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x04, - 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x53, 0x2a, 0x2b, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x04, - 0x00, 0x02, 0x01, 0x12, 0x03, 0x54, 0x04, 0x25, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x04, 0x00, - 0x02, 0x01, 0x01, 0x12, 0x03, 0x54, 0x04, 0x20, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x04, 0x00, - 0x02, 0x01, 0x02, 0x12, 0x03, 0x54, 0x23, 0x24, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x04, 0x00, - 0x02, 0x02, 0x12, 0x03, 0x55, 0x04, 0x2f, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x04, 0x00, 0x02, - 0x02, 0x01, 0x12, 0x03, 0x55, 0x04, 0x2a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x04, 0x00, 0x02, - 0x02, 0x02, 0x12, 0x03, 0x55, 0x2d, 0x2e, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x04, 0x00, 0x02, - 0x03, 0x12, 0x03, 0x56, 0x04, 0x35, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x04, 0x00, 0x02, 0x03, - 0x01, 0x12, 0x03, 0x56, 0x04, 0x30, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x04, 0x00, 0x02, 0x03, - 0x02, 0x12, 0x03, 0x56, 0x33, 0x34, 0x0a, 0x44, 0x0a, 0x04, 0x04, 0x04, 0x03, 0x00, 0x12, 0x04, - 0x5a, 0x02, 0x5c, 0x03, 0x1a, 0x36, 0x20, 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x20, 0x6d, - 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, - 0x2e, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, - 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x04, 0x03, 0x00, 0x01, 0x12, 0x03, 0x5a, 0x0a, 0x16, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, - 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x5b, 0x04, 0x32, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, - 0x00, 0x02, 0x00, 0x04, 0x12, 0x03, 0x5b, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, - 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x5b, 0x0d, 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, - 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x5b, 0x25, 0x2d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, - 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x5b, 0x30, 0x31, 0x0a, 0x44, 0x0a, 0x04, 0x04, 0x04, 0x03, - 0x01, 0x12, 0x04, 0x5f, 0x02, 0x61, 0x03, 0x1a, 0x36, 0x20, 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, - 0x65, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x69, 0x65, 0x73, 0x2e, 0x20, 0x41, 0x6e, 0x79, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x65, 0x76, - 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x03, 0x01, 0x01, 0x12, 0x03, 0x5f, 0x0a, 0x16, 0x0a, 0x0d, 0x0a, - 0x06, 0x04, 0x04, 0x03, 0x01, 0x02, 0x00, 0x12, 0x03, 0x60, 0x04, 0x32, 0x0a, 0x0e, 0x0a, 0x07, - 0x04, 0x04, 0x03, 0x01, 0x02, 0x00, 0x04, 0x12, 0x03, 0x60, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, - 0x04, 0x04, 0x03, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, 0x60, 0x0d, 0x24, 0x0a, 0x0e, 0x0a, 0x07, - 0x04, 0x04, 0x03, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x60, 0x25, 0x2d, 0x0a, 0x0e, 0x0a, 0x07, - 0x04, 0x04, 0x03, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x60, 0x30, 0x31, 0x0a, 0x0c, 0x0a, 0x04, - 0x04, 0x04, 0x08, 0x00, 0x12, 0x04, 0x63, 0x02, 0x67, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, - 0x08, 0x00, 0x01, 0x12, 0x03, 0x63, 0x08, 0x0c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, - 0x12, 0x03, 0x64, 0x04, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x06, 0x12, 0x03, - 0x64, 0x04, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x64, 0x1a, - 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x64, 0x21, 0x22, 0x0a, - 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x01, 0x12, 0x03, 0x65, 0x04, 0x23, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x04, 0x02, 0x01, 0x06, 0x12, 0x03, 0x65, 0x04, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, - 0x02, 0x01, 0x01, 0x12, 0x03, 0x65, 0x11, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, - 0x03, 0x12, 0x03, 0x65, 0x21, 0x22, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x02, 0x12, 0x03, - 0x66, 0x04, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x02, 0x06, 0x12, 0x03, 0x66, 0x04, - 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x02, 0x01, 0x12, 0x03, 0x66, 0x11, 0x1e, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x02, 0x03, 0x12, 0x03, 0x66, 0x21, 0x22, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xb8, 0x1a, 0x0a, 0x2e, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x74, - 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, - 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x73, 0x22, 0xa7, 0x01, 0x0a, 0x10, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, - 0x69, 0x70, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0c, 0x52, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x64, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x3f, 0x0a, 0x1c, - 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x61, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x19, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x41, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x80, 0x03, - 0x0a, 0x16, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, - 0x70, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x50, 0x0a, 0x0d, 0x6d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x73, 0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0c, 0x6d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x73, 0x41, 0x64, 0x64, 0x65, 0x64, 0x12, 0x54, 0x0a, 0x0f, 0x6d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x73, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, + 0x41, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0c, + 0x61, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x66, 0x0a, 0x0d, + 0x61, 0x6e, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, + 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6e, 0x79, 0x43, 0x6f, 0x6e, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x6e, 0x79, 0x43, 0x6f, 0x6e, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x5e, 0x0a, 0x0c, 0x41, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, + 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x69, 0x65, 0x73, 0x1a, 0x5e, 0x0a, 0x0c, 0x41, 0x6e, 0x79, 0x43, 0x6f, 0x6e, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, + 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x69, 0x65, 0x73, 0x22, 0xc0, 0x01, 0x0a, 0x15, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x61, 0x73, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x27, + 0x0a, 0x23, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x42, 0x41, + 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1c, 0x50, 0x45, 0x52, 0x4d, 0x49, + 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, + 0x43, 0x59, 0x5f, 0x44, 0x45, 0x4e, 0x59, 0x10, 0x01, 0x12, 0x2a, 0x0a, 0x26, 0x50, 0x45, 0x52, + 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, + 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, 0x49, 0x46, 0x5f, 0x41, 0x44, + 0x4d, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x30, 0x0a, 0x2c, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, + 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, + 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, 0x49, 0x46, 0x5f, 0x53, 0x55, 0x50, 0x45, 0x52, 0x5f, + 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x03, 0x42, 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x42, + 0xea, 0x01, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, + 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0x42, 0x15, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, + 0x4d, 0x4d, 0xaa, 0x02, 0x18, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x6c, 0x73, 0x2e, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, 0x18, + 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xe2, 0x02, 0x24, 0x58, 0x6d, 0x74, 0x70, 0x5c, + 0x4d, 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, + 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xe6, 0x18, 0x0a, + 0x06, 0x12, 0x04, 0x01, 0x00, 0x68, 0x01, 0x0a, 0x2e, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, + 0x12, 0x1a, 0x24, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, + 0x22, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x05, 0x00, 0x47, 0x0a, 0x09, 0x0a, 0x02, 0x08, + 0x0b, 0x12, 0x03, 0x05, 0x00, 0x47, 0x0a, 0x30, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x09, 0x00, + 0x0b, 0x01, 0x1a, 0x24, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, + 0x03, 0x09, 0x08, 0x21, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0a, 0x02, + 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x0a, 0x02, 0x0b, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0a, 0x0c, 0x14, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0a, 0x17, 0x18, 0x0a, 0x37, 0x0a, 0x02, 0x04, + 0x01, 0x12, 0x04, 0x0e, 0x00, 0x15, 0x01, 0x1a, 0x2b, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x65, + 0x74, 0x20, 0x6f, 0x66, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x20, 0x74, 0x68, + 0x61, 0x74, 0x20, 0x67, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x0e, 0x08, 0x11, + 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x0f, 0x02, 0x29, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, 0x0f, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0f, 0x13, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, + 0x00, 0x03, 0x12, 0x03, 0x0f, 0x27, 0x28, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, 0x12, + 0x03, 0x10, 0x02, 0x2c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x06, 0x12, 0x03, 0x10, + 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x10, 0x13, 0x27, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x10, 0x2a, 0x2b, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x01, 0x02, 0x02, 0x12, 0x03, 0x11, 0x02, 0x39, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x01, 0x02, 0x02, 0x06, 0x12, 0x03, 0x11, 0x02, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, + 0x02, 0x01, 0x12, 0x03, 0x11, 0x1e, 0x34, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x03, + 0x12, 0x03, 0x11, 0x37, 0x38, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x03, 0x12, 0x03, 0x12, + 0x02, 0x2f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x06, 0x12, 0x03, 0x12, 0x02, 0x19, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x01, 0x12, 0x03, 0x12, 0x1a, 0x2a, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x03, 0x12, 0x03, 0x12, 0x2d, 0x2e, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x01, 0x02, 0x04, 0x12, 0x03, 0x13, 0x02, 0x32, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, + 0x04, 0x06, 0x12, 0x03, 0x13, 0x02, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x04, 0x01, + 0x12, 0x03, 0x13, 0x1a, 0x2d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x04, 0x03, 0x12, 0x03, + 0x13, 0x30, 0x31, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x05, 0x12, 0x03, 0x14, 0x02, 0x38, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x05, 0x06, 0x12, 0x03, 0x14, 0x02, 0x19, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x01, 0x02, 0x05, 0x01, 0x12, 0x03, 0x14, 0x1a, 0x33, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x01, 0x02, 0x05, 0x03, 0x12, 0x03, 0x14, 0x36, 0x37, 0x0a, 0x4c, 0x0a, 0x02, 0x04, 0x02, + 0x12, 0x04, 0x18, 0x00, 0x31, 0x01, 0x1a, 0x40, 0x20, 0x41, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x67, 0x6f, 0x76, 0x65, 0x72, 0x6e, 0x73, 0x20, 0x61, + 0x64, 0x64, 0x69, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x6d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, + 0x03, 0x18, 0x08, 0x18, 0x0a, 0x1b, 0x0a, 0x04, 0x04, 0x02, 0x04, 0x00, 0x12, 0x04, 0x1a, 0x02, + 0x20, 0x03, 0x1a, 0x0d, 0x20, 0x42, 0x61, 0x73, 0x65, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x04, 0x00, 0x01, 0x12, 0x03, 0x1a, 0x07, 0x11, 0x0a, + 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x1b, 0x04, 0x20, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x02, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1b, 0x04, 0x1b, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x02, 0x04, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x1b, 0x1e, 0x1f, 0x0a, 0x0d, + 0x0a, 0x06, 0x04, 0x02, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x1c, 0x04, 0x1a, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x02, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x1c, 0x04, 0x15, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x02, 0x04, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x1c, 0x18, 0x19, 0x0a, 0x0d, 0x0a, + 0x06, 0x04, 0x02, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x1d, 0x04, 0x19, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x02, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x1d, 0x04, 0x14, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x02, 0x04, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x1d, 0x17, 0x18, 0x0a, 0x0d, 0x0a, 0x06, + 0x04, 0x02, 0x04, 0x00, 0x02, 0x03, 0x12, 0x03, 0x1e, 0x04, 0x32, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x02, 0x04, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x1e, 0x04, 0x2d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x02, 0x04, 0x00, 0x02, 0x03, 0x02, 0x12, 0x03, 0x1e, 0x30, 0x31, 0x0a, 0x0d, 0x0a, 0x06, 0x04, + 0x02, 0x04, 0x00, 0x02, 0x04, 0x12, 0x03, 0x1f, 0x04, 0x29, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, + 0x04, 0x00, 0x02, 0x04, 0x01, 0x12, 0x03, 0x1f, 0x04, 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, + 0x04, 0x00, 0x02, 0x04, 0x02, 0x12, 0x03, 0x1f, 0x27, 0x28, 0x0a, 0x44, 0x0a, 0x04, 0x04, 0x02, + 0x03, 0x00, 0x12, 0x04, 0x23, 0x02, 0x25, 0x03, 0x1a, 0x36, 0x20, 0x43, 0x6f, 0x6d, 0x62, 0x69, + 0x6e, 0x65, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x69, 0x65, 0x73, 0x2e, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x65, + 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x03, 0x00, 0x01, 0x12, 0x03, 0x23, 0x0a, 0x16, 0x0a, 0x0d, + 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x24, 0x04, 0x2b, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x04, 0x12, 0x03, 0x24, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x24, 0x0d, 0x1d, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x24, 0x1e, 0x26, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x24, 0x29, 0x2a, 0x0a, 0x44, 0x0a, + 0x04, 0x04, 0x02, 0x03, 0x01, 0x12, 0x04, 0x28, 0x02, 0x2a, 0x03, 0x1a, 0x36, 0x20, 0x43, 0x6f, + 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x2e, 0x20, 0x41, 0x6e, 0x79, 0x20, 0x6d, 0x75, 0x73, + 0x74, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x72, + 0x75, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x03, 0x01, 0x01, 0x12, 0x03, 0x28, 0x0a, + 0x16, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x12, 0x03, 0x29, 0x04, 0x2b, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x04, 0x12, 0x03, 0x29, 0x04, 0x0c, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, 0x29, 0x0d, 0x1d, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x29, 0x1e, 0x26, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x29, 0x29, 0x2a, + 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x02, 0x08, 0x00, 0x12, 0x04, 0x2c, 0x02, 0x30, 0x03, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x02, 0x08, 0x00, 0x01, 0x12, 0x03, 0x2c, 0x08, 0x0c, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x2d, 0x04, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, + 0x00, 0x06, 0x12, 0x03, 0x2d, 0x04, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, + 0x12, 0x03, 0x2d, 0x0f, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, + 0x2d, 0x16, 0x17, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x01, 0x12, 0x03, 0x2e, 0x04, 0x23, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x06, 0x12, 0x03, 0x2e, 0x04, 0x10, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x01, 0x12, 0x03, 0x2e, 0x11, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, 0x03, 0x2e, 0x21, 0x22, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, + 0x02, 0x02, 0x12, 0x03, 0x2f, 0x04, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x06, + 0x12, 0x03, 0x2f, 0x04, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x01, 0x12, 0x03, + 0x2f, 0x11, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x03, 0x12, 0x03, 0x2f, 0x21, + 0x22, 0x0a, 0x35, 0x0a, 0x02, 0x04, 0x03, 0x12, 0x04, 0x34, 0x00, 0x4d, 0x01, 0x1a, 0x29, 0x20, + 0x41, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x67, 0x6f, + 0x76, 0x65, 0x72, 0x6e, 0x73, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, + 0x03, 0x34, 0x08, 0x16, 0x0a, 0x1b, 0x0a, 0x04, 0x04, 0x03, 0x04, 0x00, 0x12, 0x04, 0x36, 0x02, + 0x3c, 0x03, 0x1a, 0x0d, 0x20, 0x42, 0x61, 0x73, 0x65, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x36, 0x07, 0x19, 0x0a, + 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x37, 0x04, 0x29, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x03, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x37, 0x04, 0x24, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x03, 0x04, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x37, 0x27, 0x28, 0x0a, 0x0d, + 0x0a, 0x06, 0x04, 0x03, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x38, 0x04, 0x23, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x03, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x38, 0x04, 0x1e, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x03, 0x04, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x38, 0x21, 0x22, 0x0a, 0x0d, 0x0a, + 0x06, 0x04, 0x03, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x39, 0x04, 0x22, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x03, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x39, 0x04, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x03, 0x04, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x39, 0x20, 0x21, 0x0a, 0x0d, 0x0a, 0x06, + 0x04, 0x03, 0x04, 0x00, 0x02, 0x03, 0x12, 0x03, 0x3a, 0x04, 0x2c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x03, 0x04, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x3a, 0x04, 0x27, 0x0a, 0x0e, 0x0a, 0x07, 0x04, + 0x03, 0x04, 0x00, 0x02, 0x03, 0x02, 0x12, 0x03, 0x3a, 0x2a, 0x2b, 0x0a, 0x0d, 0x0a, 0x06, 0x04, + 0x03, 0x04, 0x00, 0x02, 0x04, 0x12, 0x03, 0x3b, 0x04, 0x32, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, + 0x04, 0x00, 0x02, 0x04, 0x01, 0x12, 0x03, 0x3b, 0x04, 0x2d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, + 0x04, 0x00, 0x02, 0x04, 0x02, 0x12, 0x03, 0x3b, 0x30, 0x31, 0x0a, 0x44, 0x0a, 0x04, 0x04, 0x03, + 0x03, 0x00, 0x12, 0x04, 0x3f, 0x02, 0x41, 0x03, 0x1a, 0x36, 0x20, 0x43, 0x6f, 0x6d, 0x62, 0x69, + 0x6e, 0x65, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x69, 0x65, 0x73, 0x2e, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x65, + 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x03, 0x00, 0x01, 0x12, 0x03, 0x3f, 0x0a, 0x16, 0x0a, 0x0d, + 0x0a, 0x06, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x40, 0x04, 0x29, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x04, 0x12, 0x03, 0x40, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x40, 0x0d, 0x1b, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x40, 0x1c, 0x24, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x40, 0x27, 0x28, 0x0a, 0x44, 0x0a, + 0x04, 0x04, 0x03, 0x03, 0x01, 0x12, 0x04, 0x44, 0x02, 0x46, 0x03, 0x1a, 0x36, 0x20, 0x43, 0x6f, + 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x2e, 0x20, 0x41, 0x6e, 0x79, 0x20, 0x6d, 0x75, 0x73, + 0x74, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x72, + 0x75, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x03, 0x01, 0x01, 0x12, 0x03, 0x44, 0x0a, + 0x16, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x03, 0x01, 0x02, 0x00, 0x12, 0x03, 0x45, 0x04, 0x29, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x01, 0x02, 0x00, 0x04, 0x12, 0x03, 0x45, 0x04, 0x0c, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, 0x45, 0x0d, 0x1b, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x45, 0x1c, 0x24, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x45, 0x27, 0x28, + 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x03, 0x08, 0x00, 0x12, 0x04, 0x48, 0x02, 0x4c, 0x03, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x03, 0x08, 0x00, 0x01, 0x12, 0x03, 0x48, 0x08, 0x0c, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x03, 0x02, 0x00, 0x12, 0x03, 0x49, 0x04, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, + 0x00, 0x06, 0x12, 0x03, 0x49, 0x04, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x01, + 0x12, 0x03, 0x49, 0x17, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, + 0x49, 0x1e, 0x1f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x01, 0x12, 0x03, 0x4a, 0x04, 0x23, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x06, 0x12, 0x03, 0x4a, 0x04, 0x10, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x12, 0x03, 0x4a, 0x11, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x03, 0x02, 0x01, 0x03, 0x12, 0x03, 0x4a, 0x21, 0x22, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, + 0x02, 0x02, 0x12, 0x03, 0x4b, 0x04, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x06, + 0x12, 0x03, 0x4b, 0x04, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x01, 0x12, 0x03, + 0x4b, 0x11, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x02, 0x03, 0x12, 0x03, 0x4b, 0x21, + 0x22, 0x0a, 0x38, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x50, 0x00, 0x68, 0x01, 0x1a, 0x2c, 0x20, + 0x41, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x67, 0x6f, + 0x76, 0x65, 0x72, 0x6e, 0x73, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x70, + 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, + 0x04, 0x01, 0x12, 0x03, 0x50, 0x08, 0x1f, 0x0a, 0x1b, 0x0a, 0x04, 0x04, 0x04, 0x04, 0x00, 0x12, + 0x04, 0x52, 0x02, 0x57, 0x03, 0x1a, 0x0d, 0x20, 0x42, 0x61, 0x73, 0x65, 0x20, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x04, 0x00, 0x01, 0x12, 0x03, 0x52, + 0x07, 0x1c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x53, 0x04, + 0x2c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x53, 0x04, + 0x27, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x04, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x53, 0x2a, + 0x2b, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x54, 0x04, 0x25, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x54, 0x04, 0x20, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x04, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x54, 0x23, 0x24, + 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x55, 0x04, 0x2f, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x55, 0x04, 0x2a, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x04, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x55, 0x2d, 0x2e, 0x0a, + 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x04, 0x00, 0x02, 0x03, 0x12, 0x03, 0x56, 0x04, 0x35, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x04, 0x04, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x56, 0x04, 0x30, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x04, 0x04, 0x00, 0x02, 0x03, 0x02, 0x12, 0x03, 0x56, 0x33, 0x34, 0x0a, 0x44, + 0x0a, 0x04, 0x04, 0x04, 0x03, 0x00, 0x12, 0x04, 0x5a, 0x02, 0x5c, 0x03, 0x1a, 0x36, 0x20, 0x43, + 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x2e, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x6d, 0x75, + 0x73, 0x74, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, + 0x72, 0x75, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x03, 0x00, 0x01, 0x12, 0x03, 0x5a, + 0x0a, 0x16, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x5b, 0x04, + 0x32, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, 0x04, 0x12, 0x03, 0x5b, 0x04, + 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x5b, 0x0d, + 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x5b, 0x25, + 0x2d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x5b, 0x30, + 0x31, 0x0a, 0x44, 0x0a, 0x04, 0x04, 0x04, 0x03, 0x01, 0x12, 0x04, 0x5f, 0x02, 0x61, 0x03, 0x1a, + 0x36, 0x20, 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, + 0x6c, 0x65, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x2e, 0x20, 0x41, 0x6e, 0x79, + 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x20, 0x74, + 0x6f, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x03, 0x01, 0x01, + 0x12, 0x03, 0x5f, 0x0a, 0x16, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x03, 0x01, 0x02, 0x00, 0x12, + 0x03, 0x60, 0x04, 0x32, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x01, 0x02, 0x00, 0x04, 0x12, + 0x03, 0x60, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x01, 0x02, 0x00, 0x06, 0x12, + 0x03, 0x60, 0x0d, 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x01, 0x02, 0x00, 0x01, 0x12, + 0x03, 0x60, 0x25, 0x2d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x01, 0x02, 0x00, 0x03, 0x12, + 0x03, 0x60, 0x30, 0x31, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x04, 0x08, 0x00, 0x12, 0x04, 0x63, 0x02, + 0x67, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x08, 0x00, 0x01, 0x12, 0x03, 0x63, 0x08, 0x0c, + 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, 0x03, 0x64, 0x04, 0x23, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x04, 0x02, 0x00, 0x06, 0x12, 0x03, 0x64, 0x04, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x64, 0x1a, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, + 0x00, 0x03, 0x12, 0x03, 0x64, 0x21, 0x22, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x01, 0x12, + 0x03, 0x65, 0x04, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x06, 0x12, 0x03, 0x65, + 0x04, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x01, 0x12, 0x03, 0x65, 0x11, 0x1e, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x03, 0x12, 0x03, 0x65, 0x21, 0x22, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x04, 0x02, 0x02, 0x12, 0x03, 0x66, 0x04, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x04, 0x02, 0x02, 0x06, 0x12, 0x03, 0x66, 0x04, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, + 0x02, 0x01, 0x12, 0x03, 0x66, 0x11, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x02, 0x03, + 0x12, 0x03, 0x66, 0x21, 0x22, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x0a, 0xb8, 0x1a, + 0x0a, 0x2e, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x19, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xa7, 0x01, 0x0a, 0x10, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x52, 0x0e, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, - 0x12, 0x5c, 0x0a, 0x13, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x73, 0x68, 0x69, 0x70, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x12, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x41, 0x64, 0x64, 0x65, 0x64, 0x12, 0x60, - 0x0a, 0x15, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, - 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x73, 0x68, 0x69, 0x70, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x14, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, - 0x22, 0x9b, 0x04, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x64, 0x12, 0x31, 0x0a, 0x15, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, - 0x79, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x12, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x49, 0x6e, 0x62, - 0x6f, 0x78, 0x49, 0x64, 0x12, 0x52, 0x0a, 0x0d, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x69, 0x6e, - 0x62, 0x6f, 0x78, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x6d, + 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0f, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x65, + 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x19, 0x69, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x80, 0x03, 0x0a, 0x16, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x12, 0x50, 0x0a, 0x0d, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x5f, 0x61, 0x64, 0x64, 0x65, + 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, + 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0c, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x41, 0x64, 0x64, + 0x65, 0x64, 0x12, 0x54, 0x0a, 0x0f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x5f, 0x72, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x64, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x52, 0x0c, 0x61, 0x64, 0x64, 0x65, - 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x65, 0x73, 0x12, 0x56, 0x0a, 0x0f, 0x72, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x47, 0x72, - 0x6f, 0x75, 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x78, - 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x65, 0x73, - 0x12, 0x71, 0x0a, 0x16, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x3b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x14, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x73, 0x1a, 0x22, 0x0a, 0x05, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x12, 0x19, 0x0a, 0x08, - 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x1a, 0x94, 0x01, 0x0a, 0x13, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, - 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, - 0x0a, 0x09, 0x6f, 0x6c, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x00, 0x52, 0x08, 0x6f, 0x6c, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x88, 0x01, 0x01, - 0x12, 0x20, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x88, - 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6f, 0x6c, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6e, 0x65, 0x77, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0xec, - 0x01, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0x42, 0x17, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, - 0x58, 0x4d, 0x4d, 0xaa, 0x02, 0x18, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x6c, 0x73, 0x2e, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, - 0x18, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xe2, 0x02, 0x24, 0x58, 0x6d, 0x74, 0x70, - 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0xea, 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xa8, 0x0f, - 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x38, 0x01, 0x0a, 0x2f, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, - 0x00, 0x12, 0x1a, 0x25, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x20, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, - 0x03, 0x00, 0x22, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x05, 0x00, 0x47, 0x0a, 0x09, 0x0a, - 0x02, 0x08, 0x0b, 0x12, 0x03, 0x05, 0x00, 0x47, 0x0a, 0x3a, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, - 0x09, 0x00, 0x0d, 0x01, 0x1a, 0x2e, 0x20, 0x41, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, - 0x65, 0x64, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x49, 0x44, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x09, 0x08, 0x18, - 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0a, 0x02, 0x26, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x00, 0x02, 0x00, 0x04, 0x12, 0x03, 0x0a, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x0a, 0x0b, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, - 0x00, 0x01, 0x12, 0x03, 0x0a, 0x11, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, - 0x12, 0x03, 0x0a, 0x24, 0x25, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x0b, - 0x02, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x0b, 0x02, 0x08, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x0b, 0x09, 0x18, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x0b, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, - 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x0c, 0x02, 0x2a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, - 0x02, 0x05, 0x12, 0x03, 0x0c, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x01, - 0x12, 0x03, 0x0c, 0x09, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, - 0x0c, 0x28, 0x29, 0x0a, 0x64, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x10, 0x00, 0x1d, 0x01, 0x1a, - 0x23, 0x20, 0x54, 0x68, 0x65, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x0a, 0x22, 0x33, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6c, 0x69, 0x6e, 0x74, - 0x3a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x52, 0x45, 0x50, 0x45, 0x41, 0x54, 0x45, - 0x44, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x5f, 0x50, 0x4c, - 0x55, 0x52, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, - 0x12, 0x03, 0x10, 0x08, 0x1e, 0x0a, 0x39, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x14, - 0x02, 0x2e, 0x1a, 0x2c, 0x20, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, 0x74, 0x68, 0x61, - 0x74, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x62, 0x65, 0x65, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x65, - 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x0a, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x04, 0x12, 0x03, 0x14, 0x02, 0x0a, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, 0x14, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x14, 0x1c, 0x29, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, - 0x02, 0x00, 0x03, 0x12, 0x03, 0x14, 0x2c, 0x2d, 0x0a, 0x3b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, - 0x12, 0x03, 0x16, 0x02, 0x30, 0x1a, 0x2e, 0x20, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x62, 0x65, 0x65, 0x6e, 0x20, 0x72, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, - 0x6d, 0x6d, 0x69, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x04, 0x12, 0x03, - 0x16, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x06, 0x12, 0x03, 0x16, 0x0b, - 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x16, 0x1c, 0x2b, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x16, 0x2e, 0x2f, 0x0a, 0x52, 0x0a, - 0x04, 0x04, 0x01, 0x02, 0x02, 0x12, 0x03, 0x18, 0x02, 0x34, 0x1a, 0x45, 0x20, 0x49, 0x6e, 0x73, - 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, - 0x68, 0x61, 0x76, 0x65, 0x20, 0x62, 0x65, 0x65, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, - 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x2c, 0x20, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x04, 0x12, 0x03, 0x18, 0x02, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x06, 0x12, 0x03, 0x18, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x01, 0x02, 0x02, 0x01, 0x12, 0x03, 0x18, 0x1c, 0x2f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x01, 0x02, 0x02, 0x03, 0x12, 0x03, 0x18, 0x32, 0x33, 0x0a, 0x45, 0x0a, 0x04, 0x04, 0x01, 0x02, - 0x03, 0x12, 0x03, 0x1a, 0x02, 0x36, 0x1a, 0x38, 0x20, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x69, - 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x2c, 0x20, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x0a, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x04, 0x12, 0x03, 0x1a, 0x02, 0x0a, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x06, 0x12, 0x03, 0x1a, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x01, 0x02, 0x03, 0x01, 0x12, 0x03, 0x1a, 0x1c, 0x31, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, - 0x02, 0x03, 0x03, 0x12, 0x03, 0x1a, 0x34, 0x35, 0x0a, 0x6b, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, - 0x21, 0x00, 0x38, 0x01, 0x1a, 0x5f, 0x20, 0x41, 0x20, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, - 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, - 0x69, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x20, 0x49, 0x6e, - 0x63, 0x6c, 0x75, 0x64, 0x65, 0x73, 0x20, 0x61, 0x64, 0x64, 0x65, 0x64, 0x2f, 0x72, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, - 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x21, 0x08, - 0x14, 0x0a, 0x41, 0x0a, 0x04, 0x04, 0x02, 0x03, 0x00, 0x12, 0x04, 0x23, 0x02, 0x25, 0x03, 0x1a, - 0x33, 0x20, 0x41, 0x6e, 0x20, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, - 0x77, 0x61, 0x73, 0x20, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x03, 0x00, 0x01, 0x12, 0x03, 0x23, - 0x0a, 0x0f, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x24, 0x04, - 0x18, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x24, 0x04, - 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x24, 0x0b, - 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x24, 0x16, - 0x17, 0x0a, 0x3d, 0x0a, 0x04, 0x04, 0x02, 0x03, 0x01, 0x12, 0x04, 0x28, 0x02, 0x2f, 0x03, 0x1a, - 0x2f, 0x20, 0x41, 0x20, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x61, - 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, - 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x03, 0x01, 0x01, 0x12, 0x03, 0x28, 0x0a, 0x1d, 0x0a, 0x2b, - 0x0a, 0x06, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x12, 0x03, 0x2a, 0x04, 0x1a, 0x1a, 0x1c, 0x20, - 0x54, 0x68, 0x65, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, - 0x61, 0x73, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x02, 0x03, 0x01, 0x02, 0x00, 0x05, 0x12, 0x03, 0x2a, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x02, 0x03, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2a, 0x0b, 0x15, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x02, 0x03, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2a, 0x18, 0x19, 0x0a, 0x23, 0x0a, 0x06, 0x04, - 0x02, 0x03, 0x01, 0x02, 0x01, 0x12, 0x03, 0x2c, 0x04, 0x22, 0x1a, 0x14, 0x20, 0x54, 0x68, 0x65, - 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x0a, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x04, 0x12, 0x03, 0x2c, 0x04, 0x0c, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x05, 0x12, 0x03, 0x2c, 0x0d, 0x13, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x2c, 0x14, 0x1d, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x2c, 0x20, 0x21, - 0x0a, 0x22, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x01, 0x02, 0x02, 0x12, 0x03, 0x2e, 0x04, 0x22, 0x1a, - 0x13, 0x20, 0x54, 0x68, 0x65, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x02, 0x04, 0x12, - 0x03, 0x2e, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x02, 0x05, 0x12, - 0x03, 0x2e, 0x0d, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x02, 0x01, 0x12, - 0x03, 0x2e, 0x14, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x02, 0x03, 0x12, - 0x03, 0x2e, 0x20, 0x21, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x31, 0x02, - 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x05, 0x12, 0x03, 0x31, 0x02, 0x08, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x31, 0x09, 0x1e, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x31, 0x21, 0x22, 0x0a, 0x2e, 0x0a, 0x04, 0x04, - 0x02, 0x02, 0x01, 0x12, 0x03, 0x33, 0x02, 0x23, 0x1a, 0x21, 0x20, 0x54, 0x68, 0x65, 0x20, 0x69, - 0x6e, 0x62, 0x6f, 0x78, 0x65, 0x73, 0x20, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x02, 0x02, 0x01, 0x04, 0x12, 0x03, 0x33, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, - 0x01, 0x06, 0x12, 0x03, 0x33, 0x0b, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x01, - 0x12, 0x03, 0x33, 0x11, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, 0x03, - 0x33, 0x21, 0x22, 0x0a, 0x30, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x02, 0x12, 0x03, 0x35, 0x02, 0x25, - 0x1a, 0x23, 0x20, 0x54, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x65, 0x73, 0x20, 0x72, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, + 0x69, 0x70, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0e, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x12, 0x5c, 0x0a, 0x13, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, + 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x52, 0x12, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x41, 0x64, 0x64, 0x65, 0x64, 0x12, 0x60, 0x0a, 0x15, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, + 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x52, 0x14, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x22, 0x9b, 0x04, 0x0a, 0x0c, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x31, 0x0a, 0x15, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, + 0x74, 0x65, 0x64, 0x42, 0x79, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x12, 0x52, 0x0a, 0x0d, + 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x49, 0x6e, 0x62, + 0x6f, 0x78, 0x52, 0x0c, 0x61, 0x64, 0x64, 0x65, 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x65, 0x73, + 0x12, 0x56, 0x0a, 0x0f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x62, 0x6f, + 0x78, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x6d, 0x74, 0x70, + 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x64, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x64, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x65, 0x73, 0x12, 0x71, 0x0a, 0x16, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, + 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x64, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x14, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x1a, 0x22, 0x0a, 0x05, 0x49, + 0x6e, 0x62, 0x6f, 0x78, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x1a, + 0x94, 0x01, 0x0a, 0x13, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x09, 0x6f, 0x6c, 0x64, 0x5f, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x6f, 0x6c, 0x64, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x08, 0x6e, + 0x65, 0x77, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6f, + 0x6c, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6e, 0x65, 0x77, + 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0xec, 0x01, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x17, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, + 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x73, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x4d, 0xaa, 0x02, 0x18, 0x58, 0x6d, + 0x74, 0x70, 0x2e, 0x4d, 0x6c, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0xca, 0x02, 0x18, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, + 0x73, 0x5c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0xe2, 0x02, 0x24, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x5c, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x5c, 0x47, 0x50, 0x42, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1a, 0x58, 0x6d, 0x74, 0x70, 0x3a, + 0x3a, 0x4d, 0x6c, 0x73, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x73, 0x4a, 0xa8, 0x0f, 0x0a, 0x06, 0x12, 0x04, 0x01, 0x00, 0x38, 0x01, + 0x0a, 0x2f, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x25, 0x20, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x65, 0x6e, 0x63, + 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x03, 0x00, 0x22, 0x0a, 0x08, 0x0a, 0x01, 0x08, + 0x12, 0x03, 0x05, 0x00, 0x47, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x05, 0x00, 0x47, + 0x0a, 0x3a, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x09, 0x00, 0x0d, 0x01, 0x1a, 0x2e, 0x20, 0x41, + 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x61, 0x6e, + 0x64, 0x20, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x49, 0x44, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, + 0x04, 0x00, 0x01, 0x12, 0x03, 0x09, 0x08, 0x18, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, + 0x12, 0x03, 0x0a, 0x02, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x04, 0x12, 0x03, + 0x0a, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x0a, 0x0b, + 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x0a, 0x11, 0x21, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0a, 0x24, 0x25, 0x0a, 0x0b, 0x0a, + 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x0b, 0x02, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, + 0x02, 0x01, 0x05, 0x12, 0x03, 0x0b, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, + 0x01, 0x12, 0x03, 0x0b, 0x09, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, + 0x03, 0x0b, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x02, 0x12, 0x03, 0x0c, 0x02, + 0x2a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x0c, 0x02, 0x08, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x0c, 0x09, 0x25, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x0c, 0x28, 0x29, 0x0a, 0x64, 0x0a, 0x02, 0x04, + 0x01, 0x12, 0x04, 0x10, 0x00, 0x1d, 0x01, 0x1a, 0x23, 0x20, 0x54, 0x68, 0x65, 0x20, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x20, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x0a, 0x22, 0x33, 0x20, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x6c, 0x69, 0x6e, 0x74, 0x3a, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, + 0x20, 0x52, 0x45, 0x50, 0x45, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, + 0x4e, 0x41, 0x4d, 0x45, 0x53, 0x5f, 0x50, 0x4c, 0x55, 0x52, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, + 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x10, 0x08, 0x1e, 0x0a, 0x39, 0x0a, + 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x14, 0x02, 0x2e, 0x1a, 0x2c, 0x20, 0x4d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x62, + 0x65, 0x65, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, + 0x04, 0x12, 0x03, 0x14, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, + 0x03, 0x14, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x14, + 0x1c, 0x29, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x14, 0x2c, 0x2d, + 0x0a, 0x3b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x01, 0x12, 0x03, 0x16, 0x02, 0x30, 0x1a, 0x2e, 0x20, + 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x76, + 0x65, 0x20, 0x62, 0x65, 0x65, 0x6e, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x69, + 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x01, 0x02, 0x01, 0x04, 0x12, 0x03, 0x16, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x01, 0x02, 0x01, 0x06, 0x12, 0x03, 0x16, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, + 0x01, 0x01, 0x12, 0x03, 0x16, 0x1c, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, + 0x12, 0x03, 0x16, 0x2e, 0x2f, 0x0a, 0x52, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x02, 0x12, 0x03, 0x18, + 0x02, 0x34, 0x1a, 0x45, 0x20, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x62, 0x65, 0x65, + 0x6e, 0x20, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x2c, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x65, 0x64, 0x20, 0x62, + 0x79, 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, + 0x02, 0x04, 0x12, 0x03, 0x18, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x06, + 0x12, 0x03, 0x18, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x01, 0x12, 0x03, + 0x18, 0x1c, 0x2f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x03, 0x12, 0x03, 0x18, 0x32, + 0x33, 0x0a, 0x45, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x03, 0x12, 0x03, 0x1a, 0x02, 0x36, 0x1a, 0x38, + 0x20, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, - 0x6d, 0x6d, 0x69, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x04, 0x12, 0x03, - 0x35, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x06, 0x12, 0x03, 0x35, 0x0b, - 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x01, 0x12, 0x03, 0x35, 0x11, 0x20, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x03, 0x12, 0x03, 0x35, 0x23, 0x24, 0x0a, 0x31, 0x0a, - 0x04, 0x04, 0x02, 0x02, 0x03, 0x12, 0x03, 0x37, 0x02, 0x3a, 0x1a, 0x24, 0x20, 0x54, 0x68, 0x65, - 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x0a, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x03, 0x04, 0x12, 0x03, 0x37, 0x02, 0x0a, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x02, 0x02, 0x03, 0x06, 0x12, 0x03, 0x37, 0x0b, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x02, 0x02, 0x03, 0x01, 0x12, 0x03, 0x37, 0x1f, 0x35, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, - 0x02, 0x03, 0x03, 0x12, 0x03, 0x37, 0x38, 0x39, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x6d, 0x69, 0x74, 0x2c, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x65, 0x64, 0x20, 0x62, 0x79, + 0x20, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, + 0x04, 0x12, 0x03, 0x1a, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x06, 0x12, + 0x03, 0x1a, 0x0b, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x01, 0x12, 0x03, 0x1a, + 0x1c, 0x31, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x03, 0x03, 0x12, 0x03, 0x1a, 0x34, 0x35, + 0x0a, 0x6b, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x21, 0x00, 0x38, 0x01, 0x1a, 0x5f, 0x20, 0x41, + 0x20, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x20, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x73, 0x20, 0x61, + 0x64, 0x64, 0x65, 0x64, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x62, + 0x6f, 0x78, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, + 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0a, 0x0a, + 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x21, 0x08, 0x14, 0x0a, 0x41, 0x0a, 0x04, 0x04, 0x02, 0x03, + 0x00, 0x12, 0x04, 0x23, 0x02, 0x25, 0x03, 0x1a, 0x33, 0x20, 0x41, 0x6e, 0x20, 0x69, 0x6e, 0x62, + 0x6f, 0x78, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x61, 0x73, 0x20, 0x61, 0x64, 0x64, 0x65, + 0x64, 0x20, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x02, 0x03, 0x00, 0x01, 0x12, 0x03, 0x23, 0x0a, 0x0f, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, + 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x24, 0x04, 0x18, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, + 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x24, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, + 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x24, 0x0b, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, + 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x24, 0x16, 0x17, 0x0a, 0x3d, 0x0a, 0x04, 0x04, 0x02, 0x03, + 0x01, 0x12, 0x04, 0x28, 0x02, 0x2f, 0x03, 0x1a, 0x2f, 0x20, 0x41, 0x20, 0x73, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, + 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x03, 0x01, + 0x01, 0x12, 0x03, 0x28, 0x0a, 0x1d, 0x0a, 0x2b, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, + 0x12, 0x03, 0x2a, 0x04, 0x1a, 0x1a, 0x1c, 0x20, 0x54, 0x68, 0x65, 0x20, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x77, 0x61, 0x73, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x64, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x05, 0x12, 0x03, + 0x2a, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, + 0x2a, 0x0b, 0x15, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, + 0x2a, 0x18, 0x19, 0x0a, 0x23, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x01, 0x02, 0x01, 0x12, 0x03, 0x2c, + 0x04, 0x22, 0x1a, 0x14, 0x20, 0x54, 0x68, 0x65, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, + 0x73, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, + 0x02, 0x01, 0x04, 0x12, 0x03, 0x2c, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, + 0x02, 0x01, 0x05, 0x12, 0x03, 0x2c, 0x0d, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, + 0x02, 0x01, 0x01, 0x12, 0x03, 0x2c, 0x14, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x01, + 0x02, 0x01, 0x03, 0x12, 0x03, 0x2c, 0x20, 0x21, 0x0a, 0x22, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x01, + 0x02, 0x02, 0x12, 0x03, 0x2e, 0x04, 0x22, 0x1a, 0x13, 0x20, 0x54, 0x68, 0x65, 0x20, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x02, 0x03, 0x01, 0x02, 0x02, 0x04, 0x12, 0x03, 0x2e, 0x04, 0x0c, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x02, 0x03, 0x01, 0x02, 0x02, 0x05, 0x12, 0x03, 0x2e, 0x0d, 0x13, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x02, 0x03, 0x01, 0x02, 0x02, 0x01, 0x12, 0x03, 0x2e, 0x14, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x02, 0x03, 0x01, 0x02, 0x02, 0x03, 0x12, 0x03, 0x2e, 0x20, 0x21, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x31, 0x02, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, + 0x00, 0x05, 0x12, 0x03, 0x31, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, + 0x12, 0x03, 0x31, 0x09, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, + 0x31, 0x21, 0x22, 0x0a, 0x2e, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x01, 0x12, 0x03, 0x33, 0x02, 0x23, + 0x1a, 0x21, 0x20, 0x54, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x65, 0x73, 0x20, 0x61, + 0x64, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x04, 0x12, 0x03, 0x33, 0x02, + 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x06, 0x12, 0x03, 0x33, 0x0b, 0x10, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x01, 0x12, 0x03, 0x33, 0x11, 0x1e, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, 0x03, 0x33, 0x21, 0x22, 0x0a, 0x30, 0x0a, 0x04, 0x04, + 0x02, 0x02, 0x02, 0x12, 0x03, 0x35, 0x02, 0x25, 0x1a, 0x23, 0x20, 0x54, 0x68, 0x65, 0x20, 0x69, + 0x6e, 0x62, 0x6f, 0x78, 0x65, 0x73, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x69, + 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x02, 0x02, 0x02, 0x04, 0x12, 0x03, 0x35, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x02, 0x02, 0x02, 0x06, 0x12, 0x03, 0x35, 0x0b, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, + 0x02, 0x01, 0x12, 0x03, 0x35, 0x11, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x03, + 0x12, 0x03, 0x35, 0x23, 0x24, 0x0a, 0x31, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x03, 0x12, 0x03, 0x37, + 0x02, 0x3a, 0x1a, 0x24, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x03, + 0x04, 0x12, 0x03, 0x37, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x03, 0x06, 0x12, + 0x03, 0x37, 0x0b, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x03, 0x01, 0x12, 0x03, 0x37, + 0x1f, 0x35, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x03, 0x03, 0x12, 0x03, 0x37, 0x38, 0x39, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, ]; include!("xmtp.mls.message_contents.serde.rs"); // @@protoc_insertion_point(module) \ No newline at end of file diff --git a/xmtp_proto/src/gen/xmtp.mls.message_contents.serde.rs b/xmtp_proto/src/gen/xmtp.mls.message_contents.serde.rs index 5b3ec2bfa..550fe8166 100644 --- a/xmtp_proto/src/gen/xmtp.mls.message_contents.serde.rs +++ b/xmtp_proto/src/gen/xmtp.mls.message_contents.serde.rs @@ -1,75 +1,4 @@ // @generated -impl serde::Serialize for AssociationTextVersion { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - let variant = match self { - Self::Unspecified => "ASSOCIATION_TEXT_VERSION_UNSPECIFIED", - Self::AssociationTextVersion1 => "ASSOCIATION_TEXT_VERSION_1", - }; - serializer.serialize_str(variant) - } -} -impl<'de> serde::Deserialize<'de> for AssociationTextVersion { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "ASSOCIATION_TEXT_VERSION_UNSPECIFIED", - "ASSOCIATION_TEXT_VERSION_1", - ]; - - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = AssociationTextVersion; - - 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 { - "ASSOCIATION_TEXT_VERSION_UNSPECIFIED" => Ok(AssociationTextVersion::Unspecified), - "ASSOCIATION_TEXT_VERSION_1" => Ok(AssociationTextVersion::AssociationTextVersion1), - _ => Err(serde::de::Error::unknown_variant(value, FIELDS)), - } - } - } - deserializer.deserialize_any(GeneratedVisitor) - } -} impl serde::Serialize for Compression { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -368,7 +297,7 @@ impl<'de> serde::Deserialize<'de> for ConversationType { deserializer.deserialize_any(GeneratedVisitor) } } -impl serde::Serialize for CredentialRevocation { +impl serde::Serialize for DmMembers { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -376,55 +305,39 @@ impl serde::Serialize for CredentialRevocation { { use serde::ser::SerializeStruct; let mut len = 0; - if self.public_key.is_some() { + if self.dm_member_one.is_some() { len += 1; } - if self.association.is_some() { + if self.dm_member_two.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.CredentialRevocation", len)?; - if let Some(v) = self.public_key.as_ref() { - match v { - credential_revocation::PublicKey::InstallationKey(v) => { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("installationKey", pbjson::private::base64::encode(&v).as_str())?; - } - credential_revocation::PublicKey::UnsignedLegacyCreateIdentityKey(v) => { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("unsignedLegacyCreateIdentityKey", pbjson::private::base64::encode(&v).as_str())?; - } - } + let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.DmMembers", len)?; + if let Some(v) = self.dm_member_one.as_ref() { + struct_ser.serialize_field("dmMemberOne", v)?; } - if let Some(v) = self.association.as_ref() { - match v { - credential_revocation::Association::MessagingAccess(v) => { - struct_ser.serialize_field("messagingAccess", v)?; - } - } + if let Some(v) = self.dm_member_two.as_ref() { + struct_ser.serialize_field("dmMemberTwo", v)?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for CredentialRevocation { +impl<'de> serde::Deserialize<'de> for DmMembers { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "installation_key", - "installationKey", - "unsigned_legacy_create_identity_key", - "unsignedLegacyCreateIdentityKey", - "messaging_access", - "messagingAccess", + "dm_member_one", + "dmMemberOne", + "dm_member_two", + "dmMemberTwo", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - InstallationKey, - UnsignedLegacyCreateIdentityKey, - MessagingAccess, + DmMemberOne, + DmMemberTwo, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -446,9 +359,8 @@ impl<'de> serde::Deserialize<'de> for CredentialRevocation { E: serde::de::Error, { match value { - "installationKey" | "installation_key" => Ok(GeneratedField::InstallationKey), - "unsignedLegacyCreateIdentityKey" | "unsigned_legacy_create_identity_key" => Ok(GeneratedField::UnsignedLegacyCreateIdentityKey), - "messagingAccess" | "messaging_access" => Ok(GeneratedField::MessagingAccess), + "dmMemberOne" | "dm_member_one" => Ok(GeneratedField::DmMemberOne), + "dmMemberTwo" | "dm_member_two" => Ok(GeneratedField::DmMemberTwo), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -458,142 +370,41 @@ impl<'de> serde::Deserialize<'de> for CredentialRevocation { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = CredentialRevocation; + type Value = DmMembers; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.message_contents.CredentialRevocation") + formatter.write_str("struct xmtp.mls.message_contents.DmMembers") } - 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 public_key__ = None; - let mut association__ = None; + let mut dm_member_one__ = None; + let mut dm_member_two__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::InstallationKey => { - if public_key__.is_some() { - return Err(serde::de::Error::duplicate_field("installationKey")); - } - public_key__ = map_.next_value::<::std::option::Option<::pbjson::private::BytesDeserialize<_>>>()?.map(|x| credential_revocation::PublicKey::InstallationKey(x.0)); - } - GeneratedField::UnsignedLegacyCreateIdentityKey => { - if public_key__.is_some() { - return Err(serde::de::Error::duplicate_field("unsignedLegacyCreateIdentityKey")); - } - public_key__ = map_.next_value::<::std::option::Option<::pbjson::private::BytesDeserialize<_>>>()?.map(|x| credential_revocation::PublicKey::UnsignedLegacyCreateIdentityKey(x.0)); - } - GeneratedField::MessagingAccess => { - if association__.is_some() { - return Err(serde::de::Error::duplicate_field("messagingAccess")); + GeneratedField::DmMemberOne => { + if dm_member_one__.is_some() { + return Err(serde::de::Error::duplicate_field("dmMemberOne")); } - association__ = map_.next_value::<::std::option::Option<_>>()?.map(credential_revocation::Association::MessagingAccess) -; - } - } - } - Ok(CredentialRevocation { - public_key: public_key__, - association: association__, - }) - } - } - deserializer.deserialize_struct("xmtp.mls.message_contents.CredentialRevocation", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for EdDsaSignature { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.bytes.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.EdDsaSignature", len)?; - if !self.bytes.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("bytes", pbjson::private::base64::encode(&self.bytes).as_str())?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for EdDsaSignature { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "bytes", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Bytes, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "bytes" => Ok(GeneratedField::Bytes), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + dm_member_one__ = map_.next_value()?; } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = EdDsaSignature; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.message_contents.EdDsaSignature") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut bytes__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Bytes => { - if bytes__.is_some() { - return Err(serde::de::Error::duplicate_field("bytes")); + GeneratedField::DmMemberTwo => { + if dm_member_two__.is_some() { + return Err(serde::de::Error::duplicate_field("dmMemberTwo")); } - bytes__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; + dm_member_two__ = map_.next_value()?; } } } - Ok(EdDsaSignature { - bytes: bytes__.unwrap_or_default(), + Ok(DmMembers { + dm_member_one: dm_member_one__, + dm_member_two: dm_member_two__, }) } } - deserializer.deserialize_struct("xmtp.mls.message_contents.EdDsaSignature", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls.message_contents.DmMembers", FIELDS, GeneratedVisitor) } } impl serde::Serialize for EncodedContent { @@ -636,6 +447,7 @@ impl serde::Serialize for EncodedContent { } if !self.content.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("content", pbjson::private::base64::encode(&self.content).as_str())?; } struct_ser.end() @@ -762,156 +574,6 @@ impl<'de> serde::Deserialize<'de> for EncodedContent { deserializer.deserialize_struct("xmtp.mls.message_contents.EncodedContent", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for GrantMessagingAccessAssociation { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.association_text_version != 0 { - len += 1; - } - if self.signature.is_some() { - len += 1; - } - if !self.account_address.is_empty() { - len += 1; - } - if self.created_ns != 0 { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.GrantMessagingAccessAssociation", len)?; - if self.association_text_version != 0 { - let v = AssociationTextVersion::try_from(self.association_text_version) - .map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", self.association_text_version)))?; - struct_ser.serialize_field("associationTextVersion", &v)?; - } - if let Some(v) = self.signature.as_ref() { - struct_ser.serialize_field("signature", v)?; - } - if !self.account_address.is_empty() { - struct_ser.serialize_field("accountAddress", &self.account_address)?; - } - if self.created_ns != 0 { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("createdNs", ToString::to_string(&self.created_ns).as_str())?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for GrantMessagingAccessAssociation { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "association_text_version", - "associationTextVersion", - "signature", - "account_address", - "accountAddress", - "created_ns", - "createdNs", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - AssociationTextVersion, - Signature, - AccountAddress, - CreatedNs, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "associationTextVersion" | "association_text_version" => Ok(GeneratedField::AssociationTextVersion), - "signature" => Ok(GeneratedField::Signature), - "accountAddress" | "account_address" => Ok(GeneratedField::AccountAddress), - "createdNs" | "created_ns" => Ok(GeneratedField::CreatedNs), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GrantMessagingAccessAssociation; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.message_contents.GrantMessagingAccessAssociation") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut association_text_version__ = None; - let mut signature__ = None; - let mut account_address__ = None; - let mut created_ns__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::AssociationTextVersion => { - if association_text_version__.is_some() { - return Err(serde::de::Error::duplicate_field("associationTextVersion")); - } - association_text_version__ = Some(map_.next_value::()? as i32); - } - GeneratedField::Signature => { - if signature__.is_some() { - return Err(serde::de::Error::duplicate_field("signature")); - } - signature__ = map_.next_value()?; - } - GeneratedField::AccountAddress => { - if account_address__.is_some() { - return Err(serde::de::Error::duplicate_field("accountAddress")); - } - account_address__ = Some(map_.next_value()?); - } - GeneratedField::CreatedNs => { - if created_ns__.is_some() { - return Err(serde::de::Error::duplicate_field("createdNs")); - } - created_ns__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; - } - } - } - Ok(GrantMessagingAccessAssociation { - association_text_version: association_text_version__.unwrap_or_default(), - signature: signature__, - account_address: account_address__.unwrap_or_default(), - created_ns: created_ns__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("xmtp.mls.message_contents.GrantMessagingAccessAssociation", FIELDS, GeneratedVisitor) - } -} impl serde::Serialize for GroupMembership { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -1171,6 +833,9 @@ impl serde::Serialize for GroupMetadataV1 { if !self.creator_inbox_id.is_empty() { len += 1; } + if self.dm_members.is_some() { + len += 1; + } let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.GroupMetadataV1", len)?; if self.conversation_type != 0 { let v = ConversationType::try_from(self.conversation_type) @@ -1183,6 +848,9 @@ impl serde::Serialize for GroupMetadataV1 { if !self.creator_inbox_id.is_empty() { struct_ser.serialize_field("creatorInboxId", &self.creator_inbox_id)?; } + if let Some(v) = self.dm_members.as_ref() { + struct_ser.serialize_field("dmMembers", v)?; + } struct_ser.end() } } @@ -1199,6 +867,8 @@ impl<'de> serde::Deserialize<'de> for GroupMetadataV1 { "creatorAccountAddress", "creator_inbox_id", "creatorInboxId", + "dm_members", + "dmMembers", ]; #[allow(clippy::enum_variant_names)] @@ -1206,6 +876,7 @@ impl<'de> serde::Deserialize<'de> for GroupMetadataV1 { ConversationType, CreatorAccountAddress, CreatorInboxId, + DmMembers, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -1230,6 +901,7 @@ impl<'de> serde::Deserialize<'de> for GroupMetadataV1 { "conversationType" | "conversation_type" => Ok(GeneratedField::ConversationType), "creatorAccountAddress" | "creator_account_address" => Ok(GeneratedField::CreatorAccountAddress), "creatorInboxId" | "creator_inbox_id" => Ok(GeneratedField::CreatorInboxId), + "dmMembers" | "dm_members" => Ok(GeneratedField::DmMembers), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -1252,6 +924,7 @@ impl<'de> serde::Deserialize<'de> for GroupMetadataV1 { let mut conversation_type__ = None; let mut creator_account_address__ = None; let mut creator_inbox_id__ = None; + let mut dm_members__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::ConversationType => { @@ -1272,12 +945,19 @@ impl<'de> serde::Deserialize<'de> for GroupMetadataV1 { } creator_inbox_id__ = Some(map_.next_value()?); } + GeneratedField::DmMembers => { + if dm_members__.is_some() { + return Err(serde::de::Error::duplicate_field("dmMembers")); + } + dm_members__ = map_.next_value()?; + } } } Ok(GroupMetadataV1 { conversation_type: conversation_type__.unwrap_or_default(), creator_account_address: creator_account_address__.unwrap_or_default(), creator_inbox_id: creator_inbox_id__.unwrap_or_default(), + dm_members: dm_members__, }) } } @@ -1870,7 +1550,7 @@ impl<'de> serde::Deserialize<'de> for group_updated::MetadataFieldChange { deserializer.deserialize_struct("xmtp.mls.message_contents.GroupUpdated.MetadataFieldChange", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for Inboxes { +impl serde::Serialize for Inbox { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -1878,30 +1558,30 @@ impl serde::Serialize for Inboxes { { use serde::ser::SerializeStruct; let mut len = 0; - if !self.inbox_ids.is_empty() { + if !self.inbox_id.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.Inboxes", len)?; - if !self.inbox_ids.is_empty() { - struct_ser.serialize_field("inboxIds", &self.inbox_ids)?; + let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.Inbox", len)?; + if !self.inbox_id.is_empty() { + struct_ser.serialize_field("inboxId", &self.inbox_id)?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for Inboxes { +impl<'de> serde::Deserialize<'de> for Inbox { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "inbox_ids", - "inboxIds", + "inbox_id", + "inboxId", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - InboxIds, + InboxId, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -1923,7 +1603,7 @@ impl<'de> serde::Deserialize<'de> for Inboxes { E: serde::de::Error, { match value { - "inboxIds" | "inbox_ids" => Ok(GeneratedField::InboxIds), + "inboxId" | "inbox_id" => Ok(GeneratedField::InboxId), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -1933,36 +1613,36 @@ impl<'de> serde::Deserialize<'de> for Inboxes { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = Inboxes; + type Value = Inbox; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.message_contents.Inboxes") + formatter.write_str("struct xmtp.mls.message_contents.Inbox") } - 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 inbox_ids__ = None; + let mut inbox_id__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::InboxIds => { - if inbox_ids__.is_some() { - return Err(serde::de::Error::duplicate_field("inboxIds")); + GeneratedField::InboxId => { + if inbox_id__.is_some() { + return Err(serde::de::Error::duplicate_field("inboxId")); } - inbox_ids__ = Some(map_.next_value()?); + inbox_id__ = Some(map_.next_value()?); } } } - Ok(Inboxes { - inbox_ids: inbox_ids__.unwrap_or_default(), + Ok(Inbox { + inbox_id: inbox_id__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("xmtp.mls.message_contents.Inboxes", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls.message_contents.Inbox", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for LegacyCreateIdentityAssociation { +impl serde::Serialize for Inboxes { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -1970,38 +1650,30 @@ impl serde::Serialize for LegacyCreateIdentityAssociation { { use serde::ser::SerializeStruct; let mut len = 0; - if self.signature.is_some() { - len += 1; - } - if self.signed_legacy_create_identity_key.is_some() { + if !self.inbox_ids.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.LegacyCreateIdentityAssociation", len)?; - if let Some(v) = self.signature.as_ref() { - struct_ser.serialize_field("signature", v)?; - } - if let Some(v) = self.signed_legacy_create_identity_key.as_ref() { - struct_ser.serialize_field("signedLegacyCreateIdentityKey", v)?; + let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.Inboxes", len)?; + if !self.inbox_ids.is_empty() { + struct_ser.serialize_field("inboxIds", &self.inbox_ids)?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for LegacyCreateIdentityAssociation { +impl<'de> serde::Deserialize<'de> for Inboxes { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "signature", - "signed_legacy_create_identity_key", - "signedLegacyCreateIdentityKey", + "inbox_ids", + "inboxIds", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - Signature, - SignedLegacyCreateIdentityKey, + InboxIds, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -2023,8 +1695,7 @@ impl<'de> serde::Deserialize<'de> for LegacyCreateIdentityAssociation { E: serde::de::Error, { match value { - "signature" => Ok(GeneratedField::Signature), - "signedLegacyCreateIdentityKey" | "signed_legacy_create_identity_key" => Ok(GeneratedField::SignedLegacyCreateIdentityKey), + "inboxIds" | "inbox_ids" => Ok(GeneratedField::InboxIds), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -2034,41 +1705,33 @@ impl<'de> serde::Deserialize<'de> for LegacyCreateIdentityAssociation { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = LegacyCreateIdentityAssociation; + type Value = Inboxes; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.message_contents.LegacyCreateIdentityAssociation") + formatter.write_str("struct xmtp.mls.message_contents.Inboxes") } - 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 signature__ = None; - let mut signed_legacy_create_identity_key__ = None; + let mut inbox_ids__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::Signature => { - if signature__.is_some() { - return Err(serde::de::Error::duplicate_field("signature")); - } - signature__ = map_.next_value()?; - } - GeneratedField::SignedLegacyCreateIdentityKey => { - if signed_legacy_create_identity_key__.is_some() { - return Err(serde::de::Error::duplicate_field("signedLegacyCreateIdentityKey")); + GeneratedField::InboxIds => { + if inbox_ids__.is_some() { + return Err(serde::de::Error::duplicate_field("inboxIds")); } - signed_legacy_create_identity_key__ = map_.next_value()?; + inbox_ids__ = Some(map_.next_value()?); } } } - Ok(LegacyCreateIdentityAssociation { - signature: signature__, - signed_legacy_create_identity_key: signed_legacy_create_identity_key__, + Ok(Inboxes { + inbox_ids: inbox_ids__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("xmtp.mls.message_contents.LegacyCreateIdentityAssociation", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls.message_contents.Inboxes", FIELDS, GeneratedVisitor) } } impl serde::Serialize for MembershipChange { @@ -2605,6 +2268,7 @@ impl serde::Serialize for MessageHistoryKeyType { match v { message_history_key_type::Key::Chacha20Poly1305(v) => { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("chacha20Poly1305", pbjson::private::base64::encode(&v).as_str())?; } } @@ -2703,12 +2367,6 @@ impl serde::Serialize for MessageHistoryReply { if self.encryption_key.is_some() { len += 1; } - if self.signing_key.is_some() { - len += 1; - } - if !self.bundle_hash.is_empty() { - len += 1; - } let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.MessageHistoryReply", len)?; if !self.request_id.is_empty() { struct_ser.serialize_field("requestId", &self.request_id)?; @@ -2719,13 +2377,6 @@ impl serde::Serialize for MessageHistoryReply { if let Some(v) = self.encryption_key.as_ref() { struct_ser.serialize_field("encryptionKey", v)?; } - if let Some(v) = self.signing_key.as_ref() { - struct_ser.serialize_field("signingKey", v)?; - } - if !self.bundle_hash.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("bundleHash", pbjson::private::base64::encode(&self.bundle_hash).as_str())?; - } struct_ser.end() } } @@ -2741,10 +2392,6 @@ impl<'de> serde::Deserialize<'de> for MessageHistoryReply { "url", "encryption_key", "encryptionKey", - "signing_key", - "signingKey", - "bundle_hash", - "bundleHash", ]; #[allow(clippy::enum_variant_names)] @@ -2752,8 +2399,6 @@ impl<'de> serde::Deserialize<'de> for MessageHistoryReply { RequestId, Url, EncryptionKey, - SigningKey, - BundleHash, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -2778,8 +2423,6 @@ impl<'de> serde::Deserialize<'de> for MessageHistoryReply { "requestId" | "request_id" => Ok(GeneratedField::RequestId), "url" => Ok(GeneratedField::Url), "encryptionKey" | "encryption_key" => Ok(GeneratedField::EncryptionKey), - "signingKey" | "signing_key" => Ok(GeneratedField::SigningKey), - "bundleHash" | "bundle_hash" => Ok(GeneratedField::BundleHash), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -2802,8 +2445,6 @@ impl<'de> serde::Deserialize<'de> for MessageHistoryReply { let mut request_id__ = None; let mut url__ = None; let mut encryption_key__ = None; - let mut signing_key__ = None; - let mut bundle_hash__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::RequestId => { @@ -2824,28 +2465,12 @@ impl<'de> serde::Deserialize<'de> for MessageHistoryReply { } encryption_key__ = map_.next_value()?; } - GeneratedField::SigningKey => { - if signing_key__.is_some() { - return Err(serde::de::Error::duplicate_field("signingKey")); - } - signing_key__ = map_.next_value()?; - } - GeneratedField::BundleHash => { - if bundle_hash__.is_some() { - return Err(serde::de::Error::duplicate_field("bundleHash")); - } - bundle_hash__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } } } Ok(MessageHistoryReply { request_id: request_id__.unwrap_or_default(), url: url__.unwrap_or_default(), encryption_key: encryption_key__, - signing_key: signing_key__, - bundle_hash: bundle_hash__.unwrap_or_default(), }) } } @@ -3349,138 +2974,6 @@ impl<'de> serde::Deserialize<'de> for metadata_policy::MetadataBasePolicy { deserializer.deserialize_any(GeneratedVisitor) } } -impl serde::Serialize for MlsCredential { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.installation_public_key.is_empty() { - len += 1; - } - if self.association.is_some() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.MlsCredential", len)?; - if !self.installation_public_key.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("installationPublicKey", pbjson::private::base64::encode(&self.installation_public_key).as_str())?; - } - if let Some(v) = self.association.as_ref() { - match v { - mls_credential::Association::MessagingAccess(v) => { - struct_ser.serialize_field("messagingAccess", v)?; - } - mls_credential::Association::LegacyCreateIdentity(v) => { - struct_ser.serialize_field("legacyCreateIdentity", v)?; - } - } - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for MlsCredential { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "installation_public_key", - "installationPublicKey", - "messaging_access", - "messagingAccess", - "legacy_create_identity", - "legacyCreateIdentity", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - InstallationPublicKey, - MessagingAccess, - LegacyCreateIdentity, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "installationPublicKey" | "installation_public_key" => Ok(GeneratedField::InstallationPublicKey), - "messagingAccess" | "messaging_access" => Ok(GeneratedField::MessagingAccess), - "legacyCreateIdentity" | "legacy_create_identity" => Ok(GeneratedField::LegacyCreateIdentity), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = MlsCredential; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.message_contents.MlsCredential") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut installation_public_key__ = None; - let mut association__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::InstallationPublicKey => { - if installation_public_key__.is_some() { - return Err(serde::de::Error::duplicate_field("installationPublicKey")); - } - installation_public_key__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } - GeneratedField::MessagingAccess => { - if association__.is_some() { - return Err(serde::de::Error::duplicate_field("messagingAccess")); - } - association__ = map_.next_value::<::std::option::Option<_>>()?.map(mls_credential::Association::MessagingAccess) -; - } - GeneratedField::LegacyCreateIdentity => { - if association__.is_some() { - return Err(serde::de::Error::duplicate_field("legacyCreateIdentity")); - } - association__ = map_.next_value::<::std::option::Option<_>>()?.map(mls_credential::Association::LegacyCreateIdentity) -; - } - } - } - Ok(MlsCredential { - installation_public_key: installation_public_key__.unwrap_or_default(), - association: association__, - }) - } - } - deserializer.deserialize_struct("xmtp.mls.message_contents.MlsCredential", FIELDS, GeneratedVisitor) - } -} impl serde::Serialize for PermissionsUpdatePolicy { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -3991,6 +3484,7 @@ impl serde::Serialize for plaintext_envelope::V1 { let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.PlaintextEnvelope.V1", len)?; if !self.content.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("content", pbjson::private::base64::encode(&self.content).as_str())?; } if !self.idempotency_key.is_empty() { @@ -4108,6 +3602,7 @@ impl serde::Serialize for plaintext_envelope::V2 { match v { plaintext_envelope::v2::MessageType::Content(v) => { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("content", pbjson::private::base64::encode(&v).as_str())?; } plaintext_envelope::v2::MessageType::Request(v) => { @@ -4410,247 +3905,3 @@ impl<'de> serde::Deserialize<'de> for PolicySet { deserializer.deserialize_struct("xmtp.mls.message_contents.PolicySet", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for RecoverableEcdsaSignature { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.bytes.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.RecoverableEcdsaSignature", len)?; - if !self.bytes.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("bytes", pbjson::private::base64::encode(&self.bytes).as_str())?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for RecoverableEcdsaSignature { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "bytes", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Bytes, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "bytes" => Ok(GeneratedField::Bytes), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = RecoverableEcdsaSignature; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.message_contents.RecoverableEcdsaSignature") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut bytes__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Bytes => { - if bytes__.is_some() { - return Err(serde::de::Error::duplicate_field("bytes")); - } - bytes__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } - } - } - Ok(RecoverableEcdsaSignature { - bytes: bytes__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("xmtp.mls.message_contents.RecoverableEcdsaSignature", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for RevokeMessagingAccessAssociation { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.association_text_version != 0 { - len += 1; - } - if self.signature.is_some() { - len += 1; - } - if !self.account_address.is_empty() { - len += 1; - } - if self.created_ns != 0 { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls.message_contents.RevokeMessagingAccessAssociation", len)?; - if self.association_text_version != 0 { - let v = AssociationTextVersion::try_from(self.association_text_version) - .map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", self.association_text_version)))?; - struct_ser.serialize_field("associationTextVersion", &v)?; - } - if let Some(v) = self.signature.as_ref() { - struct_ser.serialize_field("signature", v)?; - } - if !self.account_address.is_empty() { - struct_ser.serialize_field("accountAddress", &self.account_address)?; - } - if self.created_ns != 0 { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("createdNs", ToString::to_string(&self.created_ns).as_str())?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for RevokeMessagingAccessAssociation { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "association_text_version", - "associationTextVersion", - "signature", - "account_address", - "accountAddress", - "created_ns", - "createdNs", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - AssociationTextVersion, - Signature, - AccountAddress, - CreatedNs, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "associationTextVersion" | "association_text_version" => Ok(GeneratedField::AssociationTextVersion), - "signature" => Ok(GeneratedField::Signature), - "accountAddress" | "account_address" => Ok(GeneratedField::AccountAddress), - "createdNs" | "created_ns" => Ok(GeneratedField::CreatedNs), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = RevokeMessagingAccessAssociation; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls.message_contents.RevokeMessagingAccessAssociation") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut association_text_version__ = None; - let mut signature__ = None; - let mut account_address__ = None; - let mut created_ns__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::AssociationTextVersion => { - if association_text_version__.is_some() { - return Err(serde::de::Error::duplicate_field("associationTextVersion")); - } - association_text_version__ = Some(map_.next_value::()? as i32); - } - GeneratedField::Signature => { - if signature__.is_some() { - return Err(serde::de::Error::duplicate_field("signature")); - } - signature__ = map_.next_value()?; - } - GeneratedField::AccountAddress => { - if account_address__.is_some() { - return Err(serde::de::Error::duplicate_field("accountAddress")); - } - account_address__ = Some(map_.next_value()?); - } - GeneratedField::CreatedNs => { - if created_ns__.is_some() { - return Err(serde::de::Error::duplicate_field("createdNs")); - } - created_ns__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; - } - } - } - Ok(RevokeMessagingAccessAssociation { - association_text_version: association_text_version__.unwrap_or_default(), - signature: signature__, - account_address: account_address__.unwrap_or_default(), - created_ns: created_ns__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("xmtp.mls.message_contents.RevokeMessagingAccessAssociation", FIELDS, GeneratedVisitor) - } -} diff --git a/xmtp_proto/src/gen/xmtp.mls_validation.v1.rs b/xmtp_proto/src/gen/xmtp.mls_validation.v1.rs index 185633227..87d1a3158 100644 --- a/xmtp_proto/src/gen/xmtp.mls_validation.v1.rs +++ b/xmtp_proto/src/gen/xmtp.mls_validation.v1.rs @@ -1,38 +1,14 @@ // @generated -/// Validates a Inbox-ID Key Package Type -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ValidateInboxIdKeyPackagesResponse { - #[prost(message, repeated, tag="1")] - pub responses: ::prost::alloc::vec::Vec, -} -/// Nested message and enum types in `ValidateInboxIdKeyPackagesResponse`. -pub mod validate_inbox_id_key_packages_response { - /// one response corresponding to information about one key package - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct Response { - #[prost(bool, tag="1")] - pub is_ok: bool, - #[prost(string, tag="2")] - pub error_message: ::prost::alloc::string::String, - #[prost(message, optional, tag="3")] - pub credential: ::core::option::Option, - #[prost(bytes="vec", tag="4")] - pub installation_public_key: ::prost::alloc::vec::Vec, - #[prost(uint64, tag="5")] - pub expiration: u64, - } -} +// This file is @generated by prost-build. /// Contains a batch of serialized Key Packages #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct ValidateKeyPackagesRequest { +pub struct ValidateInboxIdKeyPackagesRequest { #[prost(message, repeated, tag="1")] - pub key_packages: ::prost::alloc::vec::Vec, + pub key_packages: ::prost::alloc::vec::Vec, } -/// Nested message and enum types in `ValidateKeyPackagesRequest`. -pub mod validate_key_packages_request { +/// Nested message and enum types in `ValidateInboxIdKeyPackagesRequest`. +pub mod validate_inbox_id_key_packages_request { /// Wrapper for each key package #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -43,30 +19,28 @@ pub mod validate_key_packages_request { pub is_inbox_id_credential: bool, } } -/// Response to ValidateKeyPackagesRequest +/// Validates a Inbox-ID Key Package Type #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct ValidateKeyPackagesResponse { +pub struct ValidateInboxIdKeyPackagesResponse { #[prost(message, repeated, tag="1")] - pub responses: ::prost::alloc::vec::Vec, + pub responses: ::prost::alloc::vec::Vec, } -/// Nested message and enum types in `ValidateKeyPackagesResponse`. -pub mod validate_key_packages_response { - /// An individual response to one key package +/// Nested message and enum types in `ValidateInboxIdKeyPackagesResponse`. +pub mod validate_inbox_id_key_packages_response { + /// one response corresponding to information about one key package #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] - pub struct ValidationResponse { + pub struct Response { #[prost(bool, tag="1")] pub is_ok: bool, #[prost(string, tag="2")] pub error_message: ::prost::alloc::string::String, - #[prost(bytes="vec", tag="3")] - pub installation_id: ::prost::alloc::vec::Vec, - #[prost(string, tag="4")] - pub account_address: ::prost::alloc::string::String, - #[prost(bytes="vec", tag="5")] - pub credential_identity_bytes: ::prost::alloc::vec::Vec, - #[prost(uint64, tag="6")] + #[prost(message, optional, tag="3")] + pub credential: ::core::option::Option, + #[prost(bytes="vec", tag="4")] + pub installation_public_key: ::prost::alloc::vec::Vec, + #[prost(uint64, tag="5")] pub expiration: u64, } } @@ -128,53 +102,9 @@ pub struct GetAssociationStateResponse { #[prost(message, optional, tag="2")] pub state_diff: ::core::option::Option, } -/// Request to validate an InboxID with the backend service. Ensures an Inbox Id <> Installation key are valid. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ValidateInboxIdsRequest { - /// list of validation requests - #[prost(message, repeated, tag="1")] - pub requests: ::prost::alloc::vec::Vec, -} -/// Nested message and enum types in `ValidateInboxIdsRequest`. -pub mod validate_inbox_ids_request { - /// a single validation request - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct ValidationRequest { - #[prost(message, optional, tag="1")] - pub credential: ::core::option::Option, - #[prost(bytes="vec", tag="2")] - pub installation_public_key: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag="3")] - pub identity_updates: ::prost::alloc::vec::Vec, - } -} -/// Response to ValidateInboxIdRequest -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ValidateInboxIdsResponse { - /// List of validation responses - #[prost(message, repeated, tag="1")] - pub responses: ::prost::alloc::vec::Vec, -} -/// Nested message and enum types in `ValidateInboxIdsResponse`. -pub mod validate_inbox_ids_response { - /// a single validation response - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] - pub struct ValidationResponse { - #[prost(bool, tag="1")] - pub is_ok: bool, - #[prost(string, tag="2")] - pub error_message: ::prost::alloc::string::String, - #[prost(string, tag="3")] - pub inbox_id: ::prost::alloc::string::String, - } -} /// Encoded file descriptor set for the `xmtp.mls_validation.v1` package pub const FILE_DESCRIPTOR_SET: &[u8] = &[ - 0x0a, 0xb5, 0x3d, 0x0a, 0x1f, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x0a, 0xad, 0x26, 0x0a, 0x1f, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x16, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x1a, 0x27, 0x69, 0x64, @@ -182,490 +112,305 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[ 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x22, 0xe4, 0x02, 0x0a, 0x22, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, + 0x22, 0x98, 0x02, 0x0a, 0x21, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x78, 0x6d, 0x74, - 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, - 0x78, 0x49, 0x64, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, - 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x1a, 0xda, 0x01, 0x0a, 0x08, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x13, 0x0a, 0x05, 0x69, 0x73, 0x5f, 0x6f, 0x6b, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x69, 0x73, 0x4f, 0x6b, 0x12, 0x23, 0x0a, 0x0d, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x3c, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x4d, 0x6c, 0x73, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x61, 0x6c, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, - 0x36, 0x0a, 0x17, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x15, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x78, 0x70, - 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x8a, 0x02, 0x0a, 0x1a, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x60, 0x0a, 0x0c, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, - 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x78, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x67, 0x0a, 0x0c, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, + 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x44, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, - 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x2e, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x0b, 0x6b, 0x65, 0x79, - 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x1a, 0x89, 0x01, 0x0a, 0x0a, 0x4b, 0x65, 0x79, - 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x46, 0x0a, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x70, - 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x74, 0x6c, 0x73, - 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x1c, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x42, 0x79, 0x74, - 0x65, 0x73, 0x54, 0x6c, 0x73, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, - 0x33, 0x0a, 0x16, 0x69, 0x73, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x5f, 0x63, - 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x13, 0x69, 0x73, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x61, 0x6c, 0x22, 0x82, 0x03, 0x0a, 0x1b, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x46, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, - 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, - 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, - 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, - 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x1a, 0xfc, 0x01, 0x0a, 0x12, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x13, 0x0a, 0x05, 0x69, 0x73, 0x5f, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x04, 0x69, 0x73, 0x4f, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x69, - 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x3a, 0x0a, - 0x19, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x17, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x49, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x70, - 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, - 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xe4, 0x01, 0x0a, 0x1c, 0x56, 0x61, + 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, + 0x62, 0x6f, 0x78, 0x49, 0x64, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, + 0x67, 0x65, 0x52, 0x0b, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x1a, + 0x89, 0x01, 0x0a, 0x0a, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x46, + 0x0a, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x62, 0x79, + 0x74, 0x65, 0x73, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x1c, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x63, + 0x6b, 0x61, 0x67, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x54, 0x6c, 0x73, 0x53, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x12, 0x33, 0x0a, 0x16, 0x69, 0x73, 0x5f, 0x69, 0x6e, 0x62, + 0x6f, 0x78, 0x5f, 0x69, 0x64, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x73, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, + 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x22, 0xe4, 0x02, 0x0a, 0x22, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x4b, + 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x61, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, + 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x4b, 0x65, + 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x1a, 0xda, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x13, 0x0a, 0x05, 0x69, 0x73, 0x5f, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x04, 0x69, 0x73, 0x4f, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3c, 0x0a, 0x0a, + 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x2e, 0x4d, 0x6c, 0x73, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x0a, + 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x17, 0x69, 0x6e, + 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, + 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x22, 0xe4, 0x01, 0x0a, 0x1c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x68, 0x0a, 0x0e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x78, 0x6d, + 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x1a, 0x5a, 0x0a, + 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x4a, 0x0a, + 0x22, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, + 0x7a, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x1e, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x54, 0x6c, 0x73, 0x53, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x22, 0xf2, 0x01, 0x0a, 0x1d, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x68, 0x0a, 0x0e, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x73, 0x1a, 0x5a, 0x0a, 0x0c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x4a, 0x0a, 0x22, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x74, 0x6c, 0x73, 0x5f, - 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x1e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x79, - 0x74, 0x65, 0x73, 0x54, 0x6c, 0x73, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, - 0x22, 0xf2, 0x01, 0x0a, 0x1d, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x66, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x48, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, + 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x09, 0x72, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x48, + 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x73, 0x1a, 0x69, 0x0a, 0x12, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x13, 0x0a, 0x05, 0x69, 0x73, 0x5f, + 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x69, 0x73, 0x4f, 0x6b, 0x12, 0x23, + 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x22, 0xb6, + 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4b, 0x0a, + 0x0b, 0x6f, 0x6c, 0x64, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0a, + 0x6f, 0x6c, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x0b, 0x6e, 0x65, + 0x77, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x2a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, + 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x6e, 0x65, 0x77, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x22, 0xc9, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x41, + 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x11, 0x61, 0x73, 0x73, 0x6f, 0x63, + 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x10, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x4f, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x66, 0x66, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x66, 0x66, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x44, + 0x69, 0x66, 0x66, 0x32, 0xb3, 0x03, 0x0a, 0x0d, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x41, 0x70, 0x69, 0x12, 0x86, 0x01, 0x0a, 0x15, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, + 0x34, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, - 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x1a, 0x69, 0x0a, 0x12, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x13, 0x0a, 0x05, 0x69, 0x73, 0x5f, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x04, 0x69, 0x73, 0x4f, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x49, 0x64, 0x22, 0xb6, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, - 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x4b, 0x0a, 0x0b, 0x6f, 0x6c, 0x64, 0x5f, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x78, 0x6d, 0x74, 0x70, - 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x6f, 0x6c, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x73, 0x12, 0x4b, 0x0a, 0x0b, 0x6e, 0x65, 0x77, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x52, 0x0a, 0x6e, 0x65, 0x77, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x22, 0xc9, - 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, - 0x0a, 0x11, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x6d, 0x74, 0x70, - 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x10, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x4f, 0x0a, 0x0a, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, - 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x73, 0x73, 0x6f, 0x63, - 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x66, 0x66, 0x52, - 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x66, 0x66, 0x22, 0xdb, 0x02, 0x0a, 0x17, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5d, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, - 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, - 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x08, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x73, 0x1a, 0xe0, 0x01, 0x0a, 0x11, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x0a, 0x63, - 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, - 0x4d, 0x6c, 0x73, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x0a, 0x63, - 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x17, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x69, 0x6e, 0x73, 0x74, - 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, - 0x79, 0x12, 0x55, 0x0a, 0x10, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x78, 0x6d, - 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, - 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x22, 0xe8, 0x01, 0x0a, 0x18, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, - 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, - 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x09, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x1a, 0x69, 0x0a, 0x12, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x13, - 0x0a, 0x05, 0x69, 0x73, 0x5f, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x69, - 0x73, 0x4f, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x62, 0x6f, - 0x78, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x6e, 0x62, 0x6f, - 0x78, 0x49, 0x64, 0x32, 0xa8, 0x05, 0x0a, 0x0d, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x41, 0x70, 0x69, 0x12, 0x80, 0x01, 0x0a, 0x13, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x12, 0x32, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, - 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x33, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x86, 0x01, 0x0a, 0x15, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x73, 0x12, 0x34, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, - 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x80, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x32, 0x2e, 0x78, 0x6d, 0x74, 0x70, - 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, - 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x8e, 0x01, 0x0a, 0x1a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, - 0x67, 0x65, 0x73, 0x12, 0x32, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, - 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, - 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, - 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x77, 0x0a, 0x10, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x73, 0x12, 0x2f, 0x2e, 0x78, 0x6d, 0x74, 0x70, - 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, - 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, - 0x49, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x78, 0x6d, 0x74, + 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x80, + 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x32, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, + 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, - 0x78, 0x49, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xcf, - 0x01, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2d, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x6c, 0x73, 0x5f, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x58, - 0x4d, 0x58, 0xaa, 0x02, 0x15, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x6c, 0x73, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x15, 0x58, 0x6d, 0x74, - 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5c, - 0x56, 0x31, 0xe2, 0x02, 0x21, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x17, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, - 0x6c, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x3a, 0x56, 0x31, - 0x4a, 0x8d, 0x22, 0x0a, 0x07, 0x12, 0x05, 0x01, 0x00, 0x80, 0x01, 0x01, 0x0a, 0x17, 0x0a, 0x01, - 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, 0x0d, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x20, 0x41, 0x50, 0x49, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x02, 0x00, 0x1f, 0x0a, - 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x04, 0x00, 0x31, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x01, - 0x12, 0x03, 0x05, 0x00, 0x23, 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x07, 0x00, 0x44, 0x0a, - 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, 0x03, 0x07, 0x00, 0x44, 0x0a, 0x26, 0x0a, 0x02, 0x06, 0x00, - 0x12, 0x04, 0x0a, 0x00, 0x1b, 0x01, 0x1a, 0x1a, 0x20, 0x52, 0x50, 0x43, 0x73, 0x20, 0x66, 0x6f, - 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x4d, 0x4c, 0x53, 0x20, 0x41, 0x50, - 0x49, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x06, 0x00, 0x01, 0x12, 0x03, 0x0a, 0x08, 0x15, 0x0a, 0x58, - 0x0a, 0x04, 0x06, 0x00, 0x02, 0x00, 0x12, 0x03, 0x0c, 0x02, 0x5e, 0x1a, 0x4b, 0x20, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x61, 0x72, 0x73, - 0x65, 0x73, 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x6b, 0x65, - 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x72, 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x74, 0x20, - 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, - 0x01, 0x12, 0x03, 0x0c, 0x06, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x02, 0x12, - 0x03, 0x0c, 0x1a, 0x34, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0c, - 0x3f, 0x5a, 0x0a, 0x50, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x01, 0x12, 0x03, 0x0f, 0x02, 0x64, 0x1a, - 0x43, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, - 0x70, 0x61, 0x72, 0x73, 0x65, 0x73, 0x20, 0x61, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x73, 0x20, 0x72, 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, 0x74, 0x20, 0x64, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x0f, - 0x06, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x0f, 0x1c, 0x38, - 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x0f, 0x43, 0x60, 0x0a, 0x4f, - 0x0a, 0x04, 0x06, 0x00, 0x02, 0x02, 0x12, 0x03, 0x12, 0x02, 0x5e, 0x1a, 0x42, 0x20, 0x47, 0x65, - 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x61, 0x73, 0x73, - 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x66, - 0x6f, 0x72, 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x12, 0x06, 0x19, 0x0a, 0x0c, 0x0a, - 0x05, 0x06, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x12, 0x1a, 0x34, 0x0a, 0x0c, 0x0a, 0x05, 0x06, - 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x12, 0x3f, 0x5a, 0x0a, 0xaf, 0x01, 0x0a, 0x04, 0x06, 0x00, - 0x02, 0x03, 0x12, 0x03, 0x16, 0x02, 0x6c, 0x1a, 0xa1, 0x01, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x65, 0x73, 0x20, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x44, 0x20, 0x6b, 0x65, 0x79, - 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, - 0x20, 0x74, 0x68, 0x65, 0x6d, 0x2c, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x63, - 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x77, 0x68, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x20, 0x61, 0x6e, 0x20, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x20, 0x3c, 0x3e, 0x20, 0x49, - 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x4b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x69, 0x72, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x61, - 0x6c, 0x6c, 0x79, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, - 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x16, 0x06, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, - 0x03, 0x02, 0x12, 0x03, 0x16, 0x21, 0x3b, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x03, 0x03, - 0x12, 0x03, 0x16, 0x46, 0x68, 0x0a, 0x48, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x04, 0x12, 0x03, 0x1a, - 0x02, 0x55, 0x1a, 0x3b, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x6e, - 0x20, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x44, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x50, 0x61, 0x63, - 0x6b, 0x61, 0x67, 0x65, 0x0a, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x79, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x01, 0x12, 0x03, 0x1a, 0x06, 0x16, 0x0a, 0x0c, 0x0a, - 0x05, 0x06, 0x00, 0x02, 0x04, 0x02, 0x12, 0x03, 0x1a, 0x17, 0x2e, 0x0a, 0x0c, 0x0a, 0x05, 0x06, - 0x00, 0x02, 0x04, 0x03, 0x12, 0x03, 0x1a, 0x39, 0x51, 0x0a, 0x33, 0x0a, 0x02, 0x04, 0x00, 0x12, - 0x04, 0x1e, 0x00, 0x29, 0x01, 0x1a, 0x27, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x73, 0x20, 0x61, 0x20, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x2d, 0x49, 0x44, 0x20, 0x4b, 0x65, 0x79, - 0x20, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x20, 0x54, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0a, - 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x1e, 0x08, 0x2a, 0x0a, 0x4f, 0x0a, 0x04, 0x04, 0x00, - 0x03, 0x00, 0x12, 0x04, 0x20, 0x02, 0x26, 0x03, 0x1a, 0x41, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6b, - 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x00, 0x03, 0x00, 0x01, 0x12, 0x03, 0x20, 0x0a, 0x12, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, - 0x00, 0x02, 0x00, 0x12, 0x03, 0x21, 0x04, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, - 0x02, 0x00, 0x05, 0x12, 0x03, 0x21, 0x04, 0x08, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, - 0x02, 0x00, 0x01, 0x12, 0x03, 0x21, 0x09, 0x0e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, - 0x02, 0x00, 0x03, 0x12, 0x03, 0x21, 0x11, 0x12, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, - 0x02, 0x01, 0x12, 0x03, 0x22, 0x04, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, - 0x01, 0x05, 0x12, 0x03, 0x22, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, - 0x01, 0x01, 0x12, 0x03, 0x22, 0x0b, 0x18, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, - 0x01, 0x03, 0x12, 0x03, 0x22, 0x1b, 0x1c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, - 0x02, 0x12, 0x03, 0x23, 0x04, 0x2f, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x02, - 0x06, 0x12, 0x03, 0x23, 0x04, 0x1f, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x02, - 0x01, 0x12, 0x03, 0x23, 0x20, 0x2a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x02, - 0x03, 0x12, 0x03, 0x23, 0x2d, 0x2e, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x03, - 0x12, 0x03, 0x24, 0x04, 0x26, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x03, 0x05, - 0x12, 0x03, 0x24, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x03, 0x01, - 0x12, 0x03, 0x24, 0x0a, 0x21, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x03, 0x03, - 0x12, 0x03, 0x24, 0x24, 0x25, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x04, 0x12, - 0x03, 0x25, 0x04, 0x1a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x04, 0x05, 0x12, - 0x03, 0x25, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x04, 0x01, 0x12, - 0x03, 0x25, 0x0b, 0x15, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x04, 0x03, 0x12, - 0x03, 0x25, 0x18, 0x19, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x28, 0x02, - 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x04, 0x12, 0x03, 0x28, 0x02, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x28, 0x0b, 0x13, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x28, 0x14, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x28, 0x20, 0x21, 0x0a, 0x39, 0x0a, 0x02, 0x04, 0x01, 0x12, - 0x04, 0x2c, 0x00, 0x34, 0x01, 0x1a, 0x2d, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, - 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, 0x72, 0x69, - 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x50, 0x61, 0x63, 0x6b, 0x61, - 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x2c, 0x08, 0x22, - 0x0a, 0x2c, 0x0a, 0x04, 0x04, 0x01, 0x03, 0x00, 0x12, 0x04, 0x2e, 0x02, 0x31, 0x03, 0x1a, 0x1e, - 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, 0x61, 0x63, - 0x68, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x01, 0x03, 0x00, 0x01, 0x12, 0x03, 0x2e, 0x0a, 0x14, 0x0a, 0x0d, 0x0a, 0x06, - 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x2f, 0x04, 0x2f, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x01, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x2f, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2f, 0x0a, 0x2a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x01, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2f, 0x2d, 0x2e, 0x0a, 0x0d, 0x0a, 0x06, 0x04, - 0x01, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x30, 0x04, 0x24, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, - 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x30, 0x04, 0x08, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, - 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x30, 0x09, 0x1f, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, - 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x30, 0x22, 0x23, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, - 0x02, 0x00, 0x12, 0x03, 0x33, 0x02, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x04, - 0x12, 0x03, 0x33, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, 0x12, 0x03, - 0x33, 0x0b, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x33, 0x16, - 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x33, 0x25, 0x26, 0x0a, - 0x34, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x37, 0x00, 0x43, 0x01, 0x1a, 0x28, 0x20, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x37, 0x08, - 0x23, 0x0a, 0x39, 0x0a, 0x04, 0x04, 0x02, 0x03, 0x00, 0x12, 0x04, 0x39, 0x02, 0x40, 0x03, 0x1a, - 0x2b, 0x20, 0x41, 0x6e, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x20, - 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x6f, 0x6e, 0x65, 0x20, - 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x02, 0x03, 0x00, 0x01, 0x12, 0x03, 0x39, 0x0a, 0x1c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, - 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x3a, 0x04, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, - 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x3a, 0x04, 0x08, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, - 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x3a, 0x09, 0x0e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, - 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x3a, 0x11, 0x12, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, - 0x00, 0x02, 0x01, 0x12, 0x03, 0x3b, 0x04, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, - 0x02, 0x01, 0x05, 0x12, 0x03, 0x3b, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, - 0x02, 0x01, 0x01, 0x12, 0x03, 0x3b, 0x0b, 0x18, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, - 0x02, 0x01, 0x03, 0x12, 0x03, 0x3b, 0x1b, 0x1c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, - 0x02, 0x02, 0x12, 0x03, 0x3c, 0x04, 0x1e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, - 0x02, 0x05, 0x12, 0x03, 0x3c, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, - 0x02, 0x01, 0x12, 0x03, 0x3c, 0x0a, 0x19, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, - 0x02, 0x03, 0x12, 0x03, 0x3c, 0x1c, 0x1d, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, - 0x03, 0x12, 0x03, 0x3d, 0x04, 0x1f, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x03, - 0x05, 0x12, 0x03, 0x3d, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x03, - 0x01, 0x12, 0x03, 0x3d, 0x0b, 0x1a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x03, - 0x03, 0x12, 0x03, 0x3d, 0x1d, 0x1e, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x04, - 0x12, 0x03, 0x3e, 0x04, 0x28, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x04, 0x05, - 0x12, 0x03, 0x3e, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x04, 0x01, - 0x12, 0x03, 0x3e, 0x0a, 0x23, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x04, 0x03, - 0x12, 0x03, 0x3e, 0x26, 0x27, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x05, 0x12, - 0x03, 0x3f, 0x04, 0x1a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x05, 0x05, 0x12, - 0x03, 0x3f, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x05, 0x01, 0x12, - 0x03, 0x3f, 0x0b, 0x15, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x05, 0x03, 0x12, - 0x03, 0x3f, 0x18, 0x19, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x42, 0x02, - 0x2c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x04, 0x12, 0x03, 0x42, 0x02, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x06, 0x12, 0x03, 0x42, 0x0b, 0x1d, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x42, 0x1e, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x42, 0x2a, 0x2b, 0x0a, 0x3b, 0x0a, 0x02, 0x04, 0x03, 0x12, - 0x04, 0x46, 0x00, 0x4d, 0x01, 0x1a, 0x2f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, - 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, 0x72, 0x69, - 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x03, 0x46, - 0x08, 0x24, 0x0a, 0x28, 0x0a, 0x04, 0x04, 0x03, 0x03, 0x00, 0x12, 0x04, 0x48, 0x02, 0x4a, 0x03, - 0x1a, 0x1a, 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, - 0x61, 0x63, 0x68, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x03, 0x03, 0x00, 0x01, 0x12, 0x03, 0x48, 0x0a, 0x16, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x03, - 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x49, 0x04, 0x31, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, - 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x49, 0x04, 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, - 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x49, 0x0a, 0x2c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x03, 0x03, - 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x49, 0x2f, 0x30, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, 0x02, - 0x00, 0x12, 0x03, 0x4c, 0x02, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x04, 0x12, - 0x03, 0x4c, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x06, 0x12, 0x03, 0x4c, - 0x0b, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x01, 0x12, 0x03, 0x4c, 0x18, 0x26, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, 0x4c, 0x29, 0x2a, 0x0a, 0x36, - 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x50, 0x00, 0x59, 0x01, 0x1a, 0x2a, 0x20, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x04, 0x01, 0x12, 0x03, 0x50, - 0x08, 0x25, 0x0a, 0x35, 0x0a, 0x04, 0x04, 0x04, 0x03, 0x00, 0x12, 0x04, 0x52, 0x02, 0x56, 0x03, - 0x1a, 0x27, 0x20, 0x41, 0x6e, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, - 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x6f, 0x6e, 0x65, - 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x03, - 0x00, 0x01, 0x12, 0x03, 0x52, 0x0a, 0x1c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x03, 0x00, 0x02, - 0x00, 0x12, 0x03, 0x53, 0x04, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, - 0x05, 0x12, 0x03, 0x53, 0x04, 0x08, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, - 0x01, 0x12, 0x03, 0x53, 0x09, 0x0e, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x00, - 0x03, 0x12, 0x03, 0x53, 0x11, 0x12, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x03, 0x00, 0x02, 0x01, - 0x12, 0x03, 0x54, 0x04, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x01, 0x05, - 0x12, 0x03, 0x54, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x01, 0x01, - 0x12, 0x03, 0x54, 0x0b, 0x18, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x01, 0x03, - 0x12, 0x03, 0x54, 0x1b, 0x1c, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x04, 0x03, 0x00, 0x02, 0x02, 0x12, - 0x03, 0x55, 0x04, 0x18, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, - 0x03, 0x55, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, - 0x03, 0x55, 0x0b, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x04, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, - 0x03, 0x55, 0x16, 0x17, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, 0x03, 0x58, 0x02, - 0x2c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x04, 0x12, 0x03, 0x58, 0x02, 0x0a, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x06, 0x12, 0x03, 0x58, 0x0b, 0x1d, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x58, 0x1e, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x58, 0x2a, 0x2b, 0x0a, 0x4b, 0x0a, 0x02, 0x04, 0x05, 0x12, - 0x04, 0x5c, 0x00, 0x60, 0x01, 0x1a, 0x3f, 0x20, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, - 0x74, 0x6f, 0x20, 0x67, 0x65, 0x74, 0x20, 0x61, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x61, - 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x05, 0x01, 0x12, 0x03, 0x5c, - 0x08, 0x22, 0x0a, 0x27, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, 0x12, 0x03, 0x5e, 0x02, 0x45, 0x1a, - 0x1a, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x05, 0x02, 0x00, 0x04, 0x12, 0x03, 0x5e, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, - 0x00, 0x06, 0x12, 0x03, 0x5e, 0x0b, 0x34, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x01, - 0x12, 0x03, 0x5e, 0x35, 0x40, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x03, 0x12, 0x03, - 0x5e, 0x43, 0x44, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x01, 0x12, 0x03, 0x5f, 0x02, 0x45, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x04, 0x12, 0x03, 0x5f, 0x02, 0x0a, 0x0a, 0x0c, - 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x06, 0x12, 0x03, 0x5f, 0x0b, 0x34, 0x0a, 0x0c, 0x0a, 0x05, - 0x04, 0x05, 0x02, 0x01, 0x01, 0x12, 0x03, 0x5f, 0x35, 0x40, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, - 0x02, 0x01, 0x03, 0x12, 0x03, 0x5f, 0x43, 0x44, 0x0a, 0x6c, 0x0a, 0x02, 0x04, 0x06, 0x12, 0x04, - 0x64, 0x00, 0x67, 0x01, 0x1a, 0x60, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, - 0x74, 0x6f, 0x20, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2c, 0x20, 0x63, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, - 0x6e, 0x61, 0x6c, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x0a, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x6e, - 0x62, 0x6f, 0x78, 0x49, 0x44, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x06, 0x01, 0x12, 0x03, 0x64, - 0x08, 0x23, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x00, 0x12, 0x03, 0x65, 0x02, 0x44, 0x0a, - 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, 0x06, 0x12, 0x03, 0x65, 0x02, 0x2d, 0x0a, 0x0c, 0x0a, - 0x05, 0x04, 0x06, 0x02, 0x00, 0x01, 0x12, 0x03, 0x65, 0x2e, 0x3f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x06, 0x02, 0x00, 0x03, 0x12, 0x03, 0x65, 0x42, 0x43, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, - 0x01, 0x12, 0x03, 0x66, 0x02, 0x41, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x01, 0x06, 0x12, - 0x03, 0x66, 0x02, 0x31, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x01, 0x01, 0x12, 0x03, 0x66, - 0x32, 0x3c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x01, 0x03, 0x12, 0x03, 0x66, 0x3f, 0x40, - 0x0a, 0x79, 0x0a, 0x02, 0x04, 0x07, 0x12, 0x04, 0x6a, 0x00, 0x74, 0x01, 0x1a, 0x6d, 0x20, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x44, 0x20, 0x77, 0x69, - 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x20, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x20, 0x45, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x73, 0x20, - 0x61, 0x6e, 0x20, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x20, 0x49, 0x64, 0x20, 0x3c, 0x3e, 0x20, 0x49, - 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6b, 0x65, 0x79, 0x20, - 0x61, 0x72, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, - 0x07, 0x01, 0x12, 0x03, 0x6a, 0x08, 0x1f, 0x0a, 0x2b, 0x0a, 0x04, 0x04, 0x07, 0x03, 0x00, 0x12, - 0x04, 0x6c, 0x02, 0x70, 0x03, 0x1a, 0x1d, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, - 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x03, 0x00, 0x01, 0x12, 0x03, 0x6c, - 0x0a, 0x1b, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x6d, 0x04, - 0x2f, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x6d, 0x04, - 0x1f, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x6d, 0x20, - 0x2a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x6d, 0x2d, - 0x2e, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x07, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x6e, 0x04, 0x26, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x6e, 0x04, 0x09, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x6e, 0x0a, 0x21, - 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x6e, 0x24, 0x25, - 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x07, 0x03, 0x00, 0x02, 0x02, 0x12, 0x03, 0x6f, 0x04, 0x4c, 0x0a, - 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x02, 0x04, 0x12, 0x03, 0x6f, 0x04, 0x0c, 0x0a, - 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x02, 0x06, 0x12, 0x03, 0x6f, 0x0d, 0x36, 0x0a, - 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x6f, 0x37, 0x47, 0x0a, - 0x0e, 0x0a, 0x07, 0x04, 0x07, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x6f, 0x4a, 0x4b, 0x0a, - 0x2a, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x00, 0x12, 0x03, 0x73, 0x02, 0x2a, 0x1a, 0x1d, 0x20, 0x6c, - 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, - 0x07, 0x02, 0x00, 0x04, 0x12, 0x03, 0x73, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, - 0x00, 0x06, 0x12, 0x03, 0x73, 0x0b, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x01, - 0x12, 0x03, 0x73, 0x1d, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x03, 0x12, 0x03, - 0x73, 0x28, 0x29, 0x0a, 0x31, 0x0a, 0x02, 0x04, 0x08, 0x12, 0x05, 0x77, 0x00, 0x80, 0x01, 0x01, - 0x1a, 0x24, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x08, 0x01, 0x12, 0x03, 0x77, - 0x08, 0x20, 0x0a, 0x2c, 0x0a, 0x04, 0x04, 0x08, 0x03, 0x00, 0x12, 0x04, 0x79, 0x02, 0x7d, 0x03, - 0x1a, 0x1e, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x0a, - 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x03, 0x00, 0x01, 0x12, 0x03, 0x79, 0x0a, 0x1c, 0x0a, 0x0d, - 0x0a, 0x06, 0x04, 0x08, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x7a, 0x04, 0x13, 0x0a, 0x0e, 0x0a, - 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x7a, 0x04, 0x08, 0x0a, 0x0e, 0x0a, - 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x7a, 0x09, 0x0e, 0x0a, 0x0e, 0x0a, - 0x07, 0x04, 0x08, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x7a, 0x11, 0x12, 0x0a, 0x0d, 0x0a, - 0x06, 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x7b, 0x04, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, - 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x7b, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, - 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x7b, 0x0b, 0x18, 0x0a, 0x0e, 0x0a, 0x07, - 0x04, 0x08, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x7b, 0x1b, 0x1c, 0x0a, 0x0d, 0x0a, 0x06, - 0x04, 0x08, 0x03, 0x00, 0x02, 0x02, 0x12, 0x03, 0x7c, 0x04, 0x18, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x08, 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x7c, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x08, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x7c, 0x0b, 0x13, 0x0a, 0x0e, 0x0a, 0x07, 0x04, - 0x08, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x7c, 0x16, 0x17, 0x0a, 0x2b, 0x0a, 0x04, 0x04, - 0x08, 0x02, 0x00, 0x12, 0x03, 0x7f, 0x02, 0x2c, 0x1a, 0x1e, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x20, - 0x6f, 0x66, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, - 0x04, 0x12, 0x03, 0x7f, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x06, 0x12, - 0x03, 0x7f, 0x0b, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x01, 0x12, 0x03, 0x7f, - 0x1e, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x03, 0x12, 0x03, 0x7f, 0x2a, 0x2b, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x95, 0x01, 0x0a, 0x1a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, + 0x62, 0x6f, 0x78, 0x49, 0x64, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, + 0x12, 0x39, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x65, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x64, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, + 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x78, 0x6d, + 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x62, + 0x6f, 0x78, 0x49, 0x64, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xcf, 0x01, 0x0a, 0x1a, 0x63, 0x6f, + 0x6d, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x6c, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x58, 0x4d, 0x58, 0xaa, 0x02, 0x15, + 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x4d, 0x6c, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x15, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x21, + 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x4d, 0x6c, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0xea, 0x02, 0x17, 0x58, 0x6d, 0x74, 0x70, 0x3a, 0x3a, 0x4d, 0x6c, 0x73, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x3a, 0x56, 0x31, 0x4a, 0xba, 0x14, 0x0a, 0x06, + 0x12, 0x04, 0x01, 0x00, 0x51, 0x01, 0x0a, 0x17, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, + 0x1a, 0x0d, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x41, 0x50, 0x49, 0x0a, 0x0a, + 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x02, 0x00, 0x1f, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, + 0x03, 0x04, 0x00, 0x31, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x01, 0x12, 0x03, 0x05, 0x00, 0x23, 0x0a, + 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x07, 0x00, 0x44, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, 0x12, + 0x03, 0x07, 0x00, 0x44, 0x0a, 0x26, 0x0a, 0x02, 0x06, 0x00, 0x12, 0x04, 0x0a, 0x00, 0x14, 0x01, + 0x1a, 0x1a, 0x20, 0x52, 0x50, 0x43, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x6e, 0x65, 0x77, 0x20, 0x4d, 0x4c, 0x53, 0x20, 0x41, 0x50, 0x49, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, + 0x06, 0x00, 0x01, 0x12, 0x03, 0x0a, 0x08, 0x15, 0x0a, 0x50, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x00, + 0x12, 0x03, 0x0c, 0x02, 0x64, 0x1a, 0x43, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, + 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x61, 0x72, 0x73, 0x65, 0x73, 0x20, 0x61, 0x20, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x72, 0x65, 0x6c, 0x65, 0x76, 0x61, 0x6e, + 0x74, 0x20, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, + 0x02, 0x00, 0x01, 0x12, 0x03, 0x0c, 0x06, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, + 0x02, 0x12, 0x03, 0x0c, 0x1c, 0x38, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x03, 0x12, + 0x03, 0x0c, 0x43, 0x60, 0x0a, 0x4f, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x01, 0x12, 0x03, 0x0f, 0x02, + 0x5e, 0x1a, 0x42, 0x20, 0x47, 0x65, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6e, + 0x61, 0x6c, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, + 0x20, 0x6f, 0x66, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, + 0x0f, 0x06, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x0f, 0x1a, + 0x34, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x0f, 0x3f, 0x5a, 0x0a, + 0xaf, 0x01, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x02, 0x12, 0x03, 0x13, 0x02, 0x73, 0x1a, 0xa1, 0x01, + 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x49, 0x6e, 0x62, 0x6f, 0x78, + 0x49, 0x44, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x63, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x6d, 0x2c, 0x20, 0x77, 0x69, 0x74, + 0x68, 0x6f, 0x75, 0x74, 0x20, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x77, + 0x68, 0x65, 0x74, 0x68, 0x65, 0x72, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, + 0x64, 0x20, 0x3c, 0x3e, 0x20, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x69, 0x72, 0x20, + 0x69, 0x73, 0x20, 0x72, 0x65, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x2e, + 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x13, 0x06, 0x20, 0x0a, + 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x13, 0x21, 0x42, 0x0a, 0x0c, 0x0a, + 0x05, 0x06, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x13, 0x4d, 0x6f, 0x0a, 0x39, 0x0a, 0x02, 0x04, + 0x00, 0x12, 0x04, 0x17, 0x00, 0x1f, 0x01, 0x1a, 0x2d, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x73, 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x50, 0x61, 0x63, + 0x6b, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x17, + 0x08, 0x29, 0x0a, 0x2c, 0x0a, 0x04, 0x04, 0x00, 0x03, 0x00, 0x12, 0x04, 0x19, 0x02, 0x1c, 0x03, + 0x1a, 0x1e, 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, + 0x61, 0x63, 0x68, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x0a, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x03, 0x00, 0x01, 0x12, 0x03, 0x19, 0x0a, 0x14, 0x0a, 0x0d, + 0x0a, 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x1a, 0x04, 0x2f, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x1a, 0x04, 0x09, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1a, 0x0a, 0x2a, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1a, 0x2d, 0x2e, 0x0a, 0x0d, 0x0a, + 0x06, 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x1b, 0x04, 0x24, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x1b, 0x04, 0x08, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x1b, 0x09, 0x1f, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x00, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x1b, 0x22, 0x23, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x00, 0x02, 0x00, 0x12, 0x03, 0x1e, 0x02, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, + 0x00, 0x04, 0x12, 0x03, 0x1e, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x06, + 0x12, 0x03, 0x1e, 0x0b, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, + 0x1e, 0x16, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1e, 0x25, + 0x26, 0x0a, 0x33, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x22, 0x00, 0x2d, 0x01, 0x1a, 0x27, 0x20, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x73, 0x20, 0x61, 0x20, 0x49, 0x6e, 0x62, 0x6f, + 0x78, 0x2d, 0x49, 0x44, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, + 0x20, 0x54, 0x79, 0x70, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x22, + 0x08, 0x2a, 0x0a, 0x4f, 0x0a, 0x04, 0x04, 0x01, 0x03, 0x00, 0x12, 0x04, 0x24, 0x02, 0x2a, 0x03, + 0x1a, 0x41, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, + 0x63, 0x6f, 0x72, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, + 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, + 0x75, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x61, + 0x67, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x03, 0x00, 0x01, 0x12, 0x03, 0x24, 0x0a, + 0x12, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x25, 0x04, 0x13, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x25, 0x04, 0x08, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x25, 0x09, 0x0e, + 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x25, 0x11, 0x12, + 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x26, 0x04, 0x1d, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x26, 0x04, 0x0a, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x26, 0x0b, 0x18, 0x0a, + 0x0e, 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x26, 0x1b, 0x1c, 0x0a, + 0x0d, 0x0a, 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x12, 0x03, 0x27, 0x04, 0x2f, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x06, 0x12, 0x03, 0x27, 0x04, 0x1f, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x27, 0x20, 0x2a, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x27, 0x2d, 0x2e, 0x0a, 0x0d, + 0x0a, 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x03, 0x12, 0x03, 0x28, 0x04, 0x26, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x03, 0x05, 0x12, 0x03, 0x28, 0x04, 0x09, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x28, 0x0a, 0x21, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x01, 0x03, 0x00, 0x02, 0x03, 0x03, 0x12, 0x03, 0x28, 0x24, 0x25, 0x0a, 0x0d, 0x0a, + 0x06, 0x04, 0x01, 0x03, 0x00, 0x02, 0x04, 0x12, 0x03, 0x29, 0x04, 0x1a, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x01, 0x03, 0x00, 0x02, 0x04, 0x05, 0x12, 0x03, 0x29, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x01, 0x03, 0x00, 0x02, 0x04, 0x01, 0x12, 0x03, 0x29, 0x0b, 0x15, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x01, 0x03, 0x00, 0x02, 0x04, 0x03, 0x12, 0x03, 0x29, 0x18, 0x19, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x2c, 0x02, 0x22, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, + 0x00, 0x04, 0x12, 0x03, 0x2c, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x06, + 0x12, 0x03, 0x2c, 0x0b, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, + 0x2c, 0x14, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2c, 0x20, + 0x21, 0x0a, 0x3b, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x30, 0x00, 0x37, 0x01, 0x1a, 0x2f, 0x20, + 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, + 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x0a, 0x0a, 0x0a, + 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x30, 0x08, 0x24, 0x0a, 0x28, 0x0a, 0x04, 0x04, 0x02, + 0x03, 0x00, 0x12, 0x04, 0x32, 0x02, 0x34, 0x03, 0x1a, 0x1a, 0x20, 0x57, 0x72, 0x61, 0x70, 0x70, + 0x65, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x03, 0x00, 0x01, 0x12, 0x03, 0x32, + 0x0a, 0x16, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x33, 0x04, + 0x31, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x33, 0x04, + 0x09, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x33, 0x0a, + 0x2c, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x02, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x33, 0x2f, + 0x30, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x36, 0x02, 0x2b, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x04, 0x12, 0x03, 0x36, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x02, 0x02, 0x00, 0x06, 0x12, 0x03, 0x36, 0x0b, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, + 0x02, 0x00, 0x01, 0x12, 0x03, 0x36, 0x18, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, + 0x03, 0x12, 0x03, 0x36, 0x29, 0x2a, 0x0a, 0x36, 0x0a, 0x02, 0x04, 0x03, 0x12, 0x04, 0x3a, 0x00, + 0x43, 0x01, 0x1a, 0x2a, 0x20, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x6f, + 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, 0x0a, + 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12, 0x03, 0x3a, 0x08, 0x25, 0x0a, 0x35, 0x0a, 0x04, 0x04, 0x03, + 0x03, 0x00, 0x12, 0x04, 0x3c, 0x02, 0x40, 0x03, 0x1a, 0x27, 0x20, 0x41, 0x6e, 0x20, 0x69, 0x6e, + 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x20, 0x74, 0x6f, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x03, 0x00, 0x01, 0x12, 0x03, 0x3c, 0x0a, 0x1c, 0x0a, + 0x0d, 0x0a, 0x06, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x3d, 0x04, 0x13, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x3d, 0x04, 0x08, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x3d, 0x09, 0x0e, 0x0a, 0x0e, + 0x0a, 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x3d, 0x11, 0x12, 0x0a, 0x0d, + 0x0a, 0x06, 0x04, 0x03, 0x03, 0x00, 0x02, 0x01, 0x12, 0x03, 0x3e, 0x04, 0x1d, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x3e, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x3e, 0x0b, 0x18, 0x0a, 0x0e, 0x0a, + 0x07, 0x04, 0x03, 0x03, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x3e, 0x1b, 0x1c, 0x0a, 0x0d, 0x0a, + 0x06, 0x04, 0x03, 0x03, 0x00, 0x02, 0x02, 0x12, 0x03, 0x3f, 0x04, 0x18, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x03, 0x03, 0x00, 0x02, 0x02, 0x05, 0x12, 0x03, 0x3f, 0x04, 0x0a, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x03, 0x03, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x3f, 0x0b, 0x13, 0x0a, 0x0e, 0x0a, 0x07, + 0x04, 0x03, 0x03, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x3f, 0x16, 0x17, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x03, 0x02, 0x00, 0x12, 0x03, 0x42, 0x02, 0x2c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, + 0x00, 0x04, 0x12, 0x03, 0x42, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x06, + 0x12, 0x03, 0x42, 0x0b, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x01, 0x12, 0x03, + 0x42, 0x1e, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, 0x42, 0x2a, + 0x2b, 0x0a, 0x4b, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x46, 0x00, 0x4a, 0x01, 0x1a, 0x3f, 0x20, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x67, 0x65, 0x74, 0x20, 0x61, + 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x73, 0x0a, 0x0a, 0x0a, + 0x0a, 0x03, 0x04, 0x04, 0x01, 0x12, 0x03, 0x46, 0x08, 0x22, 0x0a, 0x27, 0x0a, 0x04, 0x04, 0x04, + 0x02, 0x00, 0x12, 0x03, 0x48, 0x02, 0x45, 0x1a, 0x1a, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x6f, + 0x66, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x04, 0x12, 0x03, 0x48, 0x02, + 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x06, 0x12, 0x03, 0x48, 0x0b, 0x34, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x48, 0x35, 0x40, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x48, 0x43, 0x44, 0x0a, 0x0b, 0x0a, 0x04, 0x04, + 0x04, 0x02, 0x01, 0x12, 0x03, 0x49, 0x02, 0x45, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, + 0x04, 0x12, 0x03, 0x49, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x06, 0x12, + 0x03, 0x49, 0x0b, 0x34, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x01, 0x12, 0x03, 0x49, + 0x35, 0x40, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x03, 0x12, 0x03, 0x49, 0x43, 0x44, + 0x0a, 0x6c, 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04, 0x4e, 0x00, 0x51, 0x01, 0x1a, 0x60, 0x20, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x47, 0x65, 0x74, 0x41, 0x73, + 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, + 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x61, 0x73, 0x73, 0x6f, + 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x0a, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x61, 0x6e, 0x20, 0x49, 0x6e, 0x62, 0x6f, 0x78, 0x49, 0x44, 0x0a, 0x0a, 0x0a, + 0x0a, 0x03, 0x04, 0x05, 0x01, 0x12, 0x03, 0x4e, 0x08, 0x23, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, + 0x02, 0x00, 0x12, 0x03, 0x4f, 0x02, 0x44, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x06, + 0x12, 0x03, 0x4f, 0x02, 0x2d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x01, 0x12, 0x03, + 0x4f, 0x2e, 0x3f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x03, 0x12, 0x03, 0x4f, 0x42, + 0x43, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x01, 0x12, 0x03, 0x50, 0x02, 0x41, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x06, 0x12, 0x03, 0x50, 0x02, 0x31, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x05, 0x02, 0x01, 0x01, 0x12, 0x03, 0x50, 0x32, 0x3c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, + 0x02, 0x01, 0x03, 0x12, 0x03, 0x50, 0x3f, 0x40, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, ]; include!("xmtp.mls_validation.v1.serde.rs"); include!("xmtp.mls_validation.v1.tonic.rs"); diff --git a/xmtp_proto/src/gen/xmtp.mls_validation.v1.serde.rs b/xmtp_proto/src/gen/xmtp.mls_validation.v1.serde.rs index c27c9243b..3fa759b3f 100644 --- a/xmtp_proto/src/gen/xmtp.mls_validation.v1.serde.rs +++ b/xmtp_proto/src/gen/xmtp.mls_validation.v1.serde.rs @@ -325,6 +325,7 @@ impl serde::Serialize for validate_group_messages_request::GroupMessage { let mut struct_ser = serializer.serialize_struct("xmtp.mls_validation.v1.ValidateGroupMessagesRequest.GroupMessage", len)?; if !self.group_message_bytes_tls_serialized.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("groupMessageBytesTlsSerialized", pbjson::private::base64::encode(&self.group_message_bytes_tls_serialized).as_str())?; } struct_ser.end() @@ -625,706 +626,7 @@ impl<'de> serde::Deserialize<'de> for validate_group_messages_response::Validati deserializer.deserialize_struct("xmtp.mls_validation.v1.ValidateGroupMessagesResponse.ValidationResponse", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for ValidateInboxIdKeyPackagesResponse { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.responses.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesResponse", len)?; - if !self.responses.is_empty() { - struct_ser.serialize_field("responses", &self.responses)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for ValidateInboxIdKeyPackagesResponse { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "responses", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Responses, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "responses" => Ok(GeneratedField::Responses), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = ValidateInboxIdKeyPackagesResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut responses__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Responses => { - if responses__.is_some() { - return Err(serde::de::Error::duplicate_field("responses")); - } - responses__ = Some(map_.next_value()?); - } - } - } - Ok(ValidateInboxIdKeyPackagesResponse { - responses: responses__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesResponse", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for validate_inbox_id_key_packages_response::Response { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.is_ok { - len += 1; - } - if !self.error_message.is_empty() { - len += 1; - } - if self.credential.is_some() { - len += 1; - } - if !self.installation_public_key.is_empty() { - len += 1; - } - if self.expiration != 0 { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesResponse.Response", len)?; - if self.is_ok { - struct_ser.serialize_field("isOk", &self.is_ok)?; - } - if !self.error_message.is_empty() { - struct_ser.serialize_field("errorMessage", &self.error_message)?; - } - if let Some(v) = self.credential.as_ref() { - struct_ser.serialize_field("credential", v)?; - } - if !self.installation_public_key.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("installationPublicKey", pbjson::private::base64::encode(&self.installation_public_key).as_str())?; - } - if self.expiration != 0 { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("expiration", ToString::to_string(&self.expiration).as_str())?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for validate_inbox_id_key_packages_response::Response { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "is_ok", - "isOk", - "error_message", - "errorMessage", - "credential", - "installation_public_key", - "installationPublicKey", - "expiration", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - IsOk, - ErrorMessage, - Credential, - InstallationPublicKey, - Expiration, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "isOk" | "is_ok" => Ok(GeneratedField::IsOk), - "errorMessage" | "error_message" => Ok(GeneratedField::ErrorMessage), - "credential" => Ok(GeneratedField::Credential), - "installationPublicKey" | "installation_public_key" => Ok(GeneratedField::InstallationPublicKey), - "expiration" => Ok(GeneratedField::Expiration), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = validate_inbox_id_key_packages_response::Response; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesResponse.Response") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut is_ok__ = None; - let mut error_message__ = None; - let mut credential__ = None; - let mut installation_public_key__ = None; - let mut expiration__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::IsOk => { - if is_ok__.is_some() { - return Err(serde::de::Error::duplicate_field("isOk")); - } - is_ok__ = Some(map_.next_value()?); - } - GeneratedField::ErrorMessage => { - if error_message__.is_some() { - return Err(serde::de::Error::duplicate_field("errorMessage")); - } - error_message__ = Some(map_.next_value()?); - } - GeneratedField::Credential => { - if credential__.is_some() { - return Err(serde::de::Error::duplicate_field("credential")); - } - credential__ = map_.next_value()?; - } - GeneratedField::InstallationPublicKey => { - if installation_public_key__.is_some() { - return Err(serde::de::Error::duplicate_field("installationPublicKey")); - } - installation_public_key__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } - GeneratedField::Expiration => { - if expiration__.is_some() { - return Err(serde::de::Error::duplicate_field("expiration")); - } - expiration__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; - } - } - } - Ok(validate_inbox_id_key_packages_response::Response { - is_ok: is_ok__.unwrap_or_default(), - error_message: error_message__.unwrap_or_default(), - credential: credential__, - installation_public_key: installation_public_key__.unwrap_or_default(), - expiration: expiration__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesResponse.Response", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for ValidateInboxIdsRequest { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.requests.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls_validation.v1.ValidateInboxIdsRequest", len)?; - if !self.requests.is_empty() { - struct_ser.serialize_field("requests", &self.requests)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for ValidateInboxIdsRequest { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "requests", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Requests, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "requests" => Ok(GeneratedField::Requests), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = ValidateInboxIdsRequest; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls_validation.v1.ValidateInboxIdsRequest") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut requests__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Requests => { - if requests__.is_some() { - return Err(serde::de::Error::duplicate_field("requests")); - } - requests__ = Some(map_.next_value()?); - } - } - } - Ok(ValidateInboxIdsRequest { - requests: requests__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("xmtp.mls_validation.v1.ValidateInboxIdsRequest", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for validate_inbox_ids_request::ValidationRequest { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.credential.is_some() { - len += 1; - } - if !self.installation_public_key.is_empty() { - len += 1; - } - if !self.identity_updates.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls_validation.v1.ValidateInboxIdsRequest.ValidationRequest", len)?; - if let Some(v) = self.credential.as_ref() { - struct_ser.serialize_field("credential", v)?; - } - if !self.installation_public_key.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("installationPublicKey", pbjson::private::base64::encode(&self.installation_public_key).as_str())?; - } - if !self.identity_updates.is_empty() { - struct_ser.serialize_field("identityUpdates", &self.identity_updates)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for validate_inbox_ids_request::ValidationRequest { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "credential", - "installation_public_key", - "installationPublicKey", - "identity_updates", - "identityUpdates", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Credential, - InstallationPublicKey, - IdentityUpdates, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "credential" => Ok(GeneratedField::Credential), - "installationPublicKey" | "installation_public_key" => Ok(GeneratedField::InstallationPublicKey), - "identityUpdates" | "identity_updates" => Ok(GeneratedField::IdentityUpdates), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = validate_inbox_ids_request::ValidationRequest; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls_validation.v1.ValidateInboxIdsRequest.ValidationRequest") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut credential__ = None; - let mut installation_public_key__ = None; - let mut identity_updates__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Credential => { - if credential__.is_some() { - return Err(serde::de::Error::duplicate_field("credential")); - } - credential__ = map_.next_value()?; - } - GeneratedField::InstallationPublicKey => { - if installation_public_key__.is_some() { - return Err(serde::de::Error::duplicate_field("installationPublicKey")); - } - installation_public_key__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } - GeneratedField::IdentityUpdates => { - if identity_updates__.is_some() { - return Err(serde::de::Error::duplicate_field("identityUpdates")); - } - identity_updates__ = Some(map_.next_value()?); - } - } - } - Ok(validate_inbox_ids_request::ValidationRequest { - credential: credential__, - installation_public_key: installation_public_key__.unwrap_or_default(), - identity_updates: identity_updates__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("xmtp.mls_validation.v1.ValidateInboxIdsRequest.ValidationRequest", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for ValidateInboxIdsResponse { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if !self.responses.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls_validation.v1.ValidateInboxIdsResponse", len)?; - if !self.responses.is_empty() { - struct_ser.serialize_field("responses", &self.responses)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for ValidateInboxIdsResponse { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "responses", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Responses, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "responses" => Ok(GeneratedField::Responses), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = ValidateInboxIdsResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls_validation.v1.ValidateInboxIdsResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut responses__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Responses => { - if responses__.is_some() { - return Err(serde::de::Error::duplicate_field("responses")); - } - responses__ = Some(map_.next_value()?); - } - } - } - Ok(ValidateInboxIdsResponse { - responses: responses__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("xmtp.mls_validation.v1.ValidateInboxIdsResponse", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for validate_inbox_ids_response::ValidationResponse { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.is_ok { - len += 1; - } - if !self.error_message.is_empty() { - len += 1; - } - if !self.inbox_id.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("xmtp.mls_validation.v1.ValidateInboxIdsResponse.ValidationResponse", len)?; - if self.is_ok { - struct_ser.serialize_field("isOk", &self.is_ok)?; - } - if !self.error_message.is_empty() { - struct_ser.serialize_field("errorMessage", &self.error_message)?; - } - if !self.inbox_id.is_empty() { - struct_ser.serialize_field("inboxId", &self.inbox_id)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for validate_inbox_ids_response::ValidationResponse { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "is_ok", - "isOk", - "error_message", - "errorMessage", - "inbox_id", - "inboxId", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - IsOk, - ErrorMessage, - InboxId, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> 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 { - "isOk" | "is_ok" => Ok(GeneratedField::IsOk), - "errorMessage" | "error_message" => Ok(GeneratedField::ErrorMessage), - "inboxId" | "inbox_id" => Ok(GeneratedField::InboxId), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = validate_inbox_ids_response::ValidationResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls_validation.v1.ValidateInboxIdsResponse.ValidationResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut is_ok__ = None; - let mut error_message__ = None; - let mut inbox_id__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::IsOk => { - if is_ok__.is_some() { - return Err(serde::de::Error::duplicate_field("isOk")); - } - is_ok__ = Some(map_.next_value()?); - } - GeneratedField::ErrorMessage => { - if error_message__.is_some() { - return Err(serde::de::Error::duplicate_field("errorMessage")); - } - error_message__ = Some(map_.next_value()?); - } - GeneratedField::InboxId => { - if inbox_id__.is_some() { - return Err(serde::de::Error::duplicate_field("inboxId")); - } - inbox_id__ = Some(map_.next_value()?); - } - } - } - Ok(validate_inbox_ids_response::ValidationResponse { - is_ok: is_ok__.unwrap_or_default(), - error_message: error_message__.unwrap_or_default(), - inbox_id: inbox_id__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("xmtp.mls_validation.v1.ValidateInboxIdsResponse.ValidationResponse", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for ValidateKeyPackagesRequest { +impl serde::Serialize for ValidateInboxIdKeyPackagesRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -1335,14 +637,14 @@ impl serde::Serialize for ValidateKeyPackagesRequest { if !self.key_packages.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls_validation.v1.ValidateKeyPackagesRequest", len)?; + let mut struct_ser = serializer.serialize_struct("xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesRequest", len)?; if !self.key_packages.is_empty() { struct_ser.serialize_field("keyPackages", &self.key_packages)?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for ValidateKeyPackagesRequest { +impl<'de> serde::Deserialize<'de> for ValidateInboxIdKeyPackagesRequest { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -1387,13 +689,13 @@ impl<'de> serde::Deserialize<'de> for ValidateKeyPackagesRequest { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = ValidateKeyPackagesRequest; + type Value = ValidateInboxIdKeyPackagesRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls_validation.v1.ValidateKeyPackagesRequest") + formatter.write_str("struct xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesRequest") } - 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>, { @@ -1408,15 +710,15 @@ impl<'de> serde::Deserialize<'de> for ValidateKeyPackagesRequest { } } } - Ok(ValidateKeyPackagesRequest { + Ok(ValidateInboxIdKeyPackagesRequest { key_packages: key_packages__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("xmtp.mls_validation.v1.ValidateKeyPackagesRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesRequest", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for validate_key_packages_request::KeyPackage { +impl serde::Serialize for validate_inbox_id_key_packages_request::KeyPackage { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -1430,9 +732,10 @@ impl serde::Serialize for validate_key_packages_request::KeyPackage { if self.is_inbox_id_credential { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls_validation.v1.ValidateKeyPackagesRequest.KeyPackage", len)?; + let mut struct_ser = serializer.serialize_struct("xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesRequest.KeyPackage", len)?; if !self.key_package_bytes_tls_serialized.is_empty() { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("keyPackageBytesTlsSerialized", pbjson::private::base64::encode(&self.key_package_bytes_tls_serialized).as_str())?; } if self.is_inbox_id_credential { @@ -1441,7 +744,7 @@ impl serde::Serialize for validate_key_packages_request::KeyPackage { struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for validate_key_packages_request::KeyPackage { +impl<'de> serde::Deserialize<'de> for validate_inbox_id_key_packages_request::KeyPackage { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -1490,13 +793,13 @@ impl<'de> serde::Deserialize<'de> for validate_key_packages_request::KeyPackage } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = validate_key_packages_request::KeyPackage; + type Value = validate_inbox_id_key_packages_request::KeyPackage; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls_validation.v1.ValidateKeyPackagesRequest.KeyPackage") + formatter.write_str("struct xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesRequest.KeyPackage") } - 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>, { @@ -1520,16 +823,16 @@ impl<'de> serde::Deserialize<'de> for validate_key_packages_request::KeyPackage } } } - Ok(validate_key_packages_request::KeyPackage { + Ok(validate_inbox_id_key_packages_request::KeyPackage { key_package_bytes_tls_serialized: key_package_bytes_tls_serialized__.unwrap_or_default(), is_inbox_id_credential: is_inbox_id_credential__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("xmtp.mls_validation.v1.ValidateKeyPackagesRequest.KeyPackage", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesRequest.KeyPackage", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for ValidateKeyPackagesResponse { +impl serde::Serialize for ValidateInboxIdKeyPackagesResponse { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -1540,14 +843,14 @@ impl serde::Serialize for ValidateKeyPackagesResponse { if !self.responses.is_empty() { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls_validation.v1.ValidateKeyPackagesResponse", len)?; + let mut struct_ser = serializer.serialize_struct("xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesResponse", len)?; if !self.responses.is_empty() { struct_ser.serialize_field("responses", &self.responses)?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for ValidateKeyPackagesResponse { +impl<'de> serde::Deserialize<'de> for ValidateInboxIdKeyPackagesResponse { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -1591,13 +894,13 @@ impl<'de> serde::Deserialize<'de> for ValidateKeyPackagesResponse { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = ValidateKeyPackagesResponse; + type Value = ValidateInboxIdKeyPackagesResponse; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls_validation.v1.ValidateKeyPackagesResponse") + formatter.write_str("struct xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesResponse") } - 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>, { @@ -1612,15 +915,15 @@ impl<'de> serde::Deserialize<'de> for ValidateKeyPackagesResponse { } } } - Ok(ValidateKeyPackagesResponse { + Ok(ValidateInboxIdKeyPackagesResponse { responses: responses__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("xmtp.mls_validation.v1.ValidateKeyPackagesResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesResponse", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for validate_key_packages_response::ValidationResponse { +impl serde::Serialize for validate_inbox_id_key_packages_response::Response { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -1634,44 +937,39 @@ impl serde::Serialize for validate_key_packages_response::ValidationResponse { if !self.error_message.is_empty() { len += 1; } - if !self.installation_id.is_empty() { - len += 1; - } - if !self.account_address.is_empty() { + if self.credential.is_some() { len += 1; } - if !self.credential_identity_bytes.is_empty() { + if !self.installation_public_key.is_empty() { len += 1; } if self.expiration != 0 { len += 1; } - let mut struct_ser = serializer.serialize_struct("xmtp.mls_validation.v1.ValidateKeyPackagesResponse.ValidationResponse", len)?; + let mut struct_ser = serializer.serialize_struct("xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesResponse.Response", len)?; if self.is_ok { struct_ser.serialize_field("isOk", &self.is_ok)?; } if !self.error_message.is_empty() { struct_ser.serialize_field("errorMessage", &self.error_message)?; } - if !self.installation_id.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("installationId", pbjson::private::base64::encode(&self.installation_id).as_str())?; - } - if !self.account_address.is_empty() { - struct_ser.serialize_field("accountAddress", &self.account_address)?; + if let Some(v) = self.credential.as_ref() { + struct_ser.serialize_field("credential", v)?; } - if !self.credential_identity_bytes.is_empty() { + if !self.installation_public_key.is_empty() { #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("credentialIdentityBytes", pbjson::private::base64::encode(&self.credential_identity_bytes).as_str())?; + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("installationPublicKey", pbjson::private::base64::encode(&self.installation_public_key).as_str())?; } if self.expiration != 0 { #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] struct_ser.serialize_field("expiration", ToString::to_string(&self.expiration).as_str())?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for validate_key_packages_response::ValidationResponse { +impl<'de> serde::Deserialize<'de> for validate_inbox_id_key_packages_response::Response { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -1682,12 +980,9 @@ impl<'de> serde::Deserialize<'de> for validate_key_packages_response::Validation "isOk", "error_message", "errorMessage", - "installation_id", - "installationId", - "account_address", - "accountAddress", - "credential_identity_bytes", - "credentialIdentityBytes", + "credential", + "installation_public_key", + "installationPublicKey", "expiration", ]; @@ -1695,9 +990,8 @@ impl<'de> serde::Deserialize<'de> for validate_key_packages_response::Validation enum GeneratedField { IsOk, ErrorMessage, - InstallationId, - AccountAddress, - CredentialIdentityBytes, + Credential, + InstallationPublicKey, Expiration, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -1722,9 +1016,8 @@ impl<'de> serde::Deserialize<'de> for validate_key_packages_response::Validation match value { "isOk" | "is_ok" => Ok(GeneratedField::IsOk), "errorMessage" | "error_message" => Ok(GeneratedField::ErrorMessage), - "installationId" | "installation_id" => Ok(GeneratedField::InstallationId), - "accountAddress" | "account_address" => Ok(GeneratedField::AccountAddress), - "credentialIdentityBytes" | "credential_identity_bytes" => Ok(GeneratedField::CredentialIdentityBytes), + "credential" => Ok(GeneratedField::Credential), + "installationPublicKey" | "installation_public_key" => Ok(GeneratedField::InstallationPublicKey), "expiration" => Ok(GeneratedField::Expiration), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } @@ -1735,21 +1028,20 @@ impl<'de> serde::Deserialize<'de> for validate_key_packages_response::Validation } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = validate_key_packages_response::ValidationResponse; + type Value = validate_inbox_id_key_packages_response::Response; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct xmtp.mls_validation.v1.ValidateKeyPackagesResponse.ValidationResponse") + formatter.write_str("struct xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesResponse.Response") } - 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 is_ok__ = None; let mut error_message__ = None; - let mut installation_id__ = None; - let mut account_address__ = None; - let mut credential_identity_bytes__ = None; + let mut credential__ = None; + let mut installation_public_key__ = None; let mut expiration__ = None; while let Some(k) = map_.next_key()? { match k { @@ -1765,25 +1057,17 @@ impl<'de> serde::Deserialize<'de> for validate_key_packages_response::Validation } error_message__ = Some(map_.next_value()?); } - GeneratedField::InstallationId => { - if installation_id__.is_some() { - return Err(serde::de::Error::duplicate_field("installationId")); - } - installation_id__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } - GeneratedField::AccountAddress => { - if account_address__.is_some() { - return Err(serde::de::Error::duplicate_field("accountAddress")); + GeneratedField::Credential => { + if credential__.is_some() { + return Err(serde::de::Error::duplicate_field("credential")); } - account_address__ = Some(map_.next_value()?); + credential__ = map_.next_value()?; } - GeneratedField::CredentialIdentityBytes => { - if credential_identity_bytes__.is_some() { - return Err(serde::de::Error::duplicate_field("credentialIdentityBytes")); + GeneratedField::InstallationPublicKey => { + if installation_public_key__.is_some() { + return Err(serde::de::Error::duplicate_field("installationPublicKey")); } - credential_identity_bytes__ = + installation_public_key__ = Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) ; } @@ -1797,16 +1081,15 @@ impl<'de> serde::Deserialize<'de> for validate_key_packages_response::Validation } } } - Ok(validate_key_packages_response::ValidationResponse { + Ok(validate_inbox_id_key_packages_response::Response { is_ok: is_ok__.unwrap_or_default(), error_message: error_message__.unwrap_or_default(), - installation_id: installation_id__.unwrap_or_default(), - account_address: account_address__.unwrap_or_default(), - credential_identity_bytes: credential_identity_bytes__.unwrap_or_default(), + credential: credential__, + installation_public_key: installation_public_key__.unwrap_or_default(), expiration: expiration__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("xmtp.mls_validation.v1.ValidateKeyPackagesResponse.ValidationResponse", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("xmtp.mls_validation.v1.ValidateInboxIdKeyPackagesResponse.Response", FIELDS, GeneratedVisitor) } } diff --git a/xmtp_proto/src/gen/xmtp.mls_validation.v1.tonic.rs b/xmtp_proto/src/gen/xmtp.mls_validation.v1.tonic.rs index 09d1b7b63..a0024d8cc 100644 --- a/xmtp_proto/src/gen/xmtp.mls_validation.v1.tonic.rs +++ b/xmtp_proto/src/gen/xmtp.mls_validation.v1.tonic.rs @@ -1,6 +1,6 @@ // @generated /// Generated client implementations. -#[cfg(feature = "tonic")] +#[cfg(not(target_arch = "wasm32"))] pub mod validation_api_client { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] use tonic::codegen::*; @@ -85,36 +85,6 @@ pub mod validation_api_client { self.inner = self.inner.max_encoding_message_size(limit); self } - pub async fn validate_key_packages( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/xmtp.mls_validation.v1.ValidationApi/ValidateKeyPackages", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new( - "xmtp.mls_validation.v1.ValidationApi", - "ValidateKeyPackages", - ), - ); - self.inner.unary(req, path, codec).await - } pub async fn validate_group_messages( &mut self, request: impl tonic::IntoRequest, @@ -177,7 +147,7 @@ pub mod validation_api_client { } pub async fn validate_inbox_id_key_packages( &mut self, - request: impl tonic::IntoRequest, + request: impl tonic::IntoRequest, ) -> std::result::Result< tonic::Response, tonic::Status, @@ -205,53 +175,16 @@ pub mod validation_api_client { ); self.inner.unary(req, path, codec).await } - pub async fn validate_inbox_ids( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/xmtp.mls_validation.v1.ValidationApi/ValidateInboxIds", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new( - "xmtp.mls_validation.v1.ValidationApi", - "ValidateInboxIds", - ), - ); - self.inner.unary(req, path, codec).await - } } } /// Generated server implementations. -#[cfg(feature = "tonic")] +#[cfg(not(target_arch = "wasm32"))] pub mod validation_api_server { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] use tonic::codegen::*; /// Generated trait containing gRPC methods that should be implemented for use with ValidationApiServer. #[async_trait] pub trait ValidationApi: Send + Sync + 'static { - async fn validate_key_packages( - &self, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; async fn validate_group_messages( &self, request: tonic::Request, @@ -268,34 +201,25 @@ pub mod validation_api_server { >; async fn validate_inbox_id_key_packages( &self, - request: tonic::Request, + request: tonic::Request, ) -> std::result::Result< tonic::Response, tonic::Status, >; - async fn validate_inbox_ids( - &self, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; } #[derive(Debug)] pub struct ValidationApiServer { - inner: _Inner, + inner: Arc, accept_compression_encodings: EnabledCompressionEncodings, send_compression_encodings: EnabledCompressionEncodings, max_decoding_message_size: Option, max_encoding_message_size: Option, } - struct _Inner(Arc); impl ValidationApiServer { pub fn new(inner: T) -> Self { Self::from_arc(Arc::new(inner)) } pub fn from_arc(inner: Arc) -> Self { - let inner = _Inner(inner); Self { inner, accept_compression_encodings: Default::default(), @@ -358,55 +282,7 @@ pub mod validation_api_server { Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { - let inner = self.inner.clone(); match req.uri().path() { - "/xmtp.mls_validation.v1.ValidationApi/ValidateKeyPackages" => { - #[allow(non_camel_case_types)] - struct ValidateKeyPackagesSvc(pub Arc); - impl< - T: ValidationApi, - > tonic::server::UnaryService - for ValidateKeyPackagesSvc { - type Response = super::ValidateKeyPackagesResponse; - 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 { - ::validate_key_packages(&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 inner = inner.0; - let method = ValidateKeyPackagesSvc(inner); - let codec = tonic::codec::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.mls_validation.v1.ValidationApi/ValidateGroupMessages" => { #[allow(non_camel_case_types)] struct ValidateGroupMessagesSvc(pub Arc); @@ -440,7 +316,6 @@ pub mod validation_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = ValidateGroupMessagesSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -487,7 +362,6 @@ pub mod validation_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = GetAssociationStateSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -509,8 +383,9 @@ pub mod validation_api_server { struct ValidateInboxIdKeyPackagesSvc(pub Arc); impl< T: ValidationApi, - > tonic::server::UnaryService - for ValidateInboxIdKeyPackagesSvc { + > tonic::server::UnaryService< + super::ValidateInboxIdKeyPackagesRequest, + > for ValidateInboxIdKeyPackagesSvc { type Response = super::ValidateInboxIdKeyPackagesResponse; type Future = BoxFuture< tonic::Response, @@ -518,7 +393,9 @@ pub mod validation_api_server { >; fn call( &mut self, - request: tonic::Request, + request: tonic::Request< + super::ValidateInboxIdKeyPackagesRequest, + >, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { @@ -537,7 +414,6 @@ pub mod validation_api_server { let max_encoding_message_size = self.max_encoding_message_size; let inner = self.inner.clone(); let fut = async move { - let inner = inner.0; let method = ValidateInboxIdKeyPackagesSvc(inner); let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) @@ -554,60 +430,16 @@ pub mod validation_api_server { }; Box::pin(fut) } - "/xmtp.mls_validation.v1.ValidationApi/ValidateInboxIds" => { - #[allow(non_camel_case_types)] - struct ValidateInboxIdsSvc(pub Arc); - impl< - T: ValidationApi, - > tonic::server::UnaryService - for ValidateInboxIdsSvc { - type Response = super::ValidateInboxIdsResponse; - 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 { - ::validate_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 inner = inner.0; - let method = ValidateInboxIdsSvc(inner); - let codec = tonic::codec::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) - } _ => { Box::pin(async move { Ok( http::Response::builder() .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") + .header("grpc-status", tonic::Code::Unimplemented as i32) + .header( + http::header::CONTENT_TYPE, + tonic::metadata::GRPC_CONTENT_TYPE, + ) .body(empty_body()) .unwrap(), ) @@ -628,16 +460,6 @@ pub mod validation_api_server { } } } - impl Clone for _Inner { - fn clone(&self) -> Self { - Self(Arc::clone(&self.0)) - } - } - impl std::fmt::Debug for _Inner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - } - } impl tonic::server::NamedService for ValidationApiServer { const NAME: &'static str = "xmtp.mls_validation.v1.ValidationApi"; } diff --git a/xmtp_proto/src/gen/xmtp.xmtpv4.rs b/xmtp_proto/src/gen/xmtp.xmtpv4.rs new file mode 100644 index 000000000..d93be56fe --- /dev/null +++ b/xmtp_proto/src/gen/xmtp.xmtpv4.rs @@ -0,0 +1,706 @@ +// @generated +// This file is @generated by prost-build. +/// The last seen entry per originator. Originators that have not been seen are omitted. +/// Entries MUST be sorted in ascending order, so that smaller node ID's appear first. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VectorClock { + #[prost(map="uint32, uint64", tag="1")] + pub node_id_to_sequence_id: ::std::collections::HashMap, +} +/// Data visible to the server that has been authenticated by the client. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AuthenticatedData { + #[prost(uint32, tag="1")] + pub target_originator: u32, + #[prost(bytes="vec", tag="2")] + pub target_topic: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag="3")] + pub last_seen: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ClientEnvelope { + #[prost(message, optional, tag="6")] + pub aad: ::core::option::Option, + #[prost(oneof="client_envelope::Payload", tags="1, 2, 3, 4")] + pub payload: ::core::option::Option, +} +/// Nested message and enum types in `ClientEnvelope`. +pub mod client_envelope { + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Payload { + #[prost(message, tag="1")] + GroupMessage(super::super::mls::api::v1::GroupMessageInput), + #[prost(message, tag="2")] + WelcomeMessage(super::super::mls::api::v1::WelcomeMessageInput), + #[prost(message, tag="3")] + IdentityUpdate(super::super::identity::associations::IdentityUpdate), + #[prost(message, tag="4")] + UploadKeyPackage(super::super::mls::api::v1::UploadKeyPackageRequest), + } +} +/// Wraps client envelope with payer signature +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PayerEnvelope { + /// Protobuf serialized + #[prost(bytes="vec", tag="1")] + pub unsigned_client_envelope: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag="2")] + pub payer_signature: ::core::option::Option, +} +/// For blockchain envelopes, the originator_sid is set by the smart contract, +/// but the originator_ns is set by the publishing node +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UnsignedOriginatorEnvelope { + #[prost(uint32, tag="1")] + pub originator_node_id: u32, + #[prost(uint64, tag="2")] + pub originator_sequence_id: u64, + #[prost(int64, tag="3")] + pub originator_ns: i64, + #[prost(message, optional, tag="4")] + pub payer_envelope: ::core::option::Option, +} +/// An alternative to a signature for blockchain payloads +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct BlockchainProof { + #[prost(uint64, tag="1")] + pub block_number: u64, + #[prost(uint32, tag="2")] + pub publisher_node_id: u32, +} +/// Signed originator envelope +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct OriginatorEnvelope { + /// Protobuf serialized + #[prost(bytes="vec", tag="1")] + pub unsigned_originator_envelope: ::prost::alloc::vec::Vec, + #[prost(oneof="originator_envelope::Proof", tags="2, 3")] + pub proof: ::core::option::Option, +} +/// Nested message and enum types in `OriginatorEnvelope`. +pub mod originator_envelope { + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Proof { + #[prost(message, tag="2")] + OriginatorSignature(super::super::identity::associations::RecoverableEcdsaSignature), + #[prost(message, tag="3")] + BlockchainProof(super::BlockchainProof), + } +} +/// Reports node misbehavior, submittable by nodes or by clients +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MisbehaviorReport { + #[prost(enumeration="Misbehavior", tag="1")] + pub r#type: i32, + #[prost(message, repeated, tag="2")] + pub envelopes: ::prost::alloc::vec::Vec, +} +/// Query for envelopes, shared by query and subscribe endpoints +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EnvelopesQuery { + #[prost(message, optional, tag="3")] + pub last_seen: ::core::option::Option, + #[prost(oneof="envelopes_query::Filter", tags="1, 2")] + pub filter: ::core::option::Option, +} +/// Nested message and enum types in `EnvelopesQuery`. +pub mod envelopes_query { + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Filter { + /// Client queries + #[prost(bytes, tag="1")] + Topic(::prost::alloc::vec::Vec), + /// Node queries + #[prost(uint32, tag="2")] + OriginatorNodeId(u32), + } +} +/// Batch subscribe to envelopes +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BatchSubscribeEnvelopesRequest { + #[prost(message, repeated, tag="1")] + pub requests: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `BatchSubscribeEnvelopesRequest`. +pub mod batch_subscribe_envelopes_request { + /// Single subscription request for envelopes + #[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] + pub struct SubscribeEnvelopesRequest { + #[prost(message, optional, tag="1")] + pub query: ::core::option::Option, + } +} +/// Streamed response for batch subscribe - can be multiple envelopes at once +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BatchSubscribeEnvelopesResponse { + #[prost(message, repeated, tag="1")] + pub envelopes: ::prost::alloc::vec::Vec, +} +/// Query envelopes request +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryEnvelopesRequest { + #[prost(message, optional, tag="1")] + pub query: ::core::option::Option, + #[prost(uint32, tag="2")] + pub limit: u32, +} +/// Query envelopes response +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryEnvelopesResponse { + #[prost(message, repeated, tag="1")] + pub envelopes: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PublishEnvelopeRequest { + #[prost(message, optional, tag="1")] + pub payer_envelope: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PublishEnvelopeResponse { + #[prost(message, optional, tag="1")] + pub originator_envelope: ::core::option::Option, +} +/// Misbehavior types +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum Misbehavior { + Unspecified = 0, + UnavailableNode = 1, + OutOfOrderOriginatorSid = 2, + DuplicateOriginatorSid = 3, + CyclicalMessageOrdering = 4, +} +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 { + Misbehavior::Unspecified => "MISBEHAVIOR_UNSPECIFIED", + Misbehavior::UnavailableNode => "MISBEHAVIOR_UNAVAILABLE_NODE", + Misbehavior::OutOfOrderOriginatorSid => "MISBEHAVIOR_OUT_OF_ORDER_ORIGINATOR_SID", + Misbehavior::DuplicateOriginatorSid => "MISBEHAVIOR_DUPLICATE_ORIGINATOR_SID", + Misbehavior::CyclicalMessageOrdering => "MISBEHAVIOR_CYCLICAL_MESSAGE_ORDERING", + } + } + /// 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_UNAVAILABLE_NODE" => Some(Self::UnavailableNode), + "MISBEHAVIOR_OUT_OF_ORDER_ORIGINATOR_SID" => Some(Self::OutOfOrderOriginatorSid), + "MISBEHAVIOR_DUPLICATE_ORIGINATOR_SID" => Some(Self::DuplicateOriginatorSid), + "MISBEHAVIOR_CYCLICAL_MESSAGE_ORDERING" => Some(Self::CyclicalMessageOrdering), + _ => None, + } + } +} +/// Encoded file descriptor set for the `xmtp.xmtpv4` package +pub const FILE_DESCRIPTOR_SET: &[u8] = &[ + 0x0a, 0xad, 0x3c, 0x0a, 0x24, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2f, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x5f, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, + 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x78, 0x6d, 0x74, 0x70, 0x2e, + 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x27, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x61, + 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x73, 0x73, 0x6f, + 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x25, 0x69, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2f, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x6d, 0x6c, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, + 0x2f, 0x6d, 0x6c, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xba, 0x01, 0x0a, 0x0b, 0x56, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x64, 0x0a, 0x16, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, + 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x54, 0x6f, 0x53, 0x65, 0x71, + 0x75, 0x65, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x6e, 0x6f, + 0x64, 0x65, 0x49, 0x64, 0x54, 0x6f, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x49, 0x64, + 0x1a, 0x45, 0x0a, 0x17, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x54, 0x6f, 0x53, 0x65, 0x71, 0x75, + 0x65, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9a, 0x01, 0x0a, 0x11, 0x41, 0x75, 0x74, 0x68, + 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2b, 0x0a, + 0x11, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, + 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x35, 0x0a, + 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x18, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x56, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, + 0x53, 0x65, 0x65, 0x6e, 0x22, 0x9a, 0x03, 0x0a, 0x0e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x45, + 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x49, 0x0a, 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, + 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, + 0x75, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x4f, 0x0a, 0x0f, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x5f, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x6d, + 0x74, 0x70, 0x2e, 0x6d, 0x6c, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x65, + 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x48, 0x00, 0x52, 0x0e, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x55, 0x0a, 0x0f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, + 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x58, 0x0a, 0x12, 0x75, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x6d, 0x6c, + 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4b, + 0x65, 0x79, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x48, 0x00, 0x52, 0x10, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x63, + 0x6b, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x03, 0x61, 0x61, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, + 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, + 0x61, 0x52, 0x03, 0x61, 0x61, 0x64, 0x42, 0x09, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x22, 0xa9, 0x01, 0x0a, 0x0d, 0x50, 0x61, 0x79, 0x65, 0x72, 0x45, 0x6e, 0x76, 0x65, 0x6c, + 0x6f, 0x70, 0x65, 0x12, 0x38, 0x0a, 0x18, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x16, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x5e, 0x0a, + 0x0f, 0x70, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, + 0x63, 0x64, 0x73, 0x61, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x0e, 0x70, + 0x61, 0x79, 0x65, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xe8, 0x01, + 0x0a, 0x1a, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, + 0x61, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x12, + 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, + 0x61, 0x74, 0x6f, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x6f, 0x72, + 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, + 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x6f, 0x72, 0x69, 0x67, + 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x49, 0x64, + 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x6e, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, + 0x74, 0x6f, 0x72, 0x4e, 0x73, 0x12, 0x41, 0x0a, 0x0e, 0x70, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x65, + 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x50, 0x61, 0x79, 0x65, + 0x72, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x0d, 0x70, 0x61, 0x79, 0x65, 0x72, + 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x22, 0x60, 0x0a, 0x0f, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x21, 0x0a, 0x0c, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2a, + 0x0a, 0x11, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x5f, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x73, 0x68, 0x65, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x22, 0x96, 0x02, 0x0a, 0x12, 0x4f, + 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, + 0x65, 0x12, 0x40, 0x0a, 0x1c, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x6f, 0x72, + 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x1a, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x76, 0x65, 0x6c, + 0x6f, 0x70, 0x65, 0x12, 0x6a, 0x0a, 0x14, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, + 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x35, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x2e, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x52, + 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x63, 0x64, 0x73, 0x61, 0x53, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x48, 0x00, 0x52, 0x13, 0x6f, 0x72, 0x69, 0x67, + 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, + 0x49, 0x0a, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x70, 0x72, + 0x6f, 0x6f, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x6d, 0x74, 0x70, + 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x48, 0x00, 0x52, 0x0f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x42, 0x07, 0x0a, 0x05, 0x70, 0x72, + 0x6f, 0x6f, 0x66, 0x22, 0x80, 0x01, 0x0a, 0x11, 0x4d, 0x69, 0x73, 0x62, 0x65, 0x68, 0x61, 0x76, + 0x69, 0x6f, 0x72, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x4d, 0x69, 0x73, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, + 0x72, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x3d, 0x0a, 0x09, 0x65, 0x6e, 0x76, 0x65, 0x6c, + 0x6f, 0x70, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, + 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x09, 0x65, 0x6e, 0x76, + 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x22, 0x99, 0x01, 0x0a, 0x0e, 0x45, 0x6e, 0x76, 0x65, 0x6c, + 0x6f, 0x70, 0x65, 0x73, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x05, 0x74, 0x6f, 0x70, + 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, + 0x63, 0x12, 0x2e, 0x0a, 0x12, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x5f, + 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, + 0x10, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x4e, 0x6f, 0x64, 0x65, 0x49, + 0x64, 0x12, 0x35, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x78, 0x6d, 0x74, 0x70, + 0x76, 0x34, 0x2e, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x08, + 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x42, 0x08, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x22, 0xd3, 0x01, 0x0a, 0x1e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x61, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x45, 0x6e, + 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x08, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x1a, 0x4e, 0x0a, 0x19, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x78, 0x6d, 0x74, 0x70, + 0x76, 0x34, 0x2e, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x60, 0x0a, 0x1f, 0x42, 0x61, 0x74, 0x63, + 0x68, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, + 0x70, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x09, 0x65, + 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, + 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x4f, 0x72, 0x69, + 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, + 0x09, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x22, 0x60, 0x0a, 0x15, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, + 0x2e, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, + 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x57, 0x0a, 0x16, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x09, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, + 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x6d, 0x74, 0x70, + 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, + 0x6f, 0x72, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x09, 0x65, 0x6e, 0x76, 0x65, + 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x22, 0x5b, 0x0a, 0x16, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, + 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x41, 0x0a, 0x0e, 0x70, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x50, 0x61, 0x79, 0x65, 0x72, 0x45, 0x6e, 0x76, 0x65, 0x6c, + 0x6f, 0x70, 0x65, 0x52, 0x0d, 0x70, 0x61, 0x79, 0x65, 0x72, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, + 0x70, 0x65, 0x22, 0x6b, 0x0a, 0x17, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x45, 0x6e, 0x76, + 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, + 0x13, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x65, 0x6e, 0x76, 0x65, + 0x6c, 0x6f, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, + 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x12, 0x6f, 0x72, 0x69, + 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x2a, + 0xce, 0x01, 0x0a, 0x0b, 0x4d, 0x69, 0x73, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, + 0x1b, 0x0a, 0x17, 0x4d, 0x49, 0x53, 0x42, 0x45, 0x48, 0x41, 0x56, 0x49, 0x4f, 0x52, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1c, + 0x4d, 0x49, 0x53, 0x42, 0x45, 0x48, 0x41, 0x56, 0x49, 0x4f, 0x52, 0x5f, 0x55, 0x4e, 0x41, 0x56, + 0x41, 0x49, 0x4c, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x10, 0x01, 0x12, 0x2b, + 0x0a, 0x27, 0x4d, 0x49, 0x53, 0x42, 0x45, 0x48, 0x41, 0x56, 0x49, 0x4f, 0x52, 0x5f, 0x4f, 0x55, + 0x54, 0x5f, 0x4f, 0x46, 0x5f, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x5f, 0x4f, 0x52, 0x49, 0x47, 0x49, + 0x4e, 0x41, 0x54, 0x4f, 0x52, 0x5f, 0x53, 0x49, 0x44, 0x10, 0x02, 0x12, 0x28, 0x0a, 0x24, 0x4d, + 0x49, 0x53, 0x42, 0x45, 0x48, 0x41, 0x56, 0x49, 0x4f, 0x52, 0x5f, 0x44, 0x55, 0x50, 0x4c, 0x49, + 0x43, 0x41, 0x54, 0x45, 0x5f, 0x4f, 0x52, 0x49, 0x47, 0x49, 0x4e, 0x41, 0x54, 0x4f, 0x52, 0x5f, + 0x53, 0x49, 0x44, 0x10, 0x03, 0x12, 0x29, 0x0a, 0x25, 0x4d, 0x49, 0x53, 0x42, 0x45, 0x48, 0x41, + 0x56, 0x49, 0x4f, 0x52, 0x5f, 0x43, 0x59, 0x43, 0x4c, 0x49, 0x43, 0x41, 0x4c, 0x5f, 0x4d, 0x45, + 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x4f, 0x52, 0x44, 0x45, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x04, + 0x32, 0xb4, 0x03, 0x0a, 0x0e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x41, 0x70, 0x69, 0x12, 0x9e, 0x01, 0x0a, 0x17, 0x42, 0x61, 0x74, 0x63, 0x68, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x12, + 0x2b, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x42, 0x61, + 0x74, 0x63, 0x68, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x45, 0x6e, 0x76, 0x65, + 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x20, 0x3a, 0x01, 0x2a, 0x22, 0x1b, 0x2f, 0x6d, 0x6c, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x73, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x2d, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, + 0x65, 0x73, 0x30, 0x01, 0x12, 0x7d, 0x0a, 0x0e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x45, 0x6e, 0x76, + 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x78, 0x6d, + 0x74, 0x70, 0x76, 0x34, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, + 0x70, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x78, 0x6d, 0x74, + 0x70, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x45, 0x6e, + 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x3a, 0x01, 0x2a, 0x22, 0x17, 0x2f, 0x6d, 0x6c, 0x73, + 0x2f, 0x76, 0x32, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2d, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, + 0x70, 0x65, 0x73, 0x12, 0x81, 0x01, 0x0a, 0x0f, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x45, + 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x23, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x45, 0x6e, 0x76, + 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x78, + 0x6d, 0x74, 0x70, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x73, 0x68, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x3a, 0x01, 0x2a, 0x22, 0x18, 0x2f, + 0x6d, 0x6c, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x2d, 0x65, + 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x42, 0x9f, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, + 0x78, 0x6d, 0x74, 0x70, 0x2e, 0x78, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x42, 0x0f, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x41, 0x70, 0x69, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2e, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x6d, 0x74, 0x70, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x6f, 0x2f, 0x78, 0x6d, 0x74, 0x70, + 0x76, 0x34, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x61, 0x70, 0x69, 0xa2, 0x02, + 0x03, 0x58, 0x58, 0x58, 0xaa, 0x02, 0x0b, 0x58, 0x6d, 0x74, 0x70, 0x2e, 0x58, 0x6d, 0x74, 0x70, + 0x76, 0x34, 0xca, 0x02, 0x0b, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x58, 0x6d, 0x74, 0x70, 0x76, 0x34, + 0xe2, 0x02, 0x17, 0x58, 0x6d, 0x74, 0x70, 0x5c, 0x58, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x5c, 0x47, + 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x58, 0x6d, 0x74, + 0x70, 0x3a, 0x3a, 0x58, 0x6d, 0x74, 0x70, 0x76, 0x34, 0x4a, 0xdc, 0x20, 0x0a, 0x07, 0x12, 0x05, + 0x01, 0x00, 0x95, 0x01, 0x01, 0x0a, 0x23, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x01, 0x00, 0x12, 0x1a, + 0x19, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x41, 0x50, 0x49, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x58, 0x4d, 0x54, 0x50, 0x20, 0x56, 0x34, 0x0a, 0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, + 0x03, 0x03, 0x00, 0x14, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x00, 0x12, 0x03, 0x05, 0x00, 0x26, 0x0a, + 0x09, 0x0a, 0x02, 0x03, 0x01, 0x12, 0x03, 0x06, 0x00, 0x31, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x02, + 0x12, 0x03, 0x07, 0x00, 0x2f, 0x0a, 0x09, 0x0a, 0x02, 0x03, 0x03, 0x12, 0x03, 0x08, 0x00, 0x1e, + 0x0a, 0x08, 0x0a, 0x01, 0x08, 0x12, 0x03, 0x0a, 0x00, 0x45, 0x0a, 0x09, 0x0a, 0x02, 0x08, 0x0b, + 0x12, 0x03, 0x0a, 0x00, 0x45, 0x0a, 0xb7, 0x01, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x0e, 0x00, + 0x10, 0x01, 0x1a, 0xaa, 0x01, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x73, + 0x65, 0x65, 0x6e, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x70, 0x65, 0x72, 0x20, 0x6f, 0x72, + 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x20, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, + 0x61, 0x74, 0x6f, 0x72, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, + 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x65, 0x6e, 0x20, 0x73, 0x65, 0x65, 0x6e, 0x20, 0x61, 0x72, + 0x65, 0x20, 0x6f, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x2e, 0x0a, 0x20, 0x45, 0x6e, 0x74, 0x72, + 0x69, 0x65, 0x73, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x62, 0x65, 0x20, 0x73, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x73, 0x63, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2c, 0x20, 0x73, 0x6f, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x73, + 0x6d, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x49, 0x44, 0x27, 0x73, + 0x20, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x2e, 0x0a, 0x0a, + 0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x0e, 0x08, 0x13, 0x0a, 0x0b, 0x0a, 0x04, 0x04, + 0x00, 0x02, 0x00, 0x12, 0x03, 0x0f, 0x02, 0x31, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, + 0x06, 0x12, 0x03, 0x0f, 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, + 0x03, 0x0f, 0x16, 0x2c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x0f, + 0x2f, 0x30, 0x0a, 0x53, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x13, 0x00, 0x17, 0x01, 0x1a, 0x47, + 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x74, + 0x20, 0x68, 0x61, 0x73, 0x20, 0x62, 0x65, 0x65, 0x6e, 0x20, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, + 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x01, 0x01, 0x12, 0x03, + 0x13, 0x08, 0x19, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x14, 0x02, 0x1f, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x05, 0x12, 0x03, 0x14, 0x02, 0x08, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x14, 0x09, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x14, 0x1d, 0x1e, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, + 0x02, 0x01, 0x12, 0x03, 0x15, 0x02, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x05, + 0x12, 0x03, 0x15, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, + 0x15, 0x08, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x01, 0x03, 0x12, 0x03, 0x15, 0x17, + 0x18, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x02, 0x12, 0x03, 0x16, 0x02, 0x1c, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x01, 0x02, 0x02, 0x06, 0x12, 0x03, 0x16, 0x02, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x01, 0x02, 0x02, 0x01, 0x12, 0x03, 0x16, 0x0e, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, + 0x02, 0x02, 0x03, 0x12, 0x03, 0x16, 0x1a, 0x1b, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, + 0x19, 0x00, 0x21, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02, 0x01, 0x12, 0x03, 0x19, 0x08, 0x16, + 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x02, 0x08, 0x00, 0x12, 0x04, 0x1a, 0x02, 0x1f, 0x03, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x02, 0x08, 0x00, 0x01, 0x12, 0x03, 0x1a, 0x08, 0x0f, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x1b, 0x04, 0x38, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, + 0x00, 0x06, 0x12, 0x03, 0x1b, 0x04, 0x25, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, + 0x12, 0x03, 0x1b, 0x26, 0x33, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, + 0x1b, 0x36, 0x37, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x01, 0x12, 0x03, 0x1c, 0x04, 0x3c, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x06, 0x12, 0x03, 0x1c, 0x04, 0x27, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x02, 0x02, 0x01, 0x01, 0x12, 0x03, 0x1c, 0x28, 0x37, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x02, 0x02, 0x01, 0x03, 0x12, 0x03, 0x1c, 0x3a, 0x3b, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, + 0x02, 0x02, 0x12, 0x03, 0x1d, 0x04, 0x42, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x06, + 0x12, 0x03, 0x1d, 0x04, 0x2d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x01, 0x12, 0x03, + 0x1d, 0x2e, 0x3d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x02, 0x03, 0x12, 0x03, 0x1d, 0x40, + 0x41, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x03, 0x12, 0x03, 0x1e, 0x04, 0x43, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x02, 0x02, 0x03, 0x06, 0x12, 0x03, 0x1e, 0x04, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x02, 0x02, 0x03, 0x01, 0x12, 0x03, 0x1e, 0x2c, 0x3e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, + 0x02, 0x03, 0x03, 0x12, 0x03, 0x1e, 0x41, 0x42, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x04, + 0x12, 0x03, 0x20, 0x02, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x04, 0x06, 0x12, 0x03, + 0x20, 0x02, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x04, 0x01, 0x12, 0x03, 0x20, 0x14, + 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x04, 0x03, 0x12, 0x03, 0x20, 0x1a, 0x1b, 0x0a, + 0x38, 0x0a, 0x02, 0x04, 0x03, 0x12, 0x04, 0x24, 0x00, 0x27, 0x01, 0x1a, 0x2c, 0x20, 0x57, 0x72, + 0x61, 0x70, 0x73, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x65, 0x6e, 0x76, 0x65, 0x6c, + 0x6f, 0x70, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x70, 0x61, 0x79, 0x65, 0x72, 0x20, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x03, 0x01, + 0x12, 0x03, 0x24, 0x08, 0x15, 0x0a, 0x22, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x00, 0x12, 0x03, 0x25, + 0x02, 0x25, 0x22, 0x15, 0x20, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x20, 0x73, 0x65, + 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, + 0x00, 0x05, 0x12, 0x03, 0x25, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x01, + 0x12, 0x03, 0x25, 0x08, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x00, 0x03, 0x12, 0x03, + 0x25, 0x23, 0x24, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x03, 0x02, 0x01, 0x12, 0x03, 0x26, 0x02, 0x4b, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x06, 0x12, 0x03, 0x26, 0x02, 0x36, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x12, 0x03, 0x26, 0x37, 0x46, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x03, 0x02, 0x01, 0x03, 0x12, 0x03, 0x26, 0x49, 0x4a, 0x0a, 0x8e, 0x01, 0x0a, 0x02, 0x04, + 0x04, 0x12, 0x04, 0x2b, 0x00, 0x30, 0x01, 0x1a, 0x81, 0x01, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, + 0x70, 0x65, 0x73, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, + 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x69, 0x64, 0x20, 0x69, 0x73, 0x20, 0x73, 0x65, 0x74, 0x20, 0x62, + 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x6d, 0x61, 0x72, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x61, 0x63, 0x74, 0x2c, 0x0a, 0x20, 0x62, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, + 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x6e, 0x73, 0x20, 0x69, 0x73, 0x20, + 0x73, 0x65, 0x74, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x73, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, + 0x04, 0x01, 0x12, 0x03, 0x2b, 0x08, 0x22, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x00, 0x12, + 0x03, 0x2c, 0x02, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x05, 0x12, 0x03, 0x2c, + 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2c, 0x09, 0x1b, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2c, 0x1e, 0x1f, 0x0a, 0x0b, + 0x0a, 0x04, 0x04, 0x04, 0x02, 0x01, 0x12, 0x03, 0x2d, 0x02, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x04, 0x02, 0x01, 0x05, 0x12, 0x03, 0x2d, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, + 0x01, 0x01, 0x12, 0x03, 0x2d, 0x09, 0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x01, 0x03, + 0x12, 0x03, 0x2d, 0x22, 0x23, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04, 0x02, 0x02, 0x12, 0x03, 0x2e, + 0x02, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x02, 0x05, 0x12, 0x03, 0x2e, 0x02, 0x07, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x02, 0x01, 0x12, 0x03, 0x2e, 0x08, 0x15, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x04, 0x02, 0x02, 0x03, 0x12, 0x03, 0x2e, 0x18, 0x19, 0x0a, 0x0b, 0x0a, 0x04, + 0x04, 0x04, 0x02, 0x03, 0x12, 0x03, 0x2f, 0x02, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, + 0x03, 0x06, 0x12, 0x03, 0x2f, 0x02, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x03, 0x01, + 0x12, 0x03, 0x2f, 0x10, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x03, 0x03, 0x12, 0x03, + 0x2f, 0x21, 0x22, 0x0a, 0x43, 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04, 0x33, 0x00, 0x36, 0x01, 0x1a, + 0x37, 0x20, 0x41, 0x6e, 0x20, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x20, 0x70, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x05, 0x01, 0x12, + 0x03, 0x33, 0x08, 0x17, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, 0x12, 0x03, 0x34, 0x02, + 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x05, 0x12, 0x03, 0x34, 0x02, 0x08, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x01, 0x12, 0x03, 0x34, 0x09, 0x15, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x05, 0x02, 0x00, 0x03, 0x12, 0x03, 0x34, 0x18, 0x19, 0x0a, 0x0b, 0x0a, 0x04, 0x04, + 0x05, 0x02, 0x01, 0x12, 0x03, 0x35, 0x02, 0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, + 0x05, 0x12, 0x03, 0x35, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x01, 0x12, + 0x03, 0x35, 0x09, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x03, 0x12, 0x03, 0x35, + 0x1d, 0x1e, 0x0a, 0x28, 0x0a, 0x02, 0x04, 0x06, 0x12, 0x04, 0x39, 0x00, 0x3f, 0x01, 0x1a, 0x1c, + 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x74, + 0x6f, 0x72, 0x20, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, + 0x04, 0x06, 0x01, 0x12, 0x03, 0x39, 0x08, 0x1a, 0x0a, 0x22, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x00, + 0x12, 0x03, 0x3a, 0x02, 0x29, 0x22, 0x15, 0x20, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x06, 0x02, 0x00, 0x05, 0x12, 0x03, 0x3a, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, + 0x02, 0x00, 0x01, 0x12, 0x03, 0x3a, 0x08, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x00, + 0x03, 0x12, 0x03, 0x3a, 0x27, 0x28, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x06, 0x08, 0x00, 0x12, 0x04, + 0x3b, 0x02, 0x3e, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x08, 0x00, 0x01, 0x12, 0x03, 0x3b, + 0x08, 0x0d, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, 0x01, 0x12, 0x03, 0x3c, 0x04, 0x52, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x01, 0x06, 0x12, 0x03, 0x3c, 0x04, 0x38, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x06, 0x02, 0x01, 0x01, 0x12, 0x03, 0x3c, 0x39, 0x4d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x06, 0x02, 0x01, 0x03, 0x12, 0x03, 0x3c, 0x50, 0x51, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x06, 0x02, + 0x02, 0x12, 0x03, 0x3d, 0x04, 0x29, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x02, 0x06, 0x12, + 0x03, 0x3d, 0x04, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x02, 0x01, 0x12, 0x03, 0x3d, + 0x14, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x06, 0x02, 0x02, 0x03, 0x12, 0x03, 0x3d, 0x27, 0x28, + 0x0a, 0x1f, 0x0a, 0x02, 0x05, 0x00, 0x12, 0x04, 0x42, 0x00, 0x48, 0x01, 0x1a, 0x13, 0x20, 0x4d, + 0x69, 0x73, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x00, 0x01, 0x12, 0x03, 0x42, 0x05, 0x10, 0x0a, 0x0b, 0x0a, + 0x04, 0x05, 0x00, 0x02, 0x00, 0x12, 0x03, 0x43, 0x02, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, + 0x02, 0x00, 0x01, 0x12, 0x03, 0x43, 0x02, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, + 0x02, 0x12, 0x03, 0x43, 0x1c, 0x1d, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x01, 0x12, 0x03, + 0x44, 0x02, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x44, 0x02, + 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x44, 0x21, 0x22, 0x0a, + 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x02, 0x12, 0x03, 0x45, 0x02, 0x2e, 0x0a, 0x0c, 0x0a, 0x05, + 0x05, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x45, 0x02, 0x29, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, + 0x02, 0x02, 0x02, 0x12, 0x03, 0x45, 0x2c, 0x2d, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x03, + 0x12, 0x03, 0x46, 0x02, 0x2b, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, + 0x46, 0x02, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x03, 0x02, 0x12, 0x03, 0x46, 0x29, + 0x2a, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x04, 0x12, 0x03, 0x47, 0x02, 0x2c, 0x0a, 0x0c, + 0x0a, 0x05, 0x05, 0x00, 0x02, 0x04, 0x01, 0x12, 0x03, 0x47, 0x02, 0x27, 0x0a, 0x0c, 0x0a, 0x05, + 0x05, 0x00, 0x02, 0x04, 0x02, 0x12, 0x03, 0x47, 0x2a, 0x2b, 0x0a, 0x4a, 0x0a, 0x02, 0x04, 0x07, + 0x12, 0x04, 0x4b, 0x00, 0x4e, 0x01, 0x1a, 0x3e, 0x20, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, + 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x6d, 0x69, 0x73, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, + 0x72, 0x2c, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x62, + 0x79, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x6f, 0x72, 0x20, 0x62, 0x79, 0x20, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x07, 0x01, 0x12, 0x03, 0x4b, + 0x08, 0x19, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x00, 0x12, 0x03, 0x4c, 0x02, 0x17, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x06, 0x12, 0x03, 0x4c, 0x02, 0x0d, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x07, 0x02, 0x00, 0x01, 0x12, 0x03, 0x4c, 0x0e, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x07, 0x02, 0x00, 0x03, 0x12, 0x03, 0x4c, 0x15, 0x16, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, + 0x01, 0x12, 0x03, 0x4d, 0x02, 0x2c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x04, 0x12, + 0x03, 0x4d, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x06, 0x12, 0x03, 0x4d, + 0x0b, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x01, 0x12, 0x03, 0x4d, 0x1e, 0x27, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x01, 0x03, 0x12, 0x03, 0x4d, 0x2a, 0x2b, 0x0a, 0x4a, + 0x0a, 0x02, 0x04, 0x08, 0x12, 0x04, 0x51, 0x00, 0x59, 0x01, 0x1a, 0x3e, 0x20, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, + 0x2c, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x20, + 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x08, + 0x01, 0x12, 0x03, 0x51, 0x08, 0x16, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x08, 0x08, 0x00, 0x12, 0x04, + 0x52, 0x02, 0x57, 0x03, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x08, 0x00, 0x01, 0x12, 0x03, 0x52, + 0x08, 0x0e, 0x0a, 0x1d, 0x0a, 0x04, 0x04, 0x08, 0x02, 0x00, 0x12, 0x03, 0x54, 0x04, 0x14, 0x1a, + 0x10, 0x20, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, + 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x05, 0x12, 0x03, 0x54, 0x04, 0x09, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x00, 0x01, 0x12, 0x03, 0x54, 0x0a, 0x0f, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x08, 0x02, 0x00, 0x03, 0x12, 0x03, 0x54, 0x12, 0x13, 0x0a, 0x1b, 0x0a, 0x04, 0x04, + 0x08, 0x02, 0x01, 0x12, 0x03, 0x56, 0x04, 0x22, 0x1a, 0x0e, 0x20, 0x4e, 0x6f, 0x64, 0x65, 0x20, + 0x71, 0x75, 0x65, 0x72, 0x69, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x01, + 0x05, 0x12, 0x03, 0x56, 0x04, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x01, 0x01, 0x12, + 0x03, 0x56, 0x0b, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x01, 0x03, 0x12, 0x03, 0x56, + 0x20, 0x21, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x08, 0x02, 0x02, 0x12, 0x03, 0x58, 0x02, 0x1c, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x08, 0x02, 0x02, 0x06, 0x12, 0x03, 0x58, 0x02, 0x0d, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x08, 0x02, 0x02, 0x01, 0x12, 0x03, 0x58, 0x0e, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x08, 0x02, 0x02, 0x03, 0x12, 0x03, 0x58, 0x1a, 0x1b, 0x0a, 0x2a, 0x0a, 0x02, 0x04, 0x09, 0x12, + 0x04, 0x5c, 0x00, 0x62, 0x01, 0x1a, 0x1e, 0x20, 0x42, 0x61, 0x74, 0x63, 0x68, 0x20, 0x73, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x6e, 0x76, 0x65, 0x6c, + 0x6f, 0x70, 0x65, 0x73, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x09, 0x01, 0x12, 0x03, 0x5c, 0x08, + 0x26, 0x0a, 0x39, 0x0a, 0x04, 0x04, 0x09, 0x03, 0x00, 0x12, 0x04, 0x5e, 0x02, 0x60, 0x03, 0x1a, + 0x2b, 0x20, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x09, 0x03, 0x00, 0x01, 0x12, 0x03, 0x5e, 0x0a, 0x23, 0x0a, 0x0d, 0x0a, 0x06, 0x04, 0x09, + 0x03, 0x00, 0x02, 0x00, 0x12, 0x03, 0x5f, 0x04, 0x1d, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, + 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x5f, 0x04, 0x12, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, + 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x5f, 0x13, 0x18, 0x0a, 0x0e, 0x0a, 0x07, 0x04, 0x09, 0x03, + 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x5f, 0x1b, 0x1c, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x09, 0x02, + 0x00, 0x12, 0x03, 0x61, 0x02, 0x32, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x04, 0x12, + 0x03, 0x61, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x06, 0x12, 0x03, 0x61, + 0x0b, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x01, 0x12, 0x03, 0x61, 0x25, 0x2d, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x03, 0x12, 0x03, 0x61, 0x30, 0x31, 0x0a, 0x57, + 0x0a, 0x02, 0x04, 0x0a, 0x12, 0x04, 0x65, 0x00, 0x67, 0x01, 0x1a, 0x4b, 0x20, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x65, 0x64, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x62, 0x61, 0x74, 0x63, 0x68, 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x62, 0x65, 0x20, 0x2d, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x6d, 0x75, 0x6c, 0x74, + 0x69, 0x70, 0x6c, 0x65, 0x20, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x20, 0x61, + 0x74, 0x20, 0x6f, 0x6e, 0x63, 0x65, 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x0a, 0x01, 0x12, 0x03, + 0x65, 0x08, 0x27, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0a, 0x02, 0x00, 0x12, 0x03, 0x66, 0x02, 0x2c, + 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x04, 0x12, 0x03, 0x66, 0x02, 0x0a, 0x0a, 0x0c, + 0x0a, 0x05, 0x04, 0x0a, 0x02, 0x00, 0x06, 0x12, 0x03, 0x66, 0x0b, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, + 0x04, 0x0a, 0x02, 0x00, 0x01, 0x12, 0x03, 0x66, 0x1e, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0a, + 0x02, 0x00, 0x03, 0x12, 0x03, 0x66, 0x2a, 0x2b, 0x0a, 0x25, 0x0a, 0x02, 0x04, 0x0b, 0x12, 0x04, + 0x6a, 0x00, 0x6d, 0x01, 0x1a, 0x19, 0x20, 0x51, 0x75, 0x65, 0x72, 0x79, 0x20, 0x65, 0x6e, 0x76, + 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x0a, 0x0a, + 0x0a, 0x0a, 0x03, 0x04, 0x0b, 0x01, 0x12, 0x03, 0x6a, 0x08, 0x1d, 0x0a, 0x0b, 0x0a, 0x04, 0x04, + 0x0b, 0x02, 0x00, 0x12, 0x03, 0x6b, 0x02, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, + 0x06, 0x12, 0x03, 0x6b, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x01, 0x12, + 0x03, 0x6b, 0x11, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x03, 0x12, 0x03, 0x6b, + 0x19, 0x1a, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x01, 0x12, 0x03, 0x6c, 0x02, 0x13, 0x0a, + 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x01, 0x05, 0x12, 0x03, 0x6c, 0x02, 0x08, 0x0a, 0x0c, 0x0a, + 0x05, 0x04, 0x0b, 0x02, 0x01, 0x01, 0x12, 0x03, 0x6c, 0x09, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, + 0x0b, 0x02, 0x01, 0x03, 0x12, 0x03, 0x6c, 0x11, 0x12, 0x0a, 0x26, 0x0a, 0x02, 0x04, 0x0c, 0x12, + 0x04, 0x70, 0x00, 0x72, 0x01, 0x1a, 0x1a, 0x20, 0x51, 0x75, 0x65, 0x72, 0x79, 0x20, 0x65, 0x6e, + 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x0a, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x0c, 0x01, 0x12, 0x03, 0x70, 0x08, 0x1e, 0x0a, 0x0b, 0x0a, + 0x04, 0x04, 0x0c, 0x02, 0x00, 0x12, 0x03, 0x71, 0x02, 0x2c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, + 0x02, 0x00, 0x04, 0x12, 0x03, 0x71, 0x02, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, + 0x06, 0x12, 0x03, 0x71, 0x0b, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x01, 0x12, + 0x03, 0x71, 0x1e, 0x27, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x03, 0x12, 0x03, 0x71, + 0x2a, 0x2b, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x0d, 0x12, 0x04, 0x74, 0x00, 0x76, 0x01, 0x0a, 0x0a, + 0x0a, 0x03, 0x04, 0x0d, 0x01, 0x12, 0x03, 0x74, 0x08, 0x1e, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0d, + 0x02, 0x00, 0x12, 0x03, 0x75, 0x02, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x00, 0x06, + 0x12, 0x03, 0x75, 0x02, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x00, 0x01, 0x12, 0x03, + 0x75, 0x10, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x00, 0x03, 0x12, 0x03, 0x75, 0x21, + 0x22, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x0e, 0x12, 0x04, 0x78, 0x00, 0x7a, 0x01, 0x0a, 0x0a, 0x0a, + 0x03, 0x04, 0x0e, 0x01, 0x12, 0x03, 0x78, 0x08, 0x1f, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0e, 0x02, + 0x00, 0x12, 0x03, 0x79, 0x02, 0x2d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x06, 0x12, + 0x03, 0x79, 0x02, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x01, 0x12, 0x03, 0x79, + 0x15, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x03, 0x12, 0x03, 0x79, 0x2b, 0x2c, + 0x0a, 0x1e, 0x0a, 0x02, 0x06, 0x00, 0x12, 0x05, 0x7d, 0x00, 0x95, 0x01, 0x01, 0x1a, 0x11, 0x20, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x50, 0x49, 0x0a, + 0x0a, 0x0a, 0x0a, 0x03, 0x06, 0x00, 0x01, 0x12, 0x03, 0x7d, 0x08, 0x16, 0x0a, 0x27, 0x0a, 0x04, + 0x06, 0x00, 0x02, 0x00, 0x12, 0x05, 0x7f, 0x02, 0x84, 0x01, 0x03, 0x1a, 0x18, 0x20, 0x53, 0x75, + 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x6e, 0x76, 0x65, 0x6c, + 0x6f, 0x70, 0x65, 0x73, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, + 0x7f, 0x06, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x7f, 0x1e, + 0x3c, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x06, 0x12, 0x03, 0x7f, 0x47, 0x4d, 0x0a, + 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x7f, 0x4e, 0x6d, 0x0a, 0x0f, 0x0a, + 0x05, 0x06, 0x00, 0x02, 0x00, 0x04, 0x12, 0x06, 0x80, 0x01, 0x04, 0x83, 0x01, 0x06, 0x0a, 0x13, + 0x0a, 0x09, 0x06, 0x00, 0x02, 0x00, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x06, 0x80, 0x01, 0x04, + 0x83, 0x01, 0x06, 0x0a, 0x12, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x00, 0x04, 0xb0, 0xca, 0xbc, 0x22, + 0x04, 0x12, 0x04, 0x81, 0x01, 0x06, 0x29, 0x0a, 0x12, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x00, 0x04, + 0xb0, 0xca, 0xbc, 0x22, 0x07, 0x12, 0x04, 0x82, 0x01, 0x06, 0x0f, 0x0a, 0x21, 0x0a, 0x04, 0x06, + 0x00, 0x02, 0x01, 0x12, 0x06, 0x87, 0x01, 0x02, 0x8c, 0x01, 0x03, 0x1a, 0x11, 0x20, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x20, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x73, 0x0a, 0x0a, 0x0d, + 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x01, 0x12, 0x04, 0x87, 0x01, 0x06, 0x14, 0x0a, 0x0d, 0x0a, + 0x05, 0x06, 0x00, 0x02, 0x01, 0x02, 0x12, 0x04, 0x87, 0x01, 0x15, 0x2a, 0x0a, 0x0d, 0x0a, 0x05, + 0x06, 0x00, 0x02, 0x01, 0x03, 0x12, 0x04, 0x87, 0x01, 0x35, 0x4b, 0x0a, 0x0f, 0x0a, 0x05, 0x06, + 0x00, 0x02, 0x01, 0x04, 0x12, 0x06, 0x88, 0x01, 0x04, 0x8b, 0x01, 0x06, 0x0a, 0x13, 0x0a, 0x09, + 0x06, 0x00, 0x02, 0x01, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x06, 0x88, 0x01, 0x04, 0x8b, 0x01, + 0x06, 0x0a, 0x12, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x01, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x04, 0x12, + 0x04, 0x89, 0x01, 0x06, 0x25, 0x0a, 0x12, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x01, 0x04, 0xb0, 0xca, + 0xbc, 0x22, 0x07, 0x12, 0x04, 0x8a, 0x01, 0x06, 0x0f, 0x0a, 0x22, 0x0a, 0x04, 0x06, 0x00, 0x02, + 0x02, 0x12, 0x06, 0x8f, 0x01, 0x02, 0x94, 0x01, 0x03, 0x1a, 0x12, 0x20, 0x50, 0x75, 0x62, 0x6c, + 0x69, 0x73, 0x68, 0x20, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x0a, 0x0a, 0x0d, 0x0a, + 0x05, 0x06, 0x00, 0x02, 0x02, 0x01, 0x12, 0x04, 0x8f, 0x01, 0x06, 0x15, 0x0a, 0x0d, 0x0a, 0x05, + 0x06, 0x00, 0x02, 0x02, 0x02, 0x12, 0x04, 0x8f, 0x01, 0x16, 0x2c, 0x0a, 0x0d, 0x0a, 0x05, 0x06, + 0x00, 0x02, 0x02, 0x03, 0x12, 0x04, 0x8f, 0x01, 0x37, 0x4e, 0x0a, 0x0f, 0x0a, 0x05, 0x06, 0x00, + 0x02, 0x02, 0x04, 0x12, 0x06, 0x90, 0x01, 0x04, 0x93, 0x01, 0x06, 0x0a, 0x13, 0x0a, 0x09, 0x06, + 0x00, 0x02, 0x02, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x12, 0x06, 0x90, 0x01, 0x04, 0x93, 0x01, 0x06, + 0x0a, 0x12, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x02, 0x04, 0xb0, 0xca, 0xbc, 0x22, 0x04, 0x12, 0x04, + 0x91, 0x01, 0x06, 0x26, 0x0a, 0x12, 0x0a, 0x0a, 0x06, 0x00, 0x02, 0x02, 0x04, 0xb0, 0xca, 0xbc, + 0x22, 0x07, 0x12, 0x04, 0x92, 0x01, 0x06, 0x0f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +]; +include!("xmtp.xmtpv4.serde.rs"); +include!("xmtp.xmtpv4.tonic.rs"); +// @@protoc_insertion_point(module) \ No newline at end of file diff --git a/xmtp_proto/src/gen/xmtp.xmtpv4.serde.rs b/xmtp_proto/src/gen/xmtp.xmtpv4.serde.rs new file mode 100644 index 000000000..19f6d37bd --- /dev/null +++ b/xmtp_proto/src/gen/xmtp.xmtpv4.serde.rs @@ -0,0 +1,1883 @@ +// @generated +impl serde::Serialize for AuthenticatedData { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.target_originator != 0 { + len += 1; + } + if !self.target_topic.is_empty() { + len += 1; + } + if self.last_seen.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.AuthenticatedData", len)?; + if self.target_originator != 0 { + struct_ser.serialize_field("targetOriginator", &self.target_originator)?; + } + if !self.target_topic.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("targetTopic", pbjson::private::base64::encode(&self.target_topic).as_str())?; + } + if let Some(v) = self.last_seen.as_ref() { + struct_ser.serialize_field("lastSeen", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for AuthenticatedData { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "target_originator", + "targetOriginator", + "target_topic", + "targetTopic", + "last_seen", + "lastSeen", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + TargetOriginator, + TargetTopic, + LastSeen, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "targetOriginator" | "target_originator" => Ok(GeneratedField::TargetOriginator), + "targetTopic" | "target_topic" => Ok(GeneratedField::TargetTopic), + "lastSeen" | "last_seen" => Ok(GeneratedField::LastSeen), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = AuthenticatedData; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.AuthenticatedData") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut target_originator__ = None; + let mut target_topic__ = None; + let mut last_seen__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::TargetOriginator => { + if target_originator__.is_some() { + return Err(serde::de::Error::duplicate_field("targetOriginator")); + } + target_originator__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::TargetTopic => { + if target_topic__.is_some() { + return Err(serde::de::Error::duplicate_field("targetTopic")); + } + target_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()?; + } + } + } + Ok(AuthenticatedData { + target_originator: target_originator__.unwrap_or_default(), + target_topic: target_topic__.unwrap_or_default(), + last_seen: last_seen__, + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.AuthenticatedData", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for BatchSubscribeEnvelopesRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.requests.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.BatchSubscribeEnvelopesRequest", len)?; + if !self.requests.is_empty() { + struct_ser.serialize_field("requests", &self.requests)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for BatchSubscribeEnvelopesRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "requests", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Requests, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "requests" => Ok(GeneratedField::Requests), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = BatchSubscribeEnvelopesRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.BatchSubscribeEnvelopesRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut requests__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Requests => { + if requests__.is_some() { + return Err(serde::de::Error::duplicate_field("requests")); + } + requests__ = Some(map_.next_value()?); + } + } + } + Ok(BatchSubscribeEnvelopesRequest { + requests: requests__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.BatchSubscribeEnvelopesRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for batch_subscribe_envelopes_request::SubscribeEnvelopesRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.query.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.BatchSubscribeEnvelopesRequest.SubscribeEnvelopesRequest", len)?; + if let Some(v) = self.query.as_ref() { + struct_ser.serialize_field("query", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for batch_subscribe_envelopes_request::SubscribeEnvelopesRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "query", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Query, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "query" => Ok(GeneratedField::Query), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = batch_subscribe_envelopes_request::SubscribeEnvelopesRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.BatchSubscribeEnvelopesRequest.SubscribeEnvelopesRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut query__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Query => { + if query__.is_some() { + return Err(serde::de::Error::duplicate_field("query")); + } + query__ = map_.next_value()?; + } + } + } + Ok(batch_subscribe_envelopes_request::SubscribeEnvelopesRequest { + query: query__, + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.BatchSubscribeEnvelopesRequest.SubscribeEnvelopesRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for BatchSubscribeEnvelopesResponse { + #[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.BatchSubscribeEnvelopesResponse", len)?; + if !self.envelopes.is_empty() { + struct_ser.serialize_field("envelopes", &self.envelopes)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for BatchSubscribeEnvelopesResponse { + #[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, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = BatchSubscribeEnvelopesResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.BatchSubscribeEnvelopesResponse") + } + + 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()?); + } + } + } + Ok(BatchSubscribeEnvelopesResponse { + envelopes: envelopes__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.BatchSubscribeEnvelopesResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for BlockchainProof { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.block_number != 0 { + len += 1; + } + if self.publisher_node_id != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.BlockchainProof", len)?; + if self.block_number != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("blockNumber", ToString::to_string(&self.block_number).as_str())?; + } + if self.publisher_node_id != 0 { + struct_ser.serialize_field("publisherNodeId", &self.publisher_node_id)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for BlockchainProof { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "block_number", + "blockNumber", + "publisher_node_id", + "publisherNodeId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + BlockNumber, + PublisherNodeId, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "blockNumber" | "block_number" => Ok(GeneratedField::BlockNumber), + "publisherNodeId" | "publisher_node_id" => Ok(GeneratedField::PublisherNodeId), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = BlockchainProof; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.BlockchainProof") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut block_number__ = None; + let mut publisher_node_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::BlockNumber => { + if block_number__.is_some() { + return Err(serde::de::Error::duplicate_field("blockNumber")); + } + block_number__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::PublisherNodeId => { + if publisher_node_id__.is_some() { + return Err(serde::de::Error::duplicate_field("publisherNodeId")); + } + publisher_node_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(BlockchainProof { + block_number: block_number__.unwrap_or_default(), + publisher_node_id: publisher_node_id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.BlockchainProof", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for ClientEnvelope { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.aad.is_some() { + len += 1; + } + if self.payload.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.ClientEnvelope", len)?; + if let Some(v) = self.aad.as_ref() { + struct_ser.serialize_field("aad", v)?; + } + if let Some(v) = self.payload.as_ref() { + match v { + client_envelope::Payload::GroupMessage(v) => { + struct_ser.serialize_field("groupMessage", v)?; + } + client_envelope::Payload::WelcomeMessage(v) => { + struct_ser.serialize_field("welcomeMessage", v)?; + } + client_envelope::Payload::IdentityUpdate(v) => { + struct_ser.serialize_field("identityUpdate", v)?; + } + client_envelope::Payload::UploadKeyPackage(v) => { + struct_ser.serialize_field("uploadKeyPackage", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for ClientEnvelope { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "aad", + "group_message", + "groupMessage", + "welcome_message", + "welcomeMessage", + "identity_update", + "identityUpdate", + "upload_key_package", + "uploadKeyPackage", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Aad, + GroupMessage, + WelcomeMessage, + IdentityUpdate, + UploadKeyPackage, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "aad" => Ok(GeneratedField::Aad), + "groupMessage" | "group_message" => Ok(GeneratedField::GroupMessage), + "welcomeMessage" | "welcome_message" => Ok(GeneratedField::WelcomeMessage), + "identityUpdate" | "identity_update" => Ok(GeneratedField::IdentityUpdate), + "uploadKeyPackage" | "upload_key_package" => Ok(GeneratedField::UploadKeyPackage), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ClientEnvelope; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.ClientEnvelope") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut aad__ = None; + let mut payload__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Aad => { + if aad__.is_some() { + return Err(serde::de::Error::duplicate_field("aad")); + } + aad__ = map_.next_value()?; + } + GeneratedField::GroupMessage => { + if payload__.is_some() { + return Err(serde::de::Error::duplicate_field("groupMessage")); + } + payload__ = map_.next_value::<::std::option::Option<_>>()?.map(client_envelope::Payload::GroupMessage) +; + } + GeneratedField::WelcomeMessage => { + if payload__.is_some() { + return Err(serde::de::Error::duplicate_field("welcomeMessage")); + } + payload__ = map_.next_value::<::std::option::Option<_>>()?.map(client_envelope::Payload::WelcomeMessage) +; + } + GeneratedField::IdentityUpdate => { + if payload__.is_some() { + return Err(serde::de::Error::duplicate_field("identityUpdate")); + } + payload__ = map_.next_value::<::std::option::Option<_>>()?.map(client_envelope::Payload::IdentityUpdate) +; + } + GeneratedField::UploadKeyPackage => { + if payload__.is_some() { + return Err(serde::de::Error::duplicate_field("uploadKeyPackage")); + } + payload__ = map_.next_value::<::std::option::Option<_>>()?.map(client_envelope::Payload::UploadKeyPackage) +; + } + } + } + Ok(ClientEnvelope { + aad: aad__, + payload: payload__, + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.ClientEnvelope", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for EnvelopesQuery { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.last_seen.is_some() { + len += 1; + } + if self.filter.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.EnvelopesQuery", len)?; + if let Some(v) = self.last_seen.as_ref() { + struct_ser.serialize_field("lastSeen", v)?; + } + if let Some(v) = self.filter.as_ref() { + match v { + envelopes_query::Filter::Topic(v) => { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("topic", pbjson::private::base64::encode(&v).as_str())?; + } + envelopes_query::Filter::OriginatorNodeId(v) => { + struct_ser.serialize_field("originatorNodeId", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for EnvelopesQuery { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "last_seen", + "lastSeen", + "topic", + "originator_node_id", + "originatorNodeId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + LastSeen, + Topic, + OriginatorNodeId, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "lastSeen" | "last_seen" => Ok(GeneratedField::LastSeen), + "topic" => Ok(GeneratedField::Topic), + "originatorNodeId" | "originator_node_id" => Ok(GeneratedField::OriginatorNodeId), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = EnvelopesQuery; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.EnvelopesQuery") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut last_seen__ = None; + let mut filter__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::LastSeen => { + if last_seen__.is_some() { + return Err(serde::de::Error::duplicate_field("lastSeen")); + } + last_seen__ = map_.next_value()?; + } + GeneratedField::Topic => { + if filter__.is_some() { + return Err(serde::de::Error::duplicate_field("topic")); + } + filter__ = map_.next_value::<::std::option::Option<::pbjson::private::BytesDeserialize<_>>>()?.map(|x| envelopes_query::Filter::Topic(x.0)); + } + GeneratedField::OriginatorNodeId => { + if filter__.is_some() { + return Err(serde::de::Error::duplicate_field("originatorNodeId")); + } + filter__ = map_.next_value::<::std::option::Option<::pbjson::private::NumberDeserialize<_>>>()?.map(|x| envelopes_query::Filter::OriginatorNodeId(x.0)); + } + } + } + Ok(EnvelopesQuery { + last_seen: last_seen__, + filter: filter__, + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.EnvelopesQuery", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for Misbehavior { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + let variant = match self { + Self::Unspecified => "MISBEHAVIOR_UNSPECIFIED", + Self::UnavailableNode => "MISBEHAVIOR_UNAVAILABLE_NODE", + Self::OutOfOrderOriginatorSid => "MISBEHAVIOR_OUT_OF_ORDER_ORIGINATOR_SID", + Self::DuplicateOriginatorSid => "MISBEHAVIOR_DUPLICATE_ORIGINATOR_SID", + Self::CyclicalMessageOrdering => "MISBEHAVIOR_CYCLICAL_MESSAGE_ORDERING", + }; + serializer.serialize_str(variant) + } +} +impl<'de> serde::Deserialize<'de> for Misbehavior { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "MISBEHAVIOR_UNSPECIFIED", + "MISBEHAVIOR_UNAVAILABLE_NODE", + "MISBEHAVIOR_OUT_OF_ORDER_ORIGINATOR_SID", + "MISBEHAVIOR_DUPLICATE_ORIGINATOR_SID", + "MISBEHAVIOR_CYCLICAL_MESSAGE_ORDERING", + ]; + + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = Misbehavior; + + 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 { + "MISBEHAVIOR_UNSPECIFIED" => Ok(Misbehavior::Unspecified), + "MISBEHAVIOR_UNAVAILABLE_NODE" => Ok(Misbehavior::UnavailableNode), + "MISBEHAVIOR_OUT_OF_ORDER_ORIGINATOR_SID" => Ok(Misbehavior::OutOfOrderOriginatorSid), + "MISBEHAVIOR_DUPLICATE_ORIGINATOR_SID" => Ok(Misbehavior::DuplicateOriginatorSid), + "MISBEHAVIOR_CYCLICAL_MESSAGE_ORDERING" => Ok(Misbehavior::CyclicalMessageOrdering), + _ => Err(serde::de::Error::unknown_variant(value, FIELDS)), + } + } + } + deserializer.deserialize_any(GeneratedVisitor) + } +} +impl serde::Serialize for MisbehaviorReport { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.r#type != 0 { + len += 1; + } + if !self.envelopes.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.MisbehaviorReport", len)?; + if self.r#type != 0 { + let v = Misbehavior::try_from(self.r#type) + .map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", self.r#type)))?; + struct_ser.serialize_field("type", &v)?; + } + if !self.envelopes.is_empty() { + struct_ser.serialize_field("envelopes", &self.envelopes)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for MisbehaviorReport { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "type", + "envelopes", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Type, + Envelopes, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "type" => Ok(GeneratedField::Type), + "envelopes" => Ok(GeneratedField::Envelopes), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = MisbehaviorReport; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.MisbehaviorReport") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut r#type__ = None; + let mut envelopes__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Type => { + if r#type__.is_some() { + return Err(serde::de::Error::duplicate_field("type")); + } + r#type__ = Some(map_.next_value::()? as i32); + } + GeneratedField::Envelopes => { + if envelopes__.is_some() { + return Err(serde::de::Error::duplicate_field("envelopes")); + } + envelopes__ = Some(map_.next_value()?); + } + } + } + Ok(MisbehaviorReport { + r#type: r#type__.unwrap_or_default(), + envelopes: envelopes__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.MisbehaviorReport", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for OriginatorEnvelope { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.unsigned_originator_envelope.is_empty() { + len += 1; + } + if self.proof.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.OriginatorEnvelope", len)?; + if !self.unsigned_originator_envelope.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("unsignedOriginatorEnvelope", pbjson::private::base64::encode(&self.unsigned_originator_envelope).as_str())?; + } + if let Some(v) = self.proof.as_ref() { + match v { + originator_envelope::Proof::OriginatorSignature(v) => { + struct_ser.serialize_field("originatorSignature", v)?; + } + originator_envelope::Proof::BlockchainProof(v) => { + struct_ser.serialize_field("blockchainProof", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for OriginatorEnvelope { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "unsigned_originator_envelope", + "unsignedOriginatorEnvelope", + "originator_signature", + "originatorSignature", + "blockchain_proof", + "blockchainProof", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + UnsignedOriginatorEnvelope, + OriginatorSignature, + BlockchainProof, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "unsignedOriginatorEnvelope" | "unsigned_originator_envelope" => Ok(GeneratedField::UnsignedOriginatorEnvelope), + "originatorSignature" | "originator_signature" => Ok(GeneratedField::OriginatorSignature), + "blockchainProof" | "blockchain_proof" => Ok(GeneratedField::BlockchainProof), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = OriginatorEnvelope; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.OriginatorEnvelope") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut unsigned_originator_envelope__ = None; + let mut proof__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::UnsignedOriginatorEnvelope => { + if unsigned_originator_envelope__.is_some() { + return Err(serde::de::Error::duplicate_field("unsignedOriginatorEnvelope")); + } + unsigned_originator_envelope__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::OriginatorSignature => { + if proof__.is_some() { + return Err(serde::de::Error::duplicate_field("originatorSignature")); + } + proof__ = map_.next_value::<::std::option::Option<_>>()?.map(originator_envelope::Proof::OriginatorSignature) +; + } + GeneratedField::BlockchainProof => { + if proof__.is_some() { + return Err(serde::de::Error::duplicate_field("blockchainProof")); + } + proof__ = map_.next_value::<::std::option::Option<_>>()?.map(originator_envelope::Proof::BlockchainProof) +; + } + } + } + Ok(OriginatorEnvelope { + unsigned_originator_envelope: unsigned_originator_envelope__.unwrap_or_default(), + proof: proof__, + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.OriginatorEnvelope", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for PayerEnvelope { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.unsigned_client_envelope.is_empty() { + len += 1; + } + if self.payer_signature.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.PayerEnvelope", len)?; + if !self.unsigned_client_envelope.is_empty() { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("unsignedClientEnvelope", pbjson::private::base64::encode(&self.unsigned_client_envelope).as_str())?; + } + if let Some(v) = self.payer_signature.as_ref() { + struct_ser.serialize_field("payerSignature", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for PayerEnvelope { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "unsigned_client_envelope", + "unsignedClientEnvelope", + "payer_signature", + "payerSignature", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + UnsignedClientEnvelope, + PayerSignature, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "unsignedClientEnvelope" | "unsigned_client_envelope" => Ok(GeneratedField::UnsignedClientEnvelope), + "payerSignature" | "payer_signature" => Ok(GeneratedField::PayerSignature), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = PayerEnvelope; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.PayerEnvelope") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut unsigned_client_envelope__ = None; + let mut payer_signature__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::UnsignedClientEnvelope => { + if unsigned_client_envelope__.is_some() { + return Err(serde::de::Error::duplicate_field("unsignedClientEnvelope")); + } + unsigned_client_envelope__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::PayerSignature => { + if payer_signature__.is_some() { + return Err(serde::de::Error::duplicate_field("payerSignature")); + } + payer_signature__ = map_.next_value()?; + } + } + } + Ok(PayerEnvelope { + unsigned_client_envelope: unsigned_client_envelope__.unwrap_or_default(), + payer_signature: payer_signature__, + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.PayerEnvelope", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for PublishEnvelopeRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.payer_envelope.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.PublishEnvelopeRequest", len)?; + if let Some(v) = self.payer_envelope.as_ref() { + struct_ser.serialize_field("payerEnvelope", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for PublishEnvelopeRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "payer_envelope", + "payerEnvelope", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + PayerEnvelope, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "payerEnvelope" | "payer_envelope" => Ok(GeneratedField::PayerEnvelope), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = PublishEnvelopeRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.PublishEnvelopeRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut payer_envelope__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::PayerEnvelope => { + if payer_envelope__.is_some() { + return Err(serde::de::Error::duplicate_field("payerEnvelope")); + } + payer_envelope__ = map_.next_value()?; + } + } + } + Ok(PublishEnvelopeRequest { + payer_envelope: payer_envelope__, + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.PublishEnvelopeRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for PublishEnvelopeResponse { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.originator_envelope.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.PublishEnvelopeResponse", len)?; + if let Some(v) = self.originator_envelope.as_ref() { + struct_ser.serialize_field("originatorEnvelope", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for PublishEnvelopeResponse { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "originator_envelope", + "originatorEnvelope", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + OriginatorEnvelope, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "originatorEnvelope" | "originator_envelope" => Ok(GeneratedField::OriginatorEnvelope), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = PublishEnvelopeResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.PublishEnvelopeResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut originator_envelope__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::OriginatorEnvelope => { + if originator_envelope__.is_some() { + return Err(serde::de::Error::duplicate_field("originatorEnvelope")); + } + originator_envelope__ = map_.next_value()?; + } + } + } + Ok(PublishEnvelopeResponse { + originator_envelope: originator_envelope__, + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.PublishEnvelopeResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for QueryEnvelopesRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.query.is_some() { + len += 1; + } + if self.limit != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.QueryEnvelopesRequest", len)?; + if let Some(v) = self.query.as_ref() { + struct_ser.serialize_field("query", v)?; + } + if self.limit != 0 { + struct_ser.serialize_field("limit", &self.limit)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for QueryEnvelopesRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "query", + "limit", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Query, + Limit, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "query" => Ok(GeneratedField::Query), + "limit" => Ok(GeneratedField::Limit), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = QueryEnvelopesRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.QueryEnvelopesRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut query__ = None; + let mut limit__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Query => { + if query__.is_some() { + return Err(serde::de::Error::duplicate_field("query")); + } + query__ = map_.next_value()?; + } + GeneratedField::Limit => { + if limit__.is_some() { + return Err(serde::de::Error::duplicate_field("limit")); + } + limit__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(QueryEnvelopesRequest { + query: query__, + limit: limit__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.QueryEnvelopesRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for QueryEnvelopesResponse { + #[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.QueryEnvelopesResponse", len)?; + if !self.envelopes.is_empty() { + struct_ser.serialize_field("envelopes", &self.envelopes)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for QueryEnvelopesResponse { + #[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, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = QueryEnvelopesResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.QueryEnvelopesResponse") + } + + 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()?); + } + } + } + Ok(QueryEnvelopesResponse { + envelopes: envelopes__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.QueryEnvelopesResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for UnsignedOriginatorEnvelope { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.originator_node_id != 0 { + len += 1; + } + if self.originator_sequence_id != 0 { + len += 1; + } + if self.originator_ns != 0 { + len += 1; + } + if self.payer_envelope.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.UnsignedOriginatorEnvelope", len)?; + if self.originator_node_id != 0 { + struct_ser.serialize_field("originatorNodeId", &self.originator_node_id)?; + } + if self.originator_sequence_id != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("originatorSequenceId", ToString::to_string(&self.originator_sequence_id).as_str())?; + } + if self.originator_ns != 0 { + #[allow(clippy::needless_borrow)] + #[allow(clippy::needless_borrows_for_generic_args)] + struct_ser.serialize_field("originatorNs", ToString::to_string(&self.originator_ns).as_str())?; + } + if let Some(v) = self.payer_envelope.as_ref() { + struct_ser.serialize_field("payerEnvelope", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for UnsignedOriginatorEnvelope { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "originator_node_id", + "originatorNodeId", + "originator_sequence_id", + "originatorSequenceId", + "originator_ns", + "originatorNs", + "payer_envelope", + "payerEnvelope", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + OriginatorNodeId, + OriginatorSequenceId, + OriginatorNs, + PayerEnvelope, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "originatorNodeId" | "originator_node_id" => Ok(GeneratedField::OriginatorNodeId), + "originatorSequenceId" | "originator_sequence_id" => Ok(GeneratedField::OriginatorSequenceId), + "originatorNs" | "originator_ns" => Ok(GeneratedField::OriginatorNs), + "payerEnvelope" | "payer_envelope" => Ok(GeneratedField::PayerEnvelope), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = UnsignedOriginatorEnvelope; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.UnsignedOriginatorEnvelope") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut originator_node_id__ = None; + let mut originator_sequence_id__ = None; + let mut originator_ns__ = None; + let mut payer_envelope__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::OriginatorNodeId => { + if originator_node_id__.is_some() { + return Err(serde::de::Error::duplicate_field("originatorNodeId")); + } + originator_node_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::OriginatorSequenceId => { + if originator_sequence_id__.is_some() { + return Err(serde::de::Error::duplicate_field("originatorSequenceId")); + } + originator_sequence_id__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::OriginatorNs => { + if originator_ns__.is_some() { + return Err(serde::de::Error::duplicate_field("originatorNs")); + } + originator_ns__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::PayerEnvelope => { + if payer_envelope__.is_some() { + return Err(serde::de::Error::duplicate_field("payerEnvelope")); + } + payer_envelope__ = map_.next_value()?; + } + } + } + Ok(UnsignedOriginatorEnvelope { + originator_node_id: originator_node_id__.unwrap_or_default(), + originator_sequence_id: originator_sequence_id__.unwrap_or_default(), + originator_ns: originator_ns__.unwrap_or_default(), + payer_envelope: payer_envelope__, + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.UnsignedOriginatorEnvelope", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for VectorClock { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.node_id_to_sequence_id.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("xmtp.xmtpv4.VectorClock", len)?; + if !self.node_id_to_sequence_id.is_empty() { + let v: std::collections::HashMap<_, _> = self.node_id_to_sequence_id.iter() + .map(|(k, v)| (k, v.to_string())).collect(); + struct_ser.serialize_field("nodeIdToSequenceId", &v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for VectorClock { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "node_id_to_sequence_id", + "nodeIdToSequenceId", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + NodeIdToSequenceId, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> 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 { + "nodeIdToSequenceId" | "node_id_to_sequence_id" => Ok(GeneratedField::NodeIdToSequenceId), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = VectorClock; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct xmtp.xmtpv4.VectorClock") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut node_id_to_sequence_id__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::NodeIdToSequenceId => { + if node_id_to_sequence_id__.is_some() { + return Err(serde::de::Error::duplicate_field("nodeIdToSequenceId")); + } + node_id_to_sequence_id__ = Some( + map_.next_value::, ::pbjson::private::NumberDeserialize>>()? + .into_iter().map(|(k,v)| (k.0, v.0)).collect() + ); + } + } + } + Ok(VectorClock { + node_id_to_sequence_id: node_id_to_sequence_id__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("xmtp.xmtpv4.VectorClock", FIELDS, GeneratedVisitor) + } +} diff --git a/xmtp_proto/src/gen/xmtp.xmtpv4.tonic.rs b/xmtp_proto/src/gen/xmtp.xmtpv4.tonic.rs new file mode 100644 index 000000000..3f775e2e1 --- /dev/null +++ b/xmtp_proto/src/gen/xmtp.xmtpv4.tonic.rs @@ -0,0 +1,483 @@ +// @generated +/// Generated client implementations. +#[cfg(not(target_arch = "wasm32"))] +pub mod replication_api_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /** Replication API +*/ + #[derive(Debug, Clone)] + pub struct ReplicationApiClient { + inner: tonic::client::Grpc, + } + impl ReplicationApiClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl ReplicationApiClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> ReplicationApiClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + Send + Sync, + { + ReplicationApiClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /** Subscribe to envelopes +*/ + pub async fn batch_subscribe_envelopes( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response< + tonic::codec::Streaming, + >, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/xmtp.xmtpv4.ReplicationApi/BatchSubscribeEnvelopes", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "xmtp.xmtpv4.ReplicationApi", + "BatchSubscribeEnvelopes", + ), + ); + self.inner.server_streaming(req, path, codec).await + } + /** Query envelopes +*/ + pub async fn query_envelopes( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/xmtp.xmtpv4.ReplicationApi/QueryEnvelopes", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("xmtp.xmtpv4.ReplicationApi", "QueryEnvelopes")); + self.inner.unary(req, path, codec).await + } + /** Publish envelope +*/ + pub async fn publish_envelope( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/xmtp.xmtpv4.ReplicationApi/PublishEnvelope", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new("xmtp.xmtpv4.ReplicationApi", "PublishEnvelope"), + ); + self.inner.unary(req, path, codec).await + } + } +} +/// Generated server implementations. +#[cfg(not(target_arch = "wasm32"))] +pub mod replication_api_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with ReplicationApiServer. + #[async_trait] + pub trait ReplicationApi: Send + Sync + 'static { + /// Server streaming response type for the BatchSubscribeEnvelopes method. + type BatchSubscribeEnvelopesStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + super::BatchSubscribeEnvelopesResponse, + tonic::Status, + >, + > + + Send + + 'static; + /** Subscribe to envelopes +*/ + async fn batch_subscribe_envelopes( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /** Query envelopes +*/ + async fn query_envelopes( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /** Publish envelope +*/ + async fn publish_envelope( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + /** Replication API +*/ + #[derive(Debug)] + pub struct ReplicationApiServer { + inner: Arc, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + impl ReplicationApiServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for ReplicationApiServer + where + T: ReplicationApi, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + match req.uri().path() { + "/xmtp.xmtpv4.ReplicationApi/BatchSubscribeEnvelopes" => { + #[allow(non_camel_case_types)] + struct BatchSubscribeEnvelopesSvc(pub Arc); + impl< + T: ReplicationApi, + > tonic::server::ServerStreamingService< + super::BatchSubscribeEnvelopesRequest, + > for BatchSubscribeEnvelopesSvc { + type Response = super::BatchSubscribeEnvelopesResponse; + type ResponseStream = T::BatchSubscribeEnvelopesStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::BatchSubscribeEnvelopesRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::batch_subscribe_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 = BatchSubscribeEnvelopesSvc(inner); + let codec = tonic::codec::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.ReplicationApi/QueryEnvelopes" => { + #[allow(non_camel_case_types)] + struct QueryEnvelopesSvc(pub Arc); + impl< + T: ReplicationApi, + > 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::codec::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.ReplicationApi/PublishEnvelope" => { + #[allow(non_camel_case_types)] + struct PublishEnvelopeSvc(pub Arc); + impl< + T: ReplicationApi, + > tonic::server::UnaryService + for PublishEnvelopeSvc { + type Response = super::PublishEnvelopeResponse; + 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 { + ::publish_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 = PublishEnvelopeSvc(inner); + let codec = tonic::codec::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) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", tonic::Code::Unimplemented as i32) + .header( + http::header::CONTENT_TYPE, + tonic::metadata::GRPC_CONTENT_TYPE, + ) + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for ReplicationApiServer { + 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 tonic::server::NamedService for ReplicationApiServer { + const NAME: &'static str = "xmtp.xmtpv4.ReplicationApi"; + } +} diff --git a/xmtp_user_preferences/Cargo.toml b/xmtp_user_preferences/Cargo.toml index c52321985..7f7ba0f27 100644 --- a/xmtp_user_preferences/Cargo.toml +++ b/xmtp_user_preferences/Cargo.toml @@ -1,12 +1,11 @@ [package] -name = "xmtp_user_preferences" -version = "0.1.0" edition = "2021" +name = "xmtp_user_preferences" +version.workspace = true [dependencies] -base64 = "0.21.4" +base64 = "0.22.1" # Need to include this as a dep or compile will fail because of a version mismatch -once_cell = "1.18.0" prost = { workspace = true, features = ["prost-derive"] } xmtp_proto = { path = "../xmtp_proto", features = ["xmtp-message_contents"] } xmtp_v2 = { path = "../xmtp_v2" } diff --git a/xmtp_v2/Cargo.toml b/xmtp_v2/Cargo.toml index f99ad24c0..f4fd6db74 100644 --- a/xmtp_v2/Cargo.toml +++ b/xmtp_v2/Cargo.toml @@ -1,14 +1,14 @@ [package] -name = "xmtp_v2" -version = "0.1.0" edition = "2021" +name = "xmtp_v2" rust-version = "1.64" +version.workspace = true [dependencies] aes-gcm = "0.10.1" ecdsa = "0.15.1" generic-array = "0.14.6" -getrandom = { version = "0.2.8", features = ["js"] } +getrandom = { workspace = true, features = ["js"] } hex = { workspace = true } hkdf = "0.12.3" k256 = { version = "0.12.0", features = ["ecdh"] }